diff --git a/.gn b/.gn
index ae9bd29..c5c73f8 100644
--- a/.gn
+++ b/.gn
@@ -86,7 +86,6 @@
   "//chrome/test/data/nacl:*",  # 350 errors
   "//chrome/test/media_router:*",  # 5 errors
   "//chrome/test:*",  # 2682 errors
-  "//chrome:*",  # 7 errors
 
   "//clank/third_party/gvr_shim:*",  # 1 error
   "//extensions/browser/api/alarms:*",  # 2 errors
diff --git a/DEPS b/DEPS
index f5655b2b..4ea5f99 100644
--- a/DEPS
+++ b/DEPS
@@ -195,11 +195,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'ad6aeace6eee78c652bc561b56e18a41a209947a',
+  'skia_revision': 'defd223d40232fe5521f61637aa28e1f072e4aa9',
   # 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': '667efcdc9127038d9ee16ce030a7231711b75240',
+  'v8_revision': '3860f7c638517492f126666ff427e9dca0d38068',
   # 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.
@@ -207,7 +207,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'd2f6cc6f606cdaefc69575a495c3be47085b4c75',
+  'angle_revision': '7930477fba50c542f0d23a1da845c2161d2c9a69',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -266,7 +266,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'ba3d5932bb28ec0b343ec278ac006fa6ee75d98a',
+  'devtools_frontend_revision': '5ac564dd655318736c1c3f66b0ea7690ca19c64d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -318,7 +318,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '145f115c5496a0c4ab17d13a20410143b0ce03d4',
+  'dawn_revision': '306fc502cf96ea95e306d0949108cfa3f838d5e8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -881,7 +881,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'ea1181f182dcccced1184ca2537c4027bf76e637',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'def823dfb288c999a4fd5ffb3bad34b4b965b16a',
       'condition': 'checkout_chromeos',
   },
 
@@ -1486,7 +1486,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '55c178693cdce4e4719181a12795506ea5500846',
+    Var('webrtc_git') + '/src.git' + '@' + '08ae7cea30d2e571a95ab3ebfe7f551fffec0e14',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1558,7 +1558,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@79c5fa7eebcbcb414f5d957bc74c0c0ca8b0b6ef',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@8b16441c661bbd1acc9b4484b575bb985c5912ee',
     'condition': 'checkout_src_internal',
   },
 
@@ -1577,7 +1577,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': '0WU_n4W64ltaAxz5MUUNLa1sbT9wx_IZiR2vRW6vn2sC',
+        'version': 'W-Axu-xOAC73R6IETvUzr5ssqwyUgIQVQPtCBDfKISUC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/ambient/ambient_access_token_controller.cc b/ash/ambient/ambient_access_token_controller.cc
index 93f6c9f7..2e63fd8b 100644
--- a/ash/ambient/ambient_access_token_controller.cc
+++ b/ash/ambient/ambient_access_token_controller.cc
@@ -92,7 +92,8 @@
     return;
   }
 
-  VLOG(1) << "Access token fetched.";
+  DVLOG(1) << "Access token fetched.";
+  DCHECK(gaia_id_.empty() || gaia_id_ == gaia_id);
   refresh_token_retry_backoff_.Reset();
   gaia_id_ = gaia_id;
   access_token_ = access_token;
diff --git a/ash/ambient/ambient_constants.h b/ash/ambient/ambient_constants.h
index cbb0ab27..eb4f5e67 100644
--- a/ash/ambient/ambient_constants.h
+++ b/ash/ambient/ambient_constants.h
@@ -24,6 +24,10 @@
 constexpr base::TimeDelta kPhotoRefreshInterval =
     base::TimeDelta::FromSeconds(60);
 
+// The default interval to refresh weather.
+constexpr base::TimeDelta kWeatherRefreshInterval =
+    base::TimeDelta::FromMinutes(5);
+
 // The batch size of topics to fetch in one request.
 // Magic number 2 is based on experiments that no curation on Google Photos.
 constexpr int kTopicsBatchSize = 2;
diff --git a/ash/ambient/ambient_photo_controller.cc b/ash/ambient/ambient_photo_controller.cc
index 3acdcaae..b5ea5c9 100644
--- a/ash/ambient/ambient_photo_controller.cc
+++ b/ash/ambient/ambient_photo_controller.cc
@@ -229,10 +229,16 @@
 
 void AmbientPhotoController::StartScreenUpdate() {
   FetchTopics();
+  FetchWeather();
+  weather_refresh_timer_.Start(
+      FROM_HERE, kWeatherRefreshInterval,
+      base::BindRepeating(&AmbientPhotoController::FetchWeather,
+                          weak_factory_.GetWeakPtr()));
 }
 
 void AmbientPhotoController::StopScreenUpdate() {
   photo_refresh_timer_.Stop();
+  weather_refresh_timer_.Stop();
   topic_index_ = 0;
   image_refresh_started_ = false;
   retries_to_read_from_cache_ = kMaxNumberOfCachedImages;
@@ -262,6 +268,15 @@
                          weak_factory_.GetWeakPtr()));
 }
 
+void AmbientPhotoController::FetchWeather() {
+  Shell::Get()
+      ->ambient_controller()
+      ->ambient_backend_controller()
+      ->FetchWeather(base::BindOnce(
+          &AmbientPhotoController::StartDownloadingWeatherConditionIcon,
+          weak_factory_.GetWeakPtr()));
+}
+
 void AmbientPhotoController::ClearCache() {
   task_runner_->PostTask(FROM_HERE,
                          base::BindOnce(&DeletePathRecursively, GetRootPath()));
@@ -427,9 +442,9 @@
     base::RepeatingClosure on_done,
     std::unique_ptr<std::string> details,
     std::unique_ptr<std::string> data) {
-  if (is_related_image)
+  if (is_related_image) {
     related_image_data_ = std::move(data);
-  else {
+  } else {
     image_data_ = std::move(data);
     image_details_ = std::move(details);
   }
diff --git a/ash/ambient/ambient_photo_controller.h b/ash/ambient/ambient_photo_controller.h
index 7e7689f2..d2894dc0 100644
--- a/ash/ambient/ambient_photo_controller.h
+++ b/ash/ambient/ambient_photo_controller.h
@@ -106,6 +106,8 @@
 
   void FetchTopics();
 
+  void FetchWeather();
+
   void ScheduleFetchTopics(bool backoff);
 
   void ScheduleRefreshImage();
@@ -181,6 +183,9 @@
   // The timer to refresh photos.
   base::OneShotTimer photo_refresh_timer_;
 
+  // The timer to refresh weather information.
+  base::RepeatingTimer weather_refresh_timer_;
+
   // The index of a topic to download.
   size_t topic_index_ = 0;
 
diff --git a/ash/ambient/ambient_photo_controller_unittest.cc b/ash/ambient/ambient_photo_controller_unittest.cc
index c3d4716..70aa5f5 100644
--- a/ash/ambient/ambient_photo_controller_unittest.cc
+++ b/ash/ambient/ambient_photo_controller_unittest.cc
@@ -12,6 +12,7 @@
 #include "ash/ambient/model/ambient_backend_model.h"
 #include "ash/ambient/test/ambient_ash_test_base.h"
 #include "ash/public/cpp/ambient/ambient_backend_controller.h"
+#include "ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h"
 #include "ash/shell.h"
 #include "base/barrier_closure.h"
 #include "base/base_paths.h"
@@ -345,4 +346,33 @@
   base::DeletePathRecursively(ambient_image_path);
 }
 
+TEST_F(AmbientPhotoControllerTest, ShouldStartToRefreshWeather) {
+  auto* model = photo_controller()->ambient_backend_model();
+  EXPECT_FALSE(model->show_celsius());
+  EXPECT_TRUE(model->weather_condition_icon().isNull());
+
+  WeatherInfo info;
+  info.show_celsius = true;
+  info.condition_icon_url = "https://fake-icon-url";
+  info.temp_f = 70.0f;
+  backend_controller()->SetWeatherInfo(info);
+
+  // Start to refresh weather as screen update starts.
+  photo_controller()->StartScreenUpdate();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(model->show_celsius());
+  EXPECT_FALSE(model->weather_condition_icon().isNull());
+  EXPECT_GT(info.temp_f, 0);
+
+  // Refresh weather again after time passes.
+  info.show_celsius = false;
+  info.temp_f = -70.0f;
+  backend_controller()->SetWeatherInfo(info);
+
+  FastForwardToRefreshWeather();
+  EXPECT_FALSE(model->show_celsius());
+  EXPECT_LT(info.temp_f, 0);
+}
+
 }  // namespace ash
diff --git a/ash/ambient/backdrop/ambient_backend_controller_impl.cc b/ash/ambient/backdrop/ambient_backend_controller_impl.cc
index 835759c..2791f2011 100644
--- a/ash/ambient/backdrop/ambient_backend_controller_impl.cc
+++ b/ash/ambient/backdrop/ambient_backend_controller_impl.cc
@@ -25,9 +25,11 @@
 #include "chromeos/assistant/internal/proto/google3/backdrop/backdrop.pb.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/prefs/pref_service.h"
+#include "components/user_manager/user_manager.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/data_decoder/public/cpp/data_decoder.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
@@ -142,6 +144,30 @@
   }
 }
 
+WeatherInfo ToWeatherInfo(const base::Value& result) {
+  DCHECK(result.is_list());
+
+  WeatherInfo weather_info;
+  const auto& list_result = result.GetList();
+
+  const base::Value& condition_icon_url_value =
+      list_result[backdrop::WeatherInfo::kConditionIconUrlFieldNumber - 1];
+  if (!condition_icon_url_value.is_none())
+    weather_info.condition_icon_url = condition_icon_url_value.GetString();
+
+  const base::Value& temp_f_value =
+      list_result[backdrop::WeatherInfo::kTempFFieldNumber - 1];
+  if (!temp_f_value.is_none())
+    weather_info.temp_f = temp_f_value.GetDouble();
+
+  const base::Value& show_celsius_value =
+      list_result[backdrop::WeatherInfo::kShowCelsiusFieldNumber - 1];
+  if (!show_celsius_value.is_none())
+    weather_info.show_celsius = show_celsius_value.GetBool();
+
+  return weather_info;
+}
+
 // Helper function to save the information we got from the backdrop server to a
 // public struct so that they can be accessed by public codes.
 ScreenUpdate ToScreenUpdate(
@@ -358,6 +384,47 @@
       ->SetPhotoRefreshInterval(interval);
 }
 
+void AmbientBackendControllerImpl::FetchWeather(FetchWeatherCallback callback) {
+  auto response_handler =
+      [](FetchWeatherCallback callback,
+         std::unique_ptr<BackdropURLLoader> backdrop_url_loader,
+         std::unique_ptr<std::string> response) {
+        if (response && !response->empty()) {
+          auto json_handler =
+              [](FetchWeatherCallback callback,
+                 data_decoder::DataDecoder::ValueOrError result) {
+                if (result.value) {
+                  std::move(callback).Run(ToWeatherInfo(result.value.value()));
+                } else {
+                  DVLOG(1) << "Failed to parse weather json.";
+                  std::move(callback).Run(base::nullopt);
+                }
+              };
+
+          constexpr char kJsonPrefix[] = ")]}'\n";
+          data_decoder::DataDecoder::ParseJsonIsolated(
+              response->substr(strlen(kJsonPrefix)),
+              base::BindOnce(json_handler, std::move(callback)));
+        } else {
+          std::move(callback).Run(base::nullopt);
+        }
+      };
+
+  const auto* user = user_manager::UserManager::Get()->GetActiveUser();
+  DCHECK(user->HasGaiaAccount());
+  BackdropClientConfig::Request request =
+      backdrop_client_config_.CreateFetchWeatherInfoRequest(
+          user->GetAccountId().GetGaiaId(), GetClientId());
+  std::unique_ptr<network::ResourceRequest> resource_request =
+      CreateResourceRequest(request);
+  auto backdrop_url_loader = std::make_unique<BackdropURLLoader>();
+  auto* loader_ptr = backdrop_url_loader.get();
+  loader_ptr->Start(std::move(resource_request), /*request_body=*/base::nullopt,
+                    NO_TRAFFIC_ANNOTATION_YET,
+                    base::BindOnce(response_handler, std::move(callback),
+                                   std::move(backdrop_url_loader)));
+}
+
 void AmbientBackendControllerImpl::FetchScreenUpdateInfoInternal(
     int num_topics,
     OnScreenUpdateInfoFetchedCallback callback,
diff --git a/ash/ambient/backdrop/ambient_backend_controller_impl.h b/ash/ambient/backdrop/ambient_backend_controller_impl.h
index 05bd27a8..68104c2 100644
--- a/ash/ambient/backdrop/ambient_backend_controller_impl.h
+++ b/ash/ambient/backdrop/ambient_backend_controller_impl.h
@@ -48,6 +48,7 @@
       int num_albums,
       OnSettingsAndAlbumsFetchedCallback callback) override;
   void SetPhotoRefreshInterval(base::TimeDelta interval) override;
+  void FetchWeather(FetchWeatherCallback callback) override;
 
  private:
   using BackdropClientConfig = chromeos::ambient::BackdropClientConfig;
diff --git a/ash/ambient/test/ambient_ash_test_base.cc b/ash/ambient/test/ambient_ash_test_base.cc
index ea930308..9626297 100644
--- a/ash/ambient/test/ambient_ash_test_base.cc
+++ b/ash/ambient/test/ambient_ash_test_base.cc
@@ -249,6 +249,10 @@
   task_environment()->FastForwardBy(1.2 * kPhotoRefreshInterval);
 }
 
+void AmbientAshTestBase::FastForwardToRefreshWeather() {
+  task_environment()->FastForwardBy(1.2 * kWeatherRefreshInterval);
+}
+
 int AmbientAshTestBase::GetNumOfActiveWakeLocks(
     device::mojom::WakeLockType type) {
   base::RunLoop run_loop;
@@ -293,6 +297,11 @@
   return ambient_controller()->access_token_controller_for_testing();
 }
 
+FakeAmbientBackendControllerImpl* AmbientAshTestBase::backend_controller() {
+  return static_cast<FakeAmbientBackendControllerImpl*>(
+      ambient_controller()->ambient_backend_controller());
+}
+
 void AmbientAshTestBase::FetchTopics() {
   photo_controller()->FetchTopicsForTesting();
 }
diff --git a/ash/ambient/test/ambient_ash_test_base.h b/ash/ambient/test/ambient_ash_test_base.h
index 45daec8..188dab6 100644
--- a/ash/ambient/test/ambient_ash_test_base.h
+++ b/ash/ambient/test/ambient_ash_test_base.h
@@ -28,6 +28,7 @@
 class AmbientAccessTokenController;
 class AmbientContainerView;
 class AmbientPhotoController;
+class FakeAmbientBackendControllerImpl;
 class MediaStringView;
 
 // The base class to test the Ambient Mode in Ash.
@@ -95,6 +96,9 @@
   // Advance the task environment timer to load the next photo.
   void FastForwardToNextImage();
 
+  // Advance the task environment timer to load the weather info.
+  void FastForwardToRefreshWeather();
+
   // Returns the number of active wake locks of type |type|.
   int GetNumOfActiveWakeLocks(device::mojom::WakeLockType type);
 
@@ -120,6 +124,8 @@
 
   AmbientAccessTokenController* token_controller();
 
+  FakeAmbientBackendControllerImpl* backend_controller();
+
   void FetchTopics();
 
   void FetchImage();
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index ef97d6b..94adb67 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -297,6 +297,27 @@
       <message name="IDS_ASH_STATUS_TRAY_MESSAGE_OUT_OF_USERS" desc="The error message when all the users are added into multi-profile session.">
         All available users have already been added to this session.
       </message>
+      <message name="IDS_ASH_STATUS_TRAY_NEARBY_SHARE_BUTTON_LABEL" desc="The shorter label used for the button in the status tray to toggle Nearby Share high visibility mode, which makes the device visible to all nearby devices for file sharing. [CHAR_LIMIT=14]">
+        Nearby Share
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_NEARBY_SHARE_OFF_STATE" desc="Sub-label for the Nearby Share system tray button." meaning="Nearby Share high visibility mode is currently inactive. [CHAR_LIMIT=14]">
+        Off
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_NEARBY_SHARE_ON_STATE" desc="Sub-label for the Nearby Share system tray button." meaning="Nearby Share high visibility mode is currently active with the displayed time remaining. [CHAR_LIMIT=14]">
+        On, <ph name="REMAINING_TIME">$1<ex>40 sec</ex></ph>
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_NEARBY_SHARE_REMAINING_MINUTES" desc="A short string showing the nearest number of whole minutes until Nearby Share high visibility turns off. To be used with IDS_ASH_STATUS_TRAY_NEARBY_SHARE_ON_STATE. [CHAR_LIMIT=7]">
+        <ph name="MINUTES">$1<ex>2</ex></ph> min
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_NEARBY_SHARE_REMAINING_SECONDS" desc="A short string showing the nearest number of whole seconds until Nearby Share high visibility turns off. To be used with IDS_ASH_STATUS_TRAY_NEARBY_SHARE_ON_STATE. [CHAR_LIMIT=7]">
+        <ph name="SECONDS">$1<ex>40</ex></ph> sec
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_NEARBY_SHARE_SETTINGS_TOOLTIP" desc="Tooltip text for the status tray button that shows settings for the Nearby Share feature.">
+        Show Nearby Share settings.
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_NEARBY_SHARE_TOGGLE_TOOLTIP" desc="Tooltip text used for the Nearby Share status tray button, which toggles Nearby Share high visibility mode on/off.">
+        Toggle Nearby Share high visibility.
+      </message>
       <message name="IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_BUTTON_LABEL" desc="The shorter label used for the button in the status tray to toggle the Night Light feature (which controls the color temperature of the screen) on or off. [CHAR_LIMIT=14]">
         Night Light
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_BUTTON_LABEL.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_BUTTON_LABEL.png.sha1
new file mode 100644
index 0000000..deb30a3
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_BUTTON_LABEL.png.sha1
@@ -0,0 +1 @@
+8ecad2331b353432d482d6d01a448f1535406806
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_OFF_STATE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_OFF_STATE.png.sha1
new file mode 100644
index 0000000..deb30a3
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_OFF_STATE.png.sha1
@@ -0,0 +1 @@
+8ecad2331b353432d482d6d01a448f1535406806
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_ON_STATE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_ON_STATE.png.sha1
new file mode 100644
index 0000000..457835c7
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_ON_STATE.png.sha1
@@ -0,0 +1 @@
+88560051be325ecc1bb71f35931605f5a2f88e9f
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_REMAINING_MINUTES.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_REMAINING_MINUTES.png.sha1
new file mode 100644
index 0000000..457835c7
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_REMAINING_MINUTES.png.sha1
@@ -0,0 +1 @@
+88560051be325ecc1bb71f35931605f5a2f88e9f
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_REMAINING_SECONDS.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_REMAINING_SECONDS.png.sha1
new file mode 100644
index 0000000..69559fd
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_REMAINING_SECONDS.png.sha1
@@ -0,0 +1 @@
+3c57515398623b601c60aded61eb9efcf0a018a7
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_SETTINGS_TOOLTIP.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_SETTINGS_TOOLTIP.png.sha1
new file mode 100644
index 0000000..e264bcc
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_SETTINGS_TOOLTIP.png.sha1
@@ -0,0 +1 @@
+14fa83bd6816e26df5e9ac72cbceb09818990031
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_TOGGLE_TOOLTIP.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_TOGGLE_TOOLTIP.png.sha1
new file mode 100644
index 0000000..3371c2cd
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_NEARBY_SHARE_TOGGLE_TOOLTIP.png.sha1
@@ -0,0 +1 @@
+14577c7eda36f1ee897e9d6974c02c969d478199
\ No newline at end of file
diff --git a/ash/clipboard/clipboard_history_controller_unittest.cc b/ash/clipboard/clipboard_history_controller_unittest.cc
index e5eee3b..28aa947b 100644
--- a/ash/clipboard/clipboard_history_controller_unittest.cc
+++ b/ash/clipboard/clipboard_history_controller_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/location.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/unguessable_token.h"
@@ -103,41 +104,66 @@
 
 // Tests that search + v with no history fails to show a menu.
 TEST_F(ClipboardHistoryControllerTest, NoHistoryNoMenu) {
+  base::HistogramTester histogram_tester;
   ShowMenu();
 
   EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 0);
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 0);
 }
 
 // Tests that search + v shows a menu when there is something to show.
 TEST_F(ClipboardHistoryControllerTest, MultiShowMenu) {
+  base::HistogramTester histogram_tester;
   // Copy something to enable the clipboard history menu.
   WriteToClipboard("test");
 
   ShowMenu();
 
   EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
+  histogram_tester.ExpectBucketCount(
+      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 1, 1);
+  // No UserJourneyTime should be recorded as the menu is still showing.
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 0);
 
   // Hide the menu.
   GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, /*flags=*/0);
   GetEventGenerator()->ReleaseKey(ui::VKEY_ESCAPE, /*flags=*/0);
 
   EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
+  histogram_tester.ExpectBucketCount(
+      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 1, 1);
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 1);
 
   // Reshow the menu.
   ShowMenu();
 
   EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
+  histogram_tester.ExpectBucketCount(
+      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 1, 2);
+
+  // No new UserJourneyTime histogram should be recorded as the menu is still
+  // showing.
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 1);
 
   // Hide the menu.
   GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, /*flags=*/0);
   GetEventGenerator()->ReleaseKey(ui::VKEY_ESCAPE, /*flags=*/0);
 
   EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 2);
 }
 
 // Tests that backspace deletes an item, and if it is the last item, the menu is
 // closed.
 TEST_F(ClipboardHistoryControllerTest, BasicShowMenu) {
+  base::HistogramTester histogram_tester;
   WriteToClipboard("test");
   WriteToClipboard("test");
 
@@ -164,6 +190,14 @@
   GetEventGenerator()->ReleaseKey(ui::VKEY_BACK, /*flags=*/0);
 
   EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
+
+  // This histogram is only recorded when the menu is initially built, so this
+  // should not be recorded more than once in one show, regardless of the menu
+  // contents changing.
+  histogram_tester.ExpectBucketCount(
+      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 2, 1);
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 1);
 }
 
 // Verifies that the clipboard history is disabled in some user modes, which
diff --git a/ash/clipboard/clipboard_history_menu_model_adapter.cc b/ash/clipboard/clipboard_history_menu_model_adapter.cc
index 45d0af57..9bab87d 100644
--- a/ash/clipboard/clipboard_history_menu_model_adapter.cc
+++ b/ash/clipboard/clipboard_history_menu_model_adapter.cc
@@ -8,6 +8,7 @@
 #include "ash/clipboard/clipboard_history_util.h"
 #include "ash/clipboard/views/clipboard_history_item_view.h"
 #include "ash/public/cpp/clipboard_image_model_factory.h"
+#include "base/metrics/histogram_macros.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/gfx/geometry/rect.h"
@@ -39,12 +40,19 @@
   DCHECK(model_);
   DCHECK(item_snapshots_.empty());
 
+  menu_open_time_ = base::TimeTicks::Now();
+
   int command_id = ClipboardHistoryUtil::kFirstItemCommandId;
-  for (const auto& item : clipboard_history_->GetItems()) {
+  const auto& items = clipboard_history_->GetItems();
+  // Do not include the final kDeleteCommandId item in histograms, because it is
+  // not shown.
+  UMA_HISTOGRAM_COUNTS_100(
+      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", items.size());
+  for (const auto& item : items) {
     model_->AddItem(command_id, base::string16());
 
     // Enable or disable the command depending on whether its corresponding
-    // clipboard history item is allowed to read or not.
+    // clipboard history item is allowed to be read or not.
     const auto* dlp_controller =
         ui::Clipboard::GetForCurrentThread()->GetClipboardDlpController();
     model_->SetEnabledAt(model_->GetIndexOfCommandId(command_id),
@@ -159,6 +167,10 @@
 
 void ClipboardHistoryMenuModelAdapter::OnMenuClosed(views::MenuItemView* menu) {
   ClipboardImageModelFactory::Get()->Deactivate();
+  const base::TimeDelta user_journey_time =
+      base::TimeTicks::Now() - menu_open_time_;
+  UMA_HISTOGRAM_TIMES("Ash.ClipboardHistory.ContextMenu.UserJourneyTime",
+                      user_journey_time);
   views::MenuModelAdapter::OnMenuClosed(menu);
 }
 
diff --git a/ash/clipboard/clipboard_history_menu_model_adapter.h b/ash/clipboard/clipboard_history_menu_model_adapter.h
index 342fe82..279fcb1 100644
--- a/ash/clipboard/clipboard_history_menu_model_adapter.h
+++ b/ash/clipboard/clipboard_history_menu_model_adapter.h
@@ -9,6 +9,7 @@
 
 #include "ash/ash_export.h"
 #include "base/optional.h"
+#include "base/time/time.h"
 #include "base/unguessable_token.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/views/controls/menu/menu_model_adapter.h"
@@ -95,6 +96,9 @@
   // Responsible for showing |root_view_|.
   std::unique_ptr<views::MenuRunner> menu_runner_;
 
+  // The timestamp taken when the menu is opened. Used in metrics.
+  base::TimeTicks menu_open_time_;
+
   // The mapping between the command ids and items that are copied from
   // `clipboard_history_` when the menu is created. It is used to solve the
   // possible inconsistency between the menu model data and the clipboard
diff --git a/ash/host/transformer_helper.cc b/ash/host/transformer_helper.cc
index 9cd2615..12080e5 100644
--- a/ash/host/transformer_helper.cc
+++ b/ash/host/transformer_helper.cc
@@ -10,13 +10,14 @@
 #include "ash/host/root_window_transformer.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
-#include "ui/compositor/dip_util.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_element.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
+#include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/size_conversions.h"
@@ -43,10 +44,12 @@
   }
 
   gfx::Rect GetRootWindowBounds(const gfx::Size& host_size) const override {
-    gfx::Rect bounds(host_size);
-    gfx::RectF new_bounds(ui::ConvertRectToDIP(root_window_->layer(), bounds));
-    GetInverseTransform().TransformRect(&new_bounds);
-    return gfx::Rect(gfx::ToFlooredSize(new_bounds.size()));
+    gfx::Rect host_bounds_in_pixels(host_size);
+    gfx::RectF host_bounds_in_dips = gfx::ConvertRectToDips(
+        host_bounds_in_pixels, root_window_->layer()->device_scale_factor());
+    gfx::RectF root_window_bounds = host_bounds_in_dips;
+    GetInverseTransform().TransformRect(&root_window_bounds);
+    return gfx::Rect(gfx::ToFlooredSize(root_window_bounds.size()));
   }
 
   gfx::Insets GetHostInsets() const override { return gfx::Insets(); }
diff --git a/ash/public/cpp/ambient/ambient_backend_controller.h b/ash/public/cpp/ambient/ambient_backend_controller.h
index a50bd2e..87ea5e64 100644
--- a/ash/public/cpp/ambient/ambient_backend_controller.h
+++ b/ash/public/cpp/ambient/ambient_backend_controller.h
@@ -108,6 +108,8 @@
   using OnSettingsAndAlbumsFetchedCallback =
       base::OnceCallback<void(const base::Optional<AmbientSettings>& settings,
                               PersonalAlbums personal_albums)>;
+  using FetchWeatherCallback =
+      base::OnceCallback<void(const base::Optional<WeatherInfo>& weather_info)>;
 
   static AmbientBackendController* Get();
 
@@ -154,6 +156,9 @@
 
   // Set the photo refresh interval in ambient mode.
   virtual void SetPhotoRefreshInterval(base::TimeDelta interval) = 0;
+
+  // Fetch the weather information.
+  virtual void FetchWeather(FetchWeatherCallback) = 0;
 };
 
 }  // namespace ash
diff --git a/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.cc b/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.cc
index e0c716f..2deaa0f 100644
--- a/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.cc
+++ b/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.cc
@@ -85,14 +85,17 @@
   topic.related_image_url = kFakeUrl;
   topic.topic_type = AmbientModeTopicType::kCulturalInstitute;
 
-  ash::WeatherInfo weather_info;
-  weather_info.temp_f = .0f;
-  weather_info.condition_icon_url = kFakeUrl;
-  weather_info.show_celsius = true;
-
   ash::ScreenUpdate update;
   update.next_topics.emplace_back(topic);
-  update.weather_info = weather_info;
+
+  // Only respond weather info when there is no active weather testing.
+  if (!weather_info_) {
+    ash::WeatherInfo weather_info;
+    weather_info.temp_f = .0f;
+    weather_info.condition_icon_url = kFakeUrl;
+    weather_info.show_celsius = true;
+    update.weather_info = weather_info;
+  }
 
   // Pretend to respond asynchronously.
   base::SequencedTaskRunnerHandle::Get()->PostTask(
@@ -153,6 +156,11 @@
   NOTIMPLEMENTED();
 }
 
+void FakeAmbientBackendControllerImpl::FetchWeather(
+    FetchWeatherCallback callback) {
+  std::move(callback).Run(weather_info_);
+}
+
 void FakeAmbientBackendControllerImpl::ReplyFetchSettingsAndAlbums(
     bool success) {
   if (!pending_fetch_settings_albums_callback_)
@@ -182,4 +190,9 @@
   return !pending_update_callback_.is_null();
 }
 
+void FakeAmbientBackendControllerImpl::SetWeatherInfo(
+    base::Optional<WeatherInfo> info) {
+  weather_info_ = std::move(info);
+}
+
 }  // namespace ash
diff --git a/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h b/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h
index e8e6728..ece3f88e 100644
--- a/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h
+++ b/ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h
@@ -40,6 +40,7 @@
       int num_albums,
       OnSettingsAndAlbumsFetchedCallback callback) override;
   void SetPhotoRefreshInterval(base::TimeDelta interval) override;
+  void FetchWeather(FetchWeatherCallback callback) override;
 
   // Simulate to reply the request of FetchSettingsAndAlbums().
   // If |success| is true, will return fake data.
@@ -55,10 +56,16 @@
   // Whether there is a pending UpdateSettings() request.
   bool IsUpdateSettingsPending() const;
 
+  // Sets the weather info that will be returned in subsequent calls to
+  // `FetchWeather`.
+  void SetWeatherInfo(base::Optional<WeatherInfo> info);
+
  private:
   OnSettingsAndAlbumsFetchedCallback pending_fetch_settings_albums_callback_;
 
   UpdateSettingsCallback pending_update_callback_;
+
+  base::Optional<WeatherInfo> weather_info_;
 };
 
 }  // namespace ash
diff --git a/ash/shell.cc b/ash/shell.cc
index 93892f3..97235f80 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -183,6 +183,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "dbus/bus.h"
+#include "media/base/media_switches.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/env.h"
 #include "ui/aura/layout_manager.h"
@@ -1218,7 +1219,8 @@
   // is started.
   display_manager_->CreateMirrorWindowAsyncIfAny();
 
-  if (base::FeatureList::IsEnabled(features::kMediaSessionNotification)) {
+  if (base::FeatureList::IsEnabled(features::kMediaSessionNotification) &&
+      !base::FeatureList::IsEnabled(media::kGlobalMediaControlsForChromeOS)) {
     media_notification_controller_ =
         std::make_unique<MediaNotificationControllerImpl>();
   }
diff --git a/ash/system/accessibility/switch_access_menu_view.cc b/ash/system/accessibility/switch_access_menu_view.cc
index 514af25..f081b5c 100644
--- a/ash/system/accessibility/switch_access_menu_view.cc
+++ b/ash/system/accessibility/switch_access_menu_view.cc
@@ -11,6 +11,7 @@
 #include "ash/system/accessibility/switch_access_menu_button.h"
 #include "ash/system/tray/tray_constants.h"
 #include "base/containers/flat_map.h"
+#include "base/no_destructor.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/accessibility/mojom/ax_node_data.mojom-shared.h"
 #include "ui/events/event.h"
@@ -28,58 +29,66 @@
 
 // These strings must match the values of
 // accessibility_private::SwitchAccessMenuAction.
-const base::flat_map<std::string, ButtonInfo> kMenuButtonDetails = {
-    {"copy", {&kSwitchAccessCopyIcon, IDS_ASH_SWITCH_ACCESS_COPY}},
-    {"cut", {&kSwitchAccessCutIcon, IDS_ASH_SWITCH_ACCESS_CUT}},
-    {"decrement",
-     {&kSwitchAccessDecrementIcon, IDS_ASH_SWITCH_ACCESS_DECREMENT}},
-    {"dictation", {&kDictationOnNewuiIcon, IDS_ASH_SWITCH_ACCESS_DICTATION}},
-    {"endTextSelection",
-     {&kSwitchAccessEndTextSelectionIcon,
-      IDS_ASH_SWITCH_ACCESS_END_TEXT_SELECTION}},
-    {"increment",
-     {&kSwitchAccessIncrementIcon, IDS_ASH_SWITCH_ACCESS_INCREMENT}},
-    {"jumpToBeginningOfText",
-     {&kSwitchAccessJumpToBeginningOfTextIcon,
-      IDS_ASH_SWITCH_ACCESS_JUMP_TO_BEGINNING_OF_TEXT}},
-    {"jumpToEndOfText",
-     {&kSwitchAccessJumpToEndOfTextIcon,
-      IDS_ASH_SWITCH_ACCESS_JUMP_TO_END_OF_TEXT}},
-    {"keyboard", {&kSwitchAccessKeyboardIcon, IDS_ASH_SWITCH_ACCESS_KEYBOARD}},
-    {"moveBackwardOneCharOfText",
-     {&kSwitchAccessMoveBackwardOneCharOfTextIcon,
-      IDS_ASH_SWITCH_ACCESS_MOVE_BACKWARD_ONE_CHAR_OF_TEXT}},
-    {"moveBackwardOneWordOfText",
-     {&kSwitchAccessMoveBackwardOneWordOfTextIcon,
-      IDS_ASH_SWITCH_ACCESS_MOVE_BACKWARD_ONE_WORD_OF_TEXT}},
-    {"moveCursor",
-     {&kSwitchAccessMoveCursorIcon, IDS_ASH_SWITCH_ACCESS_MOVE_CURSOR}},
-    {"moveDownOneLineOfText",
-     {&kSwitchAccessMoveDownOneLineOfTextIcon,
-      IDS_ASH_SWITCH_ACCESS_MOVE_DOWN_ONE_LINE_OF_TEXT}},
-    {"moveForwardOneCharOfText",
-     {&kSwitchAccessMoveForwardOneCharOfTextIcon,
-      IDS_ASH_SWITCH_ACCESS_MOVE_FORWARD_ONE_CHAR_OF_TEXT}},
-    {"moveForwardOneWordOfText",
-     {&kSwitchAccessMoveForwardOneWordOfTextIcon,
-      IDS_ASH_SWITCH_ACCESS_MOVE_FORWARD_ONE_WORD_OF_TEXT}},
-    {"moveUpOneLineOfText",
-     {&kSwitchAccessMoveUpOneLineOfTextIcon,
-      IDS_ASH_SWITCH_ACCESS_MOVE_UP_ONE_LINE_OF_TEXT}},
-    {"paste", {&kSwitchAccessPasteIcon, IDS_ASH_SWITCH_ACCESS_PASTE}},
-    {"scrollDown",
-     {&kSwitchAccessScrollDownIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_DOWN}},
-    {"scrollLeft",
-     {&kSwitchAccessScrollLeftIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_LEFT}},
-    {"scrollRight",
-     {&kSwitchAccessScrollRightIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_RIGHT}},
-    {"scrollUp", {&kSwitchAccessScrollUpIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_UP}},
-    {"select", {&kSwitchAccessSelectIcon, IDS_ASH_SWITCH_ACCESS_SELECT}},
-    {"settings", {&kSwitchAccessSettingsIcon, IDS_ASH_SWITCH_ACCESS_SETTINGS}},
-    {"startTextSelection",
-     {&kSwitchAccessStartTextSelectionIcon,
-      IDS_ASH_SWITCH_ACCESS_START_TEXT_SELECTION}},
-};
+const base::flat_map<std::string, ButtonInfo>& GetMenuButtonDetails() {
+  static const base::NoDestructor<base::flat_map<std::string, ButtonInfo>>
+      kMenuButtonDetails({
+          {"copy", {&kSwitchAccessCopyIcon, IDS_ASH_SWITCH_ACCESS_COPY}},
+          {"cut", {&kSwitchAccessCutIcon, IDS_ASH_SWITCH_ACCESS_CUT}},
+          {"decrement",
+           {&kSwitchAccessDecrementIcon, IDS_ASH_SWITCH_ACCESS_DECREMENT}},
+          {"dictation",
+           {&kDictationOnNewuiIcon, IDS_ASH_SWITCH_ACCESS_DICTATION}},
+          {"endTextSelection",
+           {&kSwitchAccessEndTextSelectionIcon,
+            IDS_ASH_SWITCH_ACCESS_END_TEXT_SELECTION}},
+          {"increment",
+           {&kSwitchAccessIncrementIcon, IDS_ASH_SWITCH_ACCESS_INCREMENT}},
+          {"jumpToBeginningOfText",
+           {&kSwitchAccessJumpToBeginningOfTextIcon,
+            IDS_ASH_SWITCH_ACCESS_JUMP_TO_BEGINNING_OF_TEXT}},
+          {"jumpToEndOfText",
+           {&kSwitchAccessJumpToEndOfTextIcon,
+            IDS_ASH_SWITCH_ACCESS_JUMP_TO_END_OF_TEXT}},
+          {"keyboard",
+           {&kSwitchAccessKeyboardIcon, IDS_ASH_SWITCH_ACCESS_KEYBOARD}},
+          {"moveBackwardOneCharOfText",
+           {&kSwitchAccessMoveBackwardOneCharOfTextIcon,
+            IDS_ASH_SWITCH_ACCESS_MOVE_BACKWARD_ONE_CHAR_OF_TEXT}},
+          {"moveBackwardOneWordOfText",
+           {&kSwitchAccessMoveBackwardOneWordOfTextIcon,
+            IDS_ASH_SWITCH_ACCESS_MOVE_BACKWARD_ONE_WORD_OF_TEXT}},
+          {"moveCursor",
+           {&kSwitchAccessMoveCursorIcon, IDS_ASH_SWITCH_ACCESS_MOVE_CURSOR}},
+          {"moveDownOneLineOfText",
+           {&kSwitchAccessMoveDownOneLineOfTextIcon,
+            IDS_ASH_SWITCH_ACCESS_MOVE_DOWN_ONE_LINE_OF_TEXT}},
+          {"moveForwardOneCharOfText",
+           {&kSwitchAccessMoveForwardOneCharOfTextIcon,
+            IDS_ASH_SWITCH_ACCESS_MOVE_FORWARD_ONE_CHAR_OF_TEXT}},
+          {"moveForwardOneWordOfText",
+           {&kSwitchAccessMoveForwardOneWordOfTextIcon,
+            IDS_ASH_SWITCH_ACCESS_MOVE_FORWARD_ONE_WORD_OF_TEXT}},
+          {"moveUpOneLineOfText",
+           {&kSwitchAccessMoveUpOneLineOfTextIcon,
+            IDS_ASH_SWITCH_ACCESS_MOVE_UP_ONE_LINE_OF_TEXT}},
+          {"paste", {&kSwitchAccessPasteIcon, IDS_ASH_SWITCH_ACCESS_PASTE}},
+          {"scrollDown",
+           {&kSwitchAccessScrollDownIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_DOWN}},
+          {"scrollLeft",
+           {&kSwitchAccessScrollLeftIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_LEFT}},
+          {"scrollRight",
+           {&kSwitchAccessScrollRightIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_RIGHT}},
+          {"scrollUp",
+           {&kSwitchAccessScrollUpIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_UP}},
+          {"select", {&kSwitchAccessSelectIcon, IDS_ASH_SWITCH_ACCESS_SELECT}},
+          {"settings",
+           {&kSwitchAccessSettingsIcon, IDS_ASH_SWITCH_ACCESS_SETTINGS}},
+          {"startTextSelection",
+           {&kSwitchAccessStartTextSelectionIcon,
+            IDS_ASH_SWITCH_ACCESS_START_TEXT_SELECTION}},
+      });
+  return *kMenuButtonDetails;
+}
 
 }  // namespace
 
@@ -103,8 +112,8 @@
 
   int button_count = 0;
   for (std::string action : actions) {
-    auto it = kMenuButtonDetails.find(action);
-    if (it == kMenuButtonDetails.end())
+    auto it = GetMenuButtonDetails().find(action);
+    if (it == GetMenuButtonDetails().end())
       continue;
     ButtonInfo info = it->second;
     // If this is the first button of a new row, tell the layout to start a
diff --git a/ash/system/nearby_share/nearby_share_feature_pod_controller.cc b/ash/system/nearby_share/nearby_share_feature_pod_controller.cc
index e8f7a045..8b2e73e 100644
--- a/ash/system/nearby_share/nearby_share_feature_pod_controller.cc
+++ b/ash/system/nearby_share/nearby_share_feature_pod_controller.cc
@@ -8,12 +8,14 @@
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/unified/feature_pod_button.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace ash {
 
@@ -24,12 +26,14 @@
 
 base::string16 RemainingTimeString(base::TimeDelta remaining_time) {
   if (remaining_time > kOneMinute) {
-    return base::ASCIIToUTF16(
-        base::NumberToString(remaining_time.InMinutes() + 1) + " min");
+    return l10n_util::GetStringFUTF16Int(
+        IDS_ASH_STATUS_TRAY_NEARBY_SHARE_REMAINING_MINUTES,
+        remaining_time.InMinutes() + 1);
   }
 
-  return base::ASCIIToUTF16(
-      base::NumberToString(remaining_time.InSeconds() + 1) + " sec");
+  return l10n_util::GetStringFUTF16Int(
+      IDS_ASH_STATUS_TRAY_NEARBY_SHARE_REMAINING_SECONDS,
+      static_cast<int>(remaining_time.InSeconds()) + 1);
 }
 
 }  // namespace
@@ -64,8 +68,12 @@
                       session_controller->IsActiveUserSessionStarted() &&
                       session_controller->IsUserPrimary() &&
                       !session_controller->IsScreenLocked());
-  button_->SetLabel(base::ASCIIToUTF16("Nearby Share"));
-  button_->SetLabelTooltip(base::ASCIIToUTF16("Show Nearby Share Settings."));
+  button_->SetLabel(
+      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NEARBY_SHARE_BUTTON_LABEL));
+  button_->SetLabelTooltip(l10n_util::GetStringUTF16(
+      IDS_ASH_STATUS_TRAY_NEARBY_SHARE_SETTINGS_TOOLTIP));
+  button_->SetIconTooltip(l10n_util::GetStringUTF16(
+      IDS_ASH_STATUS_TRAY_NEARBY_SHARE_TOGGLE_TOOLTIP));
   bool enabled = nearby_share_delegate_->IsHighVisibilityOn();
   OnHighVisibilityEnabledChanged(enabled);
   return button_;
@@ -99,25 +107,18 @@
 }
 
 void NearbyShareFeaturePodController::UpdateButton(bool enabled) {
-  // TODO(crrev/c/2401842): l10n strings
-
   button_->SetToggled(enabled);
   button_->SetVectorIcon(enabled ? kUnifiedMenuNearbyShareVisibleIcon
                                  : kUnifiedMenuNearbyShareNotVisibleIcon);
 
   if (enabled) {
-    button_->SetSubLabel(base::ASCIIToUTF16("On, ") +
-                         RemainingTimeString(RemainingHighVisibilityTime()));
+    button_->SetSubLabel(l10n_util::GetStringFUTF16(
+        IDS_ASH_STATUS_TRAY_NEARBY_SHARE_ON_STATE,
+        RemainingTimeString(RemainingHighVisibilityTime())));
   } else {
-    button_->SetSubLabel(base::ASCIIToUTF16("Off"));
+    button_->SetSubLabel(
+        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NEARBY_SHARE_OFF_STATE));
   }
-
-  base::string16 tooltip_state =
-      enabled ? base::ASCIIToUTF16("High visibility on.")
-              : base::ASCIIToUTF16("High visibility off");
-  button_->SetIconTooltip(
-      base::ASCIIToUTF16("Toggle Nearby Share high visibility.") +
-      tooltip_state);
 }
 
 base::TimeDelta NearbyShareFeaturePodController::RemainingHighVisibilityTime()
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 28aac95..9b4aab5 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1279,6 +1279,7 @@
     ":logging_buildflags",
     ":orderfile_buildflags",
     ":partition_alloc_buildflags",
+    ":profiler_buildflags",
     ":sanitizer_buildflags",
     ":synchronization_buildflags",
     ":tracing_buildflags",
@@ -2248,7 +2249,6 @@
     "CAN_UNWIND_WITH_FRAME_POINTERS=$can_unwind_with_frame_pointers",
     "UNSAFE_DEVELOPER_BUILD=$is_unsafe_developer_build",
     "CAN_UNWIND_WITH_CFI_TABLE=$can_unwind_with_cfi_table",
-    "ENABLE_ARM_CFI_TABLE=$enable_arm_cfi_table",
     "EXCLUDE_UNWIND_TABLES=$exclude_unwind_tables",
     "ENABLE_GDBINIT_WARNING=$enable_gdbinit_warning",
     "ENABLE_LLDBINIT_WARNING=$enable_lldbinit_warning",
@@ -2330,6 +2330,13 @@
   ]
 }
 
+buildflag_header("profiler_buildflags") {
+  header = "profiler_buildflags.h"
+  header_dir = "base/profiler"
+
+  flags = [ "ENABLE_ARM_CFI_TABLE=$enable_arm_cfi_table" ]
+}
+
 # This is the subset of files from base that should not be used with a dynamic
 # library. Note that this library cannot depend on base because base depends on
 # base_static.
diff --git a/base/android/java/src/org/chromium/base/PathUtils.java b/base/android/java/src/org/chromium/base/PathUtils.java
index 29d5154..b9c9326 100644
--- a/base/android/java/src/org/chromium/base/PathUtils.java
+++ b/base/android/java/src/org/chromium/base/PathUtils.java
@@ -108,8 +108,11 @@
             if (sCacheSubDirectory == null) {
                 paths[CACHE_DIRECTORY] = appContext.getCacheDir().getPath();
             } else {
-                paths[CACHE_DIRECTORY] =
-                        new File(appContext.getCacheDir(), sCacheSubDirectory).getPath();
+                File cacheDir = new File(appContext.getCacheDir(), sCacheSubDirectory);
+                assert cacheDir.mkdir();
+                paths[CACHE_DIRECTORY] = cacheDir.getPath();
+                // Set to rwx--S--- as the Android cache dir has a distinct gid and is setgid.
+                chmod(paths[CACHE_DIRECTORY], 02700);
             }
         }
         return paths;
diff --git a/base/android/java/src/org/chromium/base/UnownedUserDataKey.java b/base/android/java/src/org/chromium/base/UnownedUserDataKey.java
index 5a3fd76..6034907 100644
--- a/base/android/java/src/org/chromium/base/UnownedUserDataKey.java
+++ b/base/android/java/src/org/chromium/base/UnownedUserDataKey.java
@@ -9,10 +9,11 @@
 import androidx.annotation.VisibleForTesting;
 
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashSet;
+import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * UnownedUserDataKey is used in conjunction with a particular {@link UnownedUserData} as the key
@@ -60,14 +61,6 @@
  * @see UnownedUserData for the marker interface used for this type of data.
  */
 public final class UnownedUserDataKey<T extends UnownedUserData> {
-    private interface Predicate<T> {
-        boolean test(T t);
-    }
-
-    private interface Action<T> {
-        void execute(T t, WeakReference<T> weakT);
-    }
-
     @NonNull
     private final Class<T> mClazz;
     private final Set<WeakReference<UnownedUserDataHost>> mHostAttachments = new HashSet<>();
@@ -94,15 +87,14 @@
      * @param object The object to attach.
      */
     public final void attachToHost(@NonNull UnownedUserDataHost host, @NonNull T object) {
-        checkNotNull(host);
-        checkNotNull(object);
+        Objects.requireNonNull(object);
         // Setting a new value might lead to detachment of previously attached data, including
         // re-entry to this key, to happen before we update the {@link #mHostAttachments}.
         host.set(this, object);
 
-        AtomicReference<UnownedUserDataHost> retrievedHost = new AtomicReference<>();
-        executeHostAction(host::equals, (attachedHost, unused) -> retrievedHost.set(attachedHost));
-        if (retrievedHost.get() == null) mHostAttachments.add(new WeakReference<>(host));
+        if (!isAttachedToHost(host)) {
+            mHostAttachments.add(new WeakReference<>(host));
+        }
     }
 
     /**
@@ -116,12 +108,12 @@
      */
     @Nullable
     public final T retrieveDataFromHost(@NonNull UnownedUserDataHost host) {
-        checkNotNull(host);
-
-        AtomicReference<T> retrievedValue = new AtomicReference<>();
-        executeHostAction(
-                host::equals, (attachedHost, unused) -> retrievedValue.set(host.get(this)));
-        return retrievedValue.get();
+        for (UnownedUserDataHost attachedHost : getStrongRefs()) {
+            if (host.equals(attachedHost)) {
+                return host.get(this);
+            }
+        }
+        return null;
     }
 
     /**
@@ -131,11 +123,11 @@
      * @param host The host to detach from.
      */
     public final void detachFromHost(@NonNull UnownedUserDataHost host) {
-        checkNotNull(host);
-        executeHostAction(host::equals, (attachedHost, weakAttachedHost) -> {
-            attachedHost.remove(this);
-            mHostAttachments.remove(weakAttachedHost);
-        });
+        for (UnownedUserDataHost attachedHost : getStrongRefs()) {
+            if (host.equals(attachedHost)) {
+                removeHostAttachment(attachedHost);
+            }
+        }
     }
 
     /**
@@ -143,13 +135,11 @@
      * this key. It is OK to call this for already detached objects.
      */
     public final void detachFromAllHosts(@NonNull T object) {
-        checkNotNull(object);
-        executeHostAction((attachedHost)
-                                  -> object.equals(attachedHost.get(this)),
-                (attachedHost, weakAttachedHost) -> {
-                    attachedHost.remove(this);
-                    mHostAttachments.remove(weakAttachedHost);
-                });
+        for (UnownedUserDataHost attachedHost : getStrongRefs()) {
+            if (object.equals(attachedHost.get(this))) {
+                removeHostAttachment(attachedHost);
+            }
+        }
     }
 
     /**
@@ -159,7 +149,6 @@
      * @return true if currently attached, false otherwise.
      */
     public final boolean isAttachedToHost(@NonNull UnownedUserDataHost host) {
-        checkNotNull(host);
         T t = retrieveDataFromHost(host);
         return t != null;
     }
@@ -168,21 +157,35 @@
      * @return Whether the {@link UnownedUserData} is currently attached to any hosts with this key.
      */
     public final boolean isAttachedToAnyHost(@NonNull T object) {
-        checkNotNull(object);
         return getHostAttachmentCount(object) > 0;
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     /* package */ int getHostAttachmentCount(@NonNull T object) {
-        AtomicInteger count = new AtomicInteger(0);
-        executeHostAction((attachedHost)
-                                  -> object.equals(attachedHost.get(this)),
-                (attachedHost, unused) -> count.incrementAndGet());
-        return count.get();
+        int ret = 0;
+        for (UnownedUserDataHost attachedHost : getStrongRefs()) {
+            if (object.equals(attachedHost.get(this))) {
+                ret++;
+            }
+        }
+        return ret;
     }
 
-    private void executeHostAction(@NonNull Predicate<UnownedUserDataHost> predicate,
-            @NonNull Action<UnownedUserDataHost> action) {
+    private void removeHostAttachment(UnownedUserDataHost host) {
+        host.remove(this);
+        for (WeakReference<UnownedUserDataHost> hostWeakReference : mHostAttachments) {
+            if (host.equals(hostWeakReference.get())) {
+                // Modifying mHostAttachments while iterating over it is okay here because we
+                // break out of the loop right away.
+                mHostAttachments.remove(hostWeakReference);
+                return;
+            }
+        }
+    }
+
+    // TODO(https://crbug.com/1131047): Make a //base helper for this.
+    private Collection<UnownedUserDataHost> getStrongRefs() {
+        ArrayList<UnownedUserDataHost> ret = new ArrayList<>();
         Set<WeakReference<UnownedUserDataHost>> hosts = new HashSet<>(mHostAttachments);
         for (WeakReference<UnownedUserDataHost> hostWeakReference : hosts) {
             UnownedUserDataHost hostStrongReference = hostWeakReference.get();
@@ -191,23 +194,11 @@
                 continue;
             }
             if (hostStrongReference.isDestroyed()) {
-                throw new IllegalStateException("Host should have been removed already.");
+                assert false : "Host should have been removed already.";
+                throw new IllegalStateException();
             }
-            if (!predicate.test(hostStrongReference)) continue;
-
-            action.execute(hostStrongReference, hostWeakReference);
+            ret.add(hostStrongReference);
         }
-    }
-
-    private void checkNotNull(T unownedUserData) {
-        if (unownedUserData == null) {
-            throw new IllegalArgumentException("UnownedUserData can not be null.");
-        }
-    }
-
-    private static void checkNotNull(UnownedUserDataHost host) {
-        if (host == null) {
-            throw new IllegalArgumentException("UnownedUserDataHost can not be null.");
-        }
+        return ret;
     }
 }
diff --git a/base/android/junit/src/org/chromium/base/UnownedUserDataKeyTest.java b/base/android/junit/src/org/chromium/base/UnownedUserDataKeyTest.java
index e28ad815..0ff4642 100644
--- a/base/android/junit/src/org/chromium/base/UnownedUserDataKeyTest.java
+++ b/base/android/junit/src/org/chromium/base/UnownedUserDataKeyTest.java
@@ -843,14 +843,17 @@
 
     @Test
     public void testNullKeyOrDataShouldBeDisallowed() {
-        assertThrows(IllegalArgumentException.class, () -> Foo.KEY.attachToHost(null, null));
-        assertThrows(IllegalArgumentException.class, () -> Foo.KEY.attachToHost(mHost1, null));
-        assertThrows(IllegalArgumentException.class, () -> Foo.KEY.attachToHost(null, mFoo));
+        assertThrows(NullPointerException.class, () -> Foo.KEY.attachToHost(null, null));
+        assertThrows(NullPointerException.class, () -> Foo.KEY.attachToHost(mHost1, null));
+        assertThrows(NullPointerException.class, () -> Foo.KEY.attachToHost(null, mFoo));
 
-        assertThrows(IllegalArgumentException.class, () -> Foo.KEY.retrieveDataFromHost(null));
+        // Need a non-empty registry to avoid no-op.
+        Foo.KEY.attachToHost(mHost1, mFoo);
+        assertThrows(NullPointerException.class, () -> Foo.KEY.retrieveDataFromHost(null));
 
-        assertThrows(IllegalArgumentException.class, () -> Foo.KEY.detachFromHost(null));
-        assertThrows(IllegalArgumentException.class, () -> Foo.KEY.detachFromAllHosts(null));
+        assertThrows(NullPointerException.class, () -> Foo.KEY.detachFromHost(null));
+        assertThrows(NullPointerException.class, () -> Foo.KEY.detachFromAllHosts(null));
+        Foo.KEY.detachFromAllHosts(mFoo);
     }
 
     @Test
diff --git a/base/android/scoped_hardware_buffer_handle.cc b/base/android/scoped_hardware_buffer_handle.cc
index 315fba8a..294a2b1 100644
--- a/base/android/scoped_hardware_buffer_handle.cc
+++ b/base/android/scoped_hardware_buffer_handle.cc
@@ -58,8 +58,8 @@
 }
 
 AHardwareBuffer* ScopedHardwareBufferHandle::Take() {
-  AHardwareBuffer* buffer = nullptr;
-  std::swap(buffer, buffer_);
+  AHardwareBuffer* buffer = buffer_;
+  buffer_ = nullptr;
   return buffer;
 }
 
diff --git a/base/profiler/stack_sampling_profiler.cc b/base/profiler/stack_sampling_profiler.cc
index 5fb0c5c4..1c1e938e 100644
--- a/base/profiler/stack_sampling_profiler.cc
+++ b/base/profiler/stack_sampling_profiler.cc
@@ -18,6 +18,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/singleton.h"
+#include "base/profiler/profiler_buildflags.h"
 #include "base/profiler/stack_buffer.h"
 #include "base/profiler/stack_sampler.h"
 #include "base/profiler/unwinder.h"
diff --git a/base/profiler/stack_sampling_profiler_test_util.cc b/base/profiler/stack_sampling_profiler_test_util.cc
index 294dd65..20a4b879 100644
--- a/base/profiler/stack_sampling_profiler_test_util.cc
+++ b/base/profiler/stack_sampling_profiler_test_util.cc
@@ -11,6 +11,7 @@
 #include "base/compiler_specific.h"
 #include "base/location.h"
 #include "base/path_service.h"
+#include "base/profiler/profiler_buildflags.h"
 #include "base/profiler/stack_buffer.h"
 #include "base/profiler/stack_sampling_profiler.h"
 #include "base/profiler/unwinder.h"
diff --git a/base/profiler/stack_sampling_profiler_unittest.cc b/base/profiler/stack_sampling_profiler_unittest.cc
index 7eff870..7951dfe 100644
--- a/base/profiler/stack_sampling_profiler_unittest.cc
+++ b/base/profiler/stack_sampling_profiler_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/metrics_hashes.h"
+#include "base/profiler/profiler_buildflags.h"
 #include "base/profiler/sample_metadata.h"
 #include "base/profiler/stack_sampler.h"
 #include "base/profiler/stack_sampling_profiler.h"
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index 6d8ec86..1c386b0 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -677,30 +677,34 @@
 
 class Deps(object):
   def __init__(self, direct_deps_config_paths):
-    self.all_deps_config_paths = GetAllDepsConfigsInOrder(
+    self._all_deps_config_paths = GetAllDepsConfigsInOrder(
         direct_deps_config_paths)
-    self.direct_deps_configs = [
-        GetDepConfig(p) for p in direct_deps_config_paths]
-    self.all_deps_configs = [
-        GetDepConfig(p) for p in self.all_deps_config_paths]
-    self.direct_deps_config_paths = direct_deps_config_paths
+    self._direct_deps_configs = [
+        GetDepConfig(p) for p in direct_deps_config_paths
+    ]
+    self._all_deps_configs = [
+        GetDepConfig(p) for p in self._all_deps_config_paths
+    ]
+    self._direct_deps_config_paths = direct_deps_config_paths
 
-  def All(self, wanted_type):
-    return DepsOfType(wanted_type, self.all_deps_configs)
+  def All(self, wanted_type=None):
+    if wanted_type is None:
+      return self._all_deps_configs
+    return DepsOfType(wanted_type, self._all_deps_configs)
 
   def Direct(self, wanted_type=None):
     if wanted_type is None:
-      return self.direct_deps_configs
-    return DepsOfType(wanted_type, self.direct_deps_configs)
+      return self._direct_deps_configs
+    return DepsOfType(wanted_type, self._direct_deps_configs)
 
   def AllConfigPaths(self):
-    return self.all_deps_config_paths
+    return self._all_deps_config_paths
 
   def RemoveNonDirectDep(self, path):
-    if path in self.direct_deps_config_paths:
+    if path in self._direct_deps_config_paths:
       raise Exception('Cannot remove direct dep.')
-    self.all_deps_config_paths.remove(path)
-    self.all_deps_configs.remove(GetDepConfig(path))
+    self._all_deps_config_paths.remove(path)
+    self._all_deps_configs.remove(GetDepConfig(path))
 
   def GradlePrebuiltJarPaths(self):
     ret = []
@@ -962,6 +966,9 @@
   parser.add_option('--extra-classpath-jars',
       help='GYP-list of .jar files to include on the classpath when compiling, '
            'but not to include in the final binary.')
+  parser.add_option(
+      '--mergeable-android-manifests',
+      help='GN-list of AndroidManifest.xml to include in manifest merging.')
   parser.add_option('--gradle-treat-as-prebuilt', action='store_true',
       help='Whether this library should be treated as a prebuilt library by '
            'generate_gradle.py.')
@@ -1199,10 +1206,10 @@
       set(deps.AllConfigPaths() + processor_deps.AllConfigPaths() +
           list(static_library_dependent_configs_by_path)))
 
+  direct_deps = deps.Direct()
   system_library_deps = deps.Direct('system_java_library')
   direct_library_deps = deps.Direct('java_library')
-  direct_group_deps = deps.Direct('group')
-  all_group_deps = deps.All('group')
+  all_deps = deps.All()
   all_library_deps = deps.All('java_library')
   all_resources_deps = deps.All('android_resources')
 
@@ -1227,7 +1234,7 @@
           'path': options.build_config,
           'type': options.type,
           'gn_target': options.gn_target,
-          'deps_configs': deps.direct_deps_config_paths,
+          'deps_configs': [d['path'] for d in direct_deps],
           'chromium_code': not options.non_chromium_code,
       },
       # Info needed only by generate_gradle.py.
@@ -1470,12 +1477,21 @@
     config['deps_info']['dependency_zip_overlays'] = dependency_zip_overlays
     config['deps_info']['extra_package_names'] = extra_package_names
 
-  if options.type == 'group':
-    if options.extra_classpath_jars:
-      # These are .jars to add to javac classpath but not to runtime classpath.
-      extra_classpath_jars = build_utils.ParseGnList(
-          options.extra_classpath_jars)
-      deps_info['extra_classpath_jars'] = extra_classpath_jars
+  # These are .jars to add to javac classpath but not to runtime classpath.
+  extra_classpath_jars = build_utils.ParseGnList(options.extra_classpath_jars)
+  if extra_classpath_jars:
+    deps_info['extra_classpath_jars'] = extra_classpath_jars
+
+  mergeable_android_manifests = build_utils.ParseGnList(
+      options.mergeable_android_manifests)
+  if mergeable_android_manifests:
+    deps_info['mergeable_android_manifests'] = mergeable_android_manifests
+
+  extra_proguard_classpath_jars = []
+  proguard_configs = build_utils.ParseGnList(options.proguard_configs)
+  if proguard_configs:
+    # Make a copy of |proguard_configs| since it's mutated below.
+    deps_info['proguard_configs'] = list(proguard_configs)
 
   if options.type == 'dist_aar':
     # dist_aar combines all dependency R.txt files into one for the aar.
@@ -1518,11 +1534,11 @@
       javac_interface_classpath.add(
           base_module_build_config['deps_info']['interface_jar_path'])
 
-    for dep in direct_group_deps:
+    for dep in direct_deps:
       if 'extra_classpath_jars' in dep:
         javac_classpath.update(dep['extra_classpath_jars'])
         javac_interface_classpath.update(dep['extra_classpath_jars'])
-    for dep in all_group_deps:
+    for dep in all_deps:
       if 'extra_classpath_jars' in dep:
         javac_full_classpath.update(dep['extra_classpath_jars'])
         javac_full_interface_classpath.update(dep['extra_classpath_jars'])
@@ -1533,9 +1549,6 @@
     # These are jars specified by input_jars_paths that almost never change.
     # Just add them directly to all the classpaths.
     if options.extra_classpath_jars:
-      extra_classpath_jars = build_utils.ParseGnList(
-          options.extra_classpath_jars)
-      deps_info['extra_classpath_jars'] = extra_classpath_jars
       javac_classpath.update(extra_classpath_jars)
       javac_interface_classpath.update(extra_classpath_jars)
       javac_full_classpath.update(extra_classpath_jars)
@@ -1563,10 +1576,6 @@
     host_classpath.extend(c['host_jar_path'] for c in all_library_deps)
     deps_info['host_classpath'] = host_classpath
 
-  all_configs = build_utils.ParseGnList(options.proguard_configs)
-  deps_info['proguard_configs'] = list(all_configs)
-  extra_proguard_classpath_jars = []
-
   # We allow lint to be run on android_apk targets, so we collect lint
   # artifacts for them.
   # We allow lint to be run on android_app_bundle targets, so we need to
@@ -1664,7 +1673,7 @@
         base_config = GetDepConfig(dep_config['base_module_config'])
       extra_main_r_text_files.append(base_config['r_text_path'])
       static_lib_jar_paths[config_path] = base_config['device_jar_path']
-      all_configs.extend(dep_config['proguard_all_configs'])
+      proguard_configs.extend(dep_config['proguard_all_configs'])
       extra_proguard_classpath_jars.extend(
           dep_config['proguard_classpath_jars'])
       all_java_sources.extend(base_config['jni']['all_source'])
@@ -1712,16 +1721,12 @@
 
   if options.type in ('android_apk', 'dist_aar',
       'dist_jar', 'android_app_bundle_module', 'android_app_bundle'):
-    for c in all_library_deps:
-      all_configs.extend(p for p in c.get('proguard_configs', []))
-      extra_proguard_classpath_jars.extend(
-          p for p in c.get('extra_classpath_jars', []))
-    for c in all_group_deps:
-      extra_proguard_classpath_jars.extend(
-          p for p in c.get('extra_classpath_jars', []))
+    for c in all_deps:
+      proguard_configs.extend(c.get('proguard_configs', []))
+      extra_proguard_classpath_jars.extend(c.get('extra_classpath_jars', []))
     if options.type == 'android_app_bundle':
       for c in deps.Direct('android_app_bundle_module'):
-        all_configs.extend(p for p in c.get('proguard_configs', []))
+        proguard_configs.extend(p for p in c.get('proguard_configs', []))
     if options.type == 'android_app_bundle':
       for d in deps.Direct('android_app_bundle_module'):
         extra_proguard_classpath_jars.extend(
@@ -1769,7 +1774,8 @@
       assert options.proguard_enabled, ('proguard must be enabled for '
           'instrumentation apks if it\'s enabled for the tested apk.')
       # Mutating lists, so no need to explicitly re-assign to dict.
-      all_configs.extend(p for p in tested_apk_config['proguard_all_configs'])
+      proguard_configs.extend(
+          p for p in tested_apk_config['proguard_all_configs'])
       extra_proguard_classpath_jars.extend(
           p for p in tested_apk_config['proguard_classpath_jars'])
       tested_apk_config = GetDepConfig(options.tested_apk_config)
@@ -1810,7 +1816,7 @@
 
   if options.type in ('android_apk', 'dist_aar', 'dist_jar',
                       'android_app_bundle_module', 'android_app_bundle'):
-    deps_info['proguard_all_configs'] = sorted(set(all_configs))
+    deps_info['proguard_all_configs'] = sorted(set(proguard_configs))
     deps_info['proguard_classpath_jars'] = sorted(
         set(extra_proguard_classpath_jars))
 
@@ -1935,8 +1941,10 @@
         'uncompressed_locales_java_list'] = _CreateJavaLocaleListFromAssets(
             config['uncompressed_assets'], locale_paks)
 
-    config['extra_android_manifests'] = filter(None, (
-        d.get('android_manifest') for d in all_resources_deps))
+    config['extra_android_manifests'] = []
+    for c in all_deps:
+      config['extra_android_manifests'].extend(
+          c.get('mergeable_android_manifests', []))
 
     # Collect java resources
     java_resources_jars = [d['java_resources_jar'] for d in all_library_deps
@@ -1968,7 +1976,7 @@
   if is_java_target:
     jar_to_target = {}
     _AddJarMapping(jar_to_target, [deps_info])
-    _AddJarMapping(jar_to_target, deps.all_deps_configs)
+    _AddJarMapping(jar_to_target, all_deps)
     if base_module_build_config:
       _AddJarMapping(jar_to_target, [base_module_build_config['deps_info']])
     if options.tested_apk_config:
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 1003633..1c24bc8d 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -505,11 +505,6 @@
           rebase_path(invoker.bundled_srcjars, root_build_dir)
       args += [ "--bundled-srcjars=$_rebased_bundled_srcjars" ]
     }
-    if (defined(invoker.input_jars_paths)) {
-      _rebased_input_jars_paths =
-          rebase_path(invoker.input_jars_paths, root_build_dir)
-      args += [ "--extra-classpath-jars=$_rebased_input_jars_paths" ]
-    }
     if (defined(invoker.proguard_enabled) && invoker.proguard_enabled) {
       args += [ "--proguard-enabled" ]
     }
@@ -518,6 +513,18 @@
           rebase_path(invoker.proguard_mapping_path, root_build_dir)
       args += [ "--proguard-mapping-path=$_rebased_proguard_mapping_path" ]
     }
+    if (defined(invoker.input_jars_paths)) {
+      _rebased_input_jars_paths =
+          rebase_path(invoker.input_jars_paths, root_build_dir)
+      args += [ "--extra-classpath-jars=$_rebased_input_jars_paths" ]
+    }
+    if (defined(invoker.mergeable_android_manifests)) {
+      _rebased_mergeable_android_manifests =
+          rebase_path(invoker.mergeable_android_manifests, root_build_dir)
+      args += [
+        "--mergeable-android-manifests=$_rebased_mergeable_android_manifests",
+      ]
+    }
     if (defined(invoker.proguard_configs)) {
       _rebased_proguard_configs =
           rebase_path(invoker.proguard_configs, root_build_dir)
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index cd97b298..63e9802 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -979,6 +979,7 @@
                                "android_manifest",
                                "android_manifest_dep",
                                "custom_package",
+                               "mergeable_android_manifests",
                                "resource_overlay",
                                "recursive_resource_deps",
                              ])
@@ -989,6 +990,13 @@
       }
 
       possible_config_deps = _deps
+
+      # Always merge manifests from resources.
+      # * Might want to change this at some point for consistency and clarity,
+      #   but keeping for backwards-compatibility.
+      if (!defined(mergeable_android_manifests) && defined(android_manifest)) {
+        mergeable_android_manifests = [ android_manifest ]
+      }
     }
 
     prepare_resources(target_name) {
@@ -1121,9 +1129,14 @@
   #    }
   #  }
   template("java_group") {
+    _build_config_vars = [
+      "input_jars_paths",
+      "mergeable_android_manifests",
+      "proguard_configs",
+    ]
     forward_variables_from(invoker, [ "testonly" ])
     write_build_config("$target_name$build_config_target_suffix") {
-      forward_variables_from(invoker, [ "input_jars_paths" ])
+      forward_variables_from(invoker, _build_config_vars)
       type = "group"
       build_config = "$target_gen_dir/${invoker.target_name}.build_config"
       supports_android = true
@@ -1143,7 +1156,7 @@
       }
     }
     group(target_name) {
-      forward_variables_from(invoker, "*")
+      forward_variables_from(invoker, "*", _build_config_vars)
       if (!defined(deps)) {
         deps = []
       }
@@ -4219,10 +4232,7 @@
         args += [ "--ignore-resources" ]
       }
       inputs = [ invoker.aar_path ]
-      outputs = []
-      if (!_ignore_manifest) {
-        outputs += [ "${_output_path}/AndroidManifest.xml" ]
-      }
+      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
@@ -4315,6 +4325,17 @@
       not_needed(invoker, [ "strip_drawables" ])
     }
 
+    if (_ignore_manifest) {
+      # Having this available can be useful for DFMs that depend on AARs. It
+      # provides a way to have manifest entries go into the base split while
+      # the code goes into a DFM.
+      java_group("${target_name}__ignored_manifest") {
+        forward_variables_from(invoker, [ "testonly" ])
+        deps = [ ":$_unpack_target_name" ]
+        mergeable_android_manifests = [ "${_output_path}/AndroidManifest.xml" ]
+      }
+    }
+
     # Create the android_assets target for assets
     if (_use_scanned_assets) {
       _assets_target_name = "${target_name}__assets"
diff --git a/build/config/fuchsia/generate_runner_scripts.gni b/build/config/fuchsia/generate_runner_scripts.gni
index a7364124..861e3f3 100644
--- a/build/config/fuchsia/generate_runner_scripts.gni
+++ b/build/config/fuchsia/generate_runner_scripts.gni
@@ -16,6 +16,10 @@
   # generated installation scripts. If not specified, then no default directory
   # will be used.
   default_fuchsia_build_dir_for_installation = ""
+
+  # Architecture of the host tools included in Fuchsia test targets.
+  # Defaults to target_cpu.
+  fuchsia_override_host_tool_arch_for_isolated_testing = target_cpu
 }
 
 # Generates a script which deploys and optionally executes a package on a
@@ -90,6 +94,9 @@
       "//build/fuchsia/",
       "//build/util/lib/",
       "//third_party/fuchsia-sdk/sdk/.build-id/",
+      "//third_party/fuchsia-sdk/sdk/bin/fpave.sh",
+      "//third_party/fuchsia-sdk/sdk/bin/fuchsia-common.sh",
+      "//third_party/fuchsia-sdk/sdk/meta/manifest.json",
       "${boot_image_root}/qemu/qemu-kernel.kernel",
       "${boot_image_root}/qemu/storage-full.blk",
       "${boot_image_root}/qemu/zircon-a.zbi",
@@ -99,26 +106,19 @@
       data += [ "${aemu_root}/" ]
     }
 
-    # Ensure that the host tools required by runner scripts are in runtime deps.
-    # Note that these must be provided for the test host architecture, which
-    # currently always matches the |target_cpu|, rather than for the build host
-    # architecture.
     data += [
-      "//third_party/fuchsia-sdk/sdk/tools/${target_cpu}/device-finder",
-      "//third_party/fuchsia-sdk/sdk/tools/${target_cpu}/fvm",
-      "//third_party/fuchsia-sdk/sdk/tools/${target_cpu}/merkleroot",
-      "//third_party/fuchsia-sdk/sdk/tools/${target_cpu}/pm",
-      "//third_party/fuchsia-sdk/sdk/tools/${target_cpu}/symbolize",
-      "//third_party/fuchsia-sdk/sdk/tools/${target_cpu}/zbi",
+      "//third_party/fuchsia-sdk/sdk/tools/${fuchsia_override_host_tool_arch_for_isolated_testing}/device-finder",
+      "//third_party/fuchsia-sdk/sdk/tools/${fuchsia_override_host_tool_arch_for_isolated_testing}/fvm",
+      "//third_party/fuchsia-sdk/sdk/tools/${fuchsia_override_host_tool_arch_for_isolated_testing}/merkleroot",
+      "//third_party/fuchsia-sdk/sdk/tools/${fuchsia_override_host_tool_arch_for_isolated_testing}/pm",
+      "//third_party/fuchsia-sdk/sdk/tools/${fuchsia_override_host_tool_arch_for_isolated_testing}/symbolize",
+      "//third_party/fuchsia-sdk/sdk/tools/${fuchsia_override_host_tool_arch_for_isolated_testing}/zbi",
     ]
     if (target_cpu == "arm64") {
       data += [ "${qemu_arm64_root}/" ]
     } else {
       data += [
         "${qemu_root}/",
-        "//third_party/fuchsia-sdk/sdk/bin/fpave.sh",
-        "//third_party/fuchsia-sdk/sdk/bin/fuchsia-common.sh",
-        "//third_party/fuchsia-sdk/sdk/meta/manifest.json",
         "//third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer",
       ]
     }
diff --git a/build/lacros/test_runner.py b/build/lacros/test_runner.py
index 8d5a48db..a984d9d3 100755
--- a/build/lacros/test_runner.py
+++ b/build/lacros/test_runner.py
@@ -125,8 +125,10 @@
       # them to keep the directory clean.
       os.remove(p)
       continue
-
-    age = time.time() - os.path.getatime(os.path.join(p, 'chrome'))
+    chrome_path = os.path.join(p, 'chrome')
+    if not os.path.exists(chrome_path):
+      chrome_path = p
+    age = time.time() - os.path.getatime(chrome_path)
     if age > expiration_duration:
       logging.info(
           'Removing ash-chrome: "%s" as it hasn\'t been used in the '
diff --git a/cc/base/devtools_instrumentation.cc b/cc/base/devtools_instrumentation.cc
index b386c75..b65c154 100644
--- a/cc/base/devtools_instrumentation.cc
+++ b/cc/base/devtools_instrumentation.cc
@@ -43,6 +43,7 @@
 const char kNeedsBeginFrameChanged[] = "NeedsBeginFrameChanged";
 const char kActivateLayerTree[] = "ActivateLayerTree";
 const char kRequestMainThreadFrame[] = "RequestMainThreadFrame";
+const char kDroppedFrame[] = "DroppedFrame";
 const char kBeginMainThreadFrame[] = "BeginMainThreadFrame";
 const char kDrawFrame[] = "DrawFrame";
 const char kCompositeLayers[] = "CompositeLayers";
diff --git a/cc/base/devtools_instrumentation.h b/cc/base/devtools_instrumentation.h
index 8c2fd99..02f0254 100644
--- a/cc/base/devtools_instrumentation.h
+++ b/cc/base/devtools_instrumentation.h
@@ -38,6 +38,7 @@
 CC_BASE_EXPORT extern const char kNeedsBeginFrameChanged[];
 CC_BASE_EXPORT extern const char kActivateLayerTree[];
 CC_BASE_EXPORT extern const char kRequestMainThreadFrame[];
+CC_BASE_EXPORT extern const char kDroppedFrame[];
 CC_BASE_EXPORT extern const char kBeginMainThreadFrame[];
 CC_BASE_EXPORT extern const char kDrawFrame[];
 CC_BASE_EXPORT extern const char kCompositeLayers[];
@@ -192,6 +193,15 @@
       TRACE_EVENT_SCOPE_THREAD, internal::kLayerTreeId, layer_tree_host_id);
 }
 
+inline void CC_BASE_EXPORT
+DidDropSmoothnessFrame(int layer_tree_host_id,
+                       base::TimeTicks dropped_frame_timestamp) {
+  TRACE_EVENT_INSTANT_WITH_TIMESTAMP1(
+      internal::CategoryName::kTimelineFrame, internal::kDroppedFrame,
+      TRACE_EVENT_SCOPE_THREAD, dropped_frame_timestamp, internal::kLayerTreeId,
+      layer_tree_host_id);
+}
+
 inline std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
 BeginMainThreadFrameData(int frame_id) {
   std::unique_ptr<base::trace_event::TracedValue> value(
diff --git a/cc/base/math_util.h b/cc/base/math_util.h
index a5f30e02..1368c9c1 100644
--- a/cc/base/math_util.h
+++ b/cc/base/math_util.h
@@ -70,9 +70,9 @@
     // However, w may be close to 0 and we lose precision on our geometry
     // calculations if we allow scaling to extremely large values.
     return gfx::PointF(base::ClampToRange(x() * inv_w, -kInfiniteCoordinate,
-                                          (float)kInfiniteCoordinate),
+                                          float{kInfiniteCoordinate}),
                        base::ClampToRange(y() * inv_w, -kInfiniteCoordinate,
-                                          (float)kInfiniteCoordinate));
+                                          float{kInfiniteCoordinate}));
   }
 
   gfx::Point3F CartesianPoint3d() const {
@@ -86,11 +86,11 @@
     // However, w may be close to 0 and we lose precision on our geometry
     // calculations if we allow scaling to extremely large values.
     return gfx::Point3F(base::ClampToRange(x() * inv_w, -kInfiniteCoordinate,
-                                           (float)kInfiniteCoordinate),
+                                           float{kInfiniteCoordinate}),
                         base::ClampToRange(y() * inv_w, -kInfiniteCoordinate,
-                                           (float)kInfiniteCoordinate),
+                                           float{kInfiniteCoordinate}),
                         base::ClampToRange(z() * inv_w, -kInfiniteCoordinate,
-                                           (float)kInfiniteCoordinate));
+                                           float{kInfiniteCoordinate}));
   }
 
   SkScalar x() const { return vec[0]; }
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index 74cd4be5..320e9d6a 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -294,12 +294,14 @@
     const viz::BeginFrameArgs& args,
     LatencyUkmReporter* latency_ukm_reporter,
     bool should_report_metrics,
-    SmoothThread smooth_thread)
+    SmoothThread smooth_thread,
+    int layer_tree_host_id)
     : should_report_metrics_(should_report_metrics),
       args_(args),
       active_trackers_(active_trackers),
       latency_ukm_reporter_(latency_ukm_reporter),
-      smooth_thread_(smooth_thread) {}
+      smooth_thread_(smooth_thread),
+      layer_tree_host_id_(layer_tree_host_id) {}
 
 std::unique_ptr<CompositorFrameReporter>
 CompositorFrameReporter::CopyReporterAtBeginImplStage() {
@@ -311,7 +313,7 @@
   }
   auto new_reporter = std::make_unique<CompositorFrameReporter>(
       active_trackers_, args_, latency_ukm_reporter_, should_report_metrics_,
-      smooth_thread_);
+      smooth_thread_, layer_tree_host_id_);
   new_reporter->did_finish_impl_frame_ = did_finish_impl_frame_;
   new_reporter->impl_frame_finish_time_ = impl_frame_finish_time_;
   new_reporter->main_frame_abort_time_ = main_frame_abort_time_;
@@ -844,6 +846,11 @@
   if (stage_history_.empty())
     return;
 
+  if (IsDroppedFrameAffectingSmoothness()) {
+    devtools_instrumentation::DidDropSmoothnessFrame(layer_tree_host_id_,
+                                                     args_.frame_time);
+  }
+
   const auto trace_track = perfetto::Track(reinterpret_cast<uint64_t>(this));
   TRACE_EVENT_BEGIN(
       "cc,benchmark", "PipelineReporter", trace_track,
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h
index 56f9faa..d7abf295 100644
--- a/cc/metrics/compositor_frame_reporter.h
+++ b/cc/metrics/compositor_frame_reporter.h
@@ -14,6 +14,7 @@
 #include "base/optional.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/time.h"
+#include "cc/base/devtools_instrumentation.h"
 #include "cc/cc_export.h"
 #include "cc/metrics/begin_main_frame_metrics.h"
 #include "cc/metrics/event_metrics.h"
@@ -147,7 +148,8 @@
                           const viz::BeginFrameArgs& args,
                           LatencyUkmReporter* latency_ukm_reporter,
                           bool should_report_metrics,
-                          SmoothThread smooth_thread);
+                          SmoothThread smooth_thread,
+                          int layer_tree_host_id);
   ~CompositorFrameReporter();
 
   CompositorFrameReporter(const CompositorFrameReporter& reporter) = delete;
@@ -324,6 +326,7 @@
   bool has_partial_update_ = false;
 
   const SmoothThread smooth_thread_;
+  const int layer_tree_host_id_;
 
   // If this is a cloned pointer, then |cloned_from_| is a weak pointer to the
   // original reporter this was cloned from.
diff --git a/cc/metrics/compositor_frame_reporter_unittest.cc b/cc/metrics/compositor_frame_reporter_unittest.cc
index fcefecb..94a2c45 100644
--- a/cc/metrics/compositor_frame_reporter_unittest.cc
+++ b/cc/metrics/compositor_frame_reporter_unittest.cc
@@ -33,7 +33,8 @@
             viz::BeginFrameArgs(),
             nullptr,
             /*should_report_metrics=*/true,
-            CompositorFrameReporter::SmoothThread::kSmoothBoth)) {
+            CompositorFrameReporter::SmoothThread::kSmoothBoth,
+            /*layer_tree_host_id=*/1)) {
     pipeline_reporter_->set_tick_clock(&test_tick_clock_);
     AdvanceNowByMs(1);
   }
diff --git a/cc/metrics/compositor_frame_reporting_controller.cc b/cc/metrics/compositor_frame_reporting_controller.cc
index d412fa1..507239e988 100644
--- a/cc/metrics/compositor_frame_reporting_controller.cc
+++ b/cc/metrics/compositor_frame_reporting_controller.cc
@@ -20,8 +20,10 @@
 }  // namespace
 
 CompositorFrameReportingController::CompositorFrameReportingController(
-    bool should_report_metrics)
+    bool should_report_metrics,
+    int layer_tree_host_id)
     : should_report_metrics_(should_report_metrics),
+      layer_tree_host_id_(layer_tree_host_id),
       latency_ukm_reporter_(std::make_unique<LatencyUkmReporter>()) {}
 
 CompositorFrameReportingController::~CompositorFrameReportingController() {
@@ -71,7 +73,7 @@
   }
   auto reporter = std::make_unique<CompositorFrameReporter>(
       active_trackers_, args, latency_ukm_reporter_.get(),
-      should_report_metrics_, GetSmoothThread());
+      should_report_metrics_, GetSmoothThread(), layer_tree_host_id_);
   reporter->set_tick_clock(tick_clock_);
   reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame,
                        begin_time);
@@ -98,7 +100,7 @@
     // deadline yet). So will start a new reporter at BeginMainFrame.
     auto reporter = std::make_unique<CompositorFrameReporter>(
         active_trackers_, args, latency_ukm_reporter_.get(),
-        should_report_metrics_, GetSmoothThread());
+        should_report_metrics_, GetSmoothThread(), layer_tree_host_id_);
     reporter->set_tick_clock(tick_clock_);
     reporter->StartStage(StageType::kSendBeginMainFrameToCommit, Now());
     reporter->SetDroppedFrameCounter(dropped_frame_counter_);
diff --git a/cc/metrics/compositor_frame_reporting_controller.h b/cc/metrics/compositor_frame_reporting_controller.h
index 714c60f..aa24c8f 100644
--- a/cc/metrics/compositor_frame_reporting_controller.h
+++ b/cc/metrics/compositor_frame_reporting_controller.h
@@ -41,7 +41,8 @@
     kNumPipelineStages
   };
 
-  explicit CompositorFrameReportingController(bool should_report_metrics);
+  CompositorFrameReportingController(bool should_report_metrics,
+                                     int layer_tree_host_id);
   virtual ~CompositorFrameReportingController();
 
   CompositorFrameReportingController(
@@ -119,6 +120,8 @@
   CompositorFrameReporter::SmoothThread GetSmoothThread() const;
 
   const bool should_report_metrics_;
+  const int layer_tree_host_id_;
+
   viz::BeginFrameId last_submitted_frame_id_;
 
   bool next_activate_has_invalidation_ = false;
diff --git a/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
index 221133f..807f839 100644
--- a/cc/metrics/compositor_frame_reporting_controller_unittest.cc
+++ b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
@@ -30,7 +30,8 @@
     : public CompositorFrameReportingController {
  public:
   TestCompositorFrameReportingController()
-      : CompositorFrameReportingController(/*should_report_metrics=*/true) {}
+      : CompositorFrameReportingController(/*should_report_metrics=*/true,
+                                           /*layer_tree_host_id=*/1) {}
 
   TestCompositorFrameReportingController(
       const TestCompositorFrameReportingController& controller) = delete;
diff --git a/cc/metrics/frame_sequence_tracker_unittest.cc b/cc/metrics/frame_sequence_tracker_unittest.cc
index 6567296..4f03150 100644
--- a/cc/metrics/frame_sequence_tracker_unittest.cc
+++ b/cc/metrics/frame_sequence_tracker_unittest.cc
@@ -45,7 +45,8 @@
   FrameSequenceTrackerTest()
       : compositor_frame_reporting_controller_(
             std::make_unique<CompositorFrameReportingController>(
-                /*should_report_metrics=*/true)),
+                /*should_report_metrics=*/true,
+                /*layer_tree_host_id=*/1)),
         collection_(/*is_single_threaded=*/false,
                     compositor_frame_reporting_controller_.get()) {
     tracker_ = collection_.StartScrollSequence(
diff --git a/cc/paint/discardable_image_map_unittest.cc b/cc/paint/discardable_image_map_unittest.cc
index 2766302..a334098 100644
--- a/cc/paint/discardable_image_map_unittest.cc
+++ b/cc/paint/discardable_image_map_unittest.cc
@@ -875,8 +875,7 @@
   EXPECT_EQ(SkSize::Make(4.f, 4.f), draw_images[0].scale);
 }
 
-// Temporarily disabled for a Skia roll (https://crbug.com/skia/10744).
-TEST_F(DiscardableImageMapTest, DISABLED_CapturesImagesInPaintFilters) {
+TEST_F(DiscardableImageMapTest, CapturesImagesInPaintFilters) {
   // Create the record to use in the filter.
   auto filter_record = sk_make_sp<PaintOpBuffer>();
 
@@ -894,7 +893,7 @@
   display_list->StartPaint();
   PaintFlags flags;
   flags.setImageFilter(sk_make_sp<RecordPaintFilter>(
-      filter_record, SkRect::MakeWH(100.f, 100.f)));
+      filter_record, SkRect::MakeWH(150.f, 150.f)));
   display_list->push<DrawRectOp>(SkRect::MakeWH(200, 200), flags);
   display_list->EndPaintOfUnpaired(visible_rect);
   display_list->Finalize();
@@ -914,8 +913,9 @@
   ASSERT_EQ(draw_images.size(), 1u);
   EXPECT_EQ(draw_images[0].image, animated_image);
   // The position of the image is the position of the DrawRectOp that uses the
-  // filter.
-  EXPECT_EQ(gfx::Rect(200, 200), inset_rects[0]);
+  // filter. Since the bounds of the filter does not depend on the source/input,
+  // the resulting bounds is that of the RecordPaintFilter.
+  EXPECT_EQ(gfx::Rect(150, 150), inset_rects[0]);
   // Images in a filter are decoded at the original size.
   EXPECT_EQ(SkSize::Make(1.f, 1.f), draw_images[0].scale);
 }
diff --git a/cc/test/fake_compositor_frame_reporting_controller.cc b/cc/test/fake_compositor_frame_reporting_controller.cc
index 40e4627..d63aee5 100644
--- a/cc/test/fake_compositor_frame_reporting_controller.cc
+++ b/cc/test/fake_compositor_frame_reporting_controller.cc
@@ -13,7 +13,8 @@
 base::TimeDelta INTERVAL = base::TimeDelta::FromMilliseconds(16);
 
 FakeCompositorFrameReportingController::FakeCompositorFrameReportingController()
-    : CompositorFrameReportingController(/*should_report_metrics=*/true) {}
+    : CompositorFrameReportingController(/*should_report_metrics=*/true,
+                                         /*layer_tree_host_id=*/1) {}
 
 void FakeCompositorFrameReportingController::WillBeginMainFrame(
     const viz::BeginFrameArgs& args) {
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 71e34d3..e6f7ae9b 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -389,8 +389,8 @@
       current_begin_frame_tracker_(FROM_HERE),
       compositor_frame_reporting_controller_(
           std::make_unique<CompositorFrameReportingController>(
-              /*should_report_metrics=*/!settings
-                  .single_thread_proxy_scheduler)),
+              /*should_report_metrics=*/!settings.single_thread_proxy_scheduler,
+              id)),
       settings_(settings),
       is_synchronous_single_threaded_(!task_runner_provider->HasImplThread() &&
                                       !settings_.single_thread_proxy_scheduler),
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index d629218f..bfaf61fe 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/chrome_build.gni")
+import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/compiler/compiler.gni")
 import("//build/config/features.gni")
 import("//build/config/locales.gni")
@@ -164,23 +165,28 @@
         "//chrome/app/version_assembly:chrome_exe_manifest",
         "//chrome/browser:active_use_util",
         "//chrome/browser:chrome_process_finder",
+        "//chrome/browser/policy:path_parser",
         "//chrome/chrome_elf",
         "//chrome/common:constants",
+        "//chrome/install_static:install_static_util",
         "//chrome/install_static:secondary_module",
         "//chrome/installer/util:constants",
         "//chrome/installer/util:did_run_support",
         "//components/browser_watcher:browser_watcher_client",
+        "//components/crash/core/app",
         "//components/crash/core/app:run_as_crashpad_handler",
         "//components/crash/core/common",
         "//components/flags_ui:switches",
         "//content:sandbox_helper_win",
         "//content/public/common:static_switches",
         "//crypto",
+        "//gpu/command_buffer/service",
         "//sandbox",
         "//sandbox/policy",
         "//third_party/breakpad:breakpad_handler",
         "//third_party/breakpad:breakpad_sender",
         "//third_party/crashpad/crashpad/util",
+        "//ui/gl",
       ]
       data_deps = [
         "//chrome/app/version_assembly:version_assembly_manifest",
@@ -338,6 +344,7 @@
       "//headless:headless_shell_browser_lib",
       "//net:net_resources",
       "//ppapi/buildflags",
+      "//sandbox/win:sandbox",
       "//third_party/cld_3/src/src:cld_3",
       "//third_party/wtl",
       "//ui/views",
@@ -938,7 +945,6 @@
     deps = [
       ":browser_dependencies",
       ":child_dependencies",
-      "//build:chromeos_buildflags",
       "//chrome/app:command_ids",
       "//chrome/common:buildflags",
       "//chrome/common/profiler",
@@ -1168,11 +1174,18 @@
 
 group("browser_dependencies") {
   public_deps = [
+    "//build:branding_buildflags",
+    "//build:chromeos_buildflags",
+    "//chrome/app:shutdown_signal_handlers",
     "//chrome/browser",
+    "//chrome/browser/policy:path_parser",
     "//chrome/common",
+    "//components/crash/core/app",
     "//components/gwp_asan/buildflags",
     "//components/heap_profiling/in_process",
+    "//components/startup_metric_utils/browser",
     "//components/sync",
+    "//components/upload_list:upload_list",
     "//services/tracing/public/cpp",
   ]
   if (enable_plugins) {
@@ -1190,6 +1203,23 @@
     public_deps += [ "//components/gwp_asan/client" ]
   }
 
+  if (enable_nacl) {
+    public_deps += [ "//components/nacl/browser" ]
+  }
+
+  if (chromeos_is_browser_only) {
+    public_deps += [ "//chromeos/lacros" ]
+  }
+
+  if (is_chromeos) {
+    public_deps += [
+      "//chrome/browser/chromeos",
+      "//chromeos",
+      "//chromeos/constants",
+      "//chromeos/memory",
+    ]
+  }
+
   if (!is_component_build) {
     assert_no_deps = [
       # Blink should not be used in the browser process. In component build this
@@ -1227,6 +1257,7 @@
     "//services/tracing/public/cpp",
     "//third_party/blink/public:blink_devtools_frontend_resources",
     "//third_party/blink/public:blink_devtools_inspector_resources",
+    "//v8:v8_headers",
   ]
 
   if (enable_nacl) {
@@ -1499,6 +1530,8 @@
     ]
 
     deps = [
+      ":browser_dependencies",
+      ":child_dependencies",
       "//chrome/browser/ui",
       "//chrome/child",
       "//chrome/common",
@@ -1509,6 +1542,7 @@
       "//components/heap_profiling/in_process",
       "//components/minidump_uploader",
       "//components/safe_browsing:buildflags",
+      "//components/safe_browsing/android:safe_browsing_api_handler",
       "//components/safe_browsing/android:safe_browsing_mobile",
       "//components/services/heap_profiling",
       "//content/public/app",
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 115a415..5711960 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -2975,11 +2975,11 @@
     "java/src/org/chromium/chrome/browser/ApplicationLifetime.java",
     "java/src/org/chromium/chrome/browser/ChromeBackupAgent.java",
     "java/src/org/chromium/chrome/browser/ChromeBackupWatcher.java",
-    "java/src/org/chromium/chrome/browser/ChromeVersionInfo.java",
     "java/src/org/chromium/chrome/browser/DevToolsServer.java",
     "java/src/org/chromium/chrome/browser/IntentHandler.java",
     "java/src/org/chromium/chrome/browser/IntentHelper.java",
     "java/src/org/chromium/chrome/browser/NearOomMonitor.java",
+    "java/src/org/chromium/chrome/browser/PlayServicesVersionInfo.java",
     "java/src/org/chromium/chrome/browser/SearchGeolocationDisclosureTabHelper.java",
     "java/src/org/chromium/chrome/browser/ServiceTabLauncher.java",
     "java/src/org/chromium/chrome/browser/ShortcutHelper.java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index a1d16f9..8cf09a2 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -43,6 +43,7 @@
   "java/src/org/chromium/chrome/browser/LauncherShortcutActivity.java",
   "java/src/org/chromium/chrome/browser/NavigationPopup.java",
   "java/src/org/chromium/chrome/browser/NearOomMonitor.java",
+  "java/src/org/chromium/chrome/browser/PlayServicesVersionInfo.java",
   "java/src/org/chromium/chrome/browser/PowerBroadcastReceiver.java",
   "java/src/org/chromium/chrome/browser/SearchGeolocationDisclosureTabHelper.java",
   "java/src/org/chromium/chrome/browser/ServiceTabLauncher.java",
@@ -1144,9 +1145,11 @@
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsRecyclerView.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsRecyclerViewAdapter.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionCommonProperties.java",
+  "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHost.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListProperties.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionProcessor.java",
+  "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewDelegate.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionsMetrics.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/UrlBarDelegate.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/VoiceSuggestionProvider.java",
@@ -1164,8 +1167,6 @@
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SuggestionDrawableState.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SuggestionSpannable.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java",
-  "java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionHost.java",
-  "java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewDelegate.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewViewBinder.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessor.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 01b8822c..e94f816 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -539,6 +539,7 @@
   "javatests/src/org/chromium/chrome/browser/sync/OpenTabsTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesSettingsTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesSettingsWithFakeProfileSyncServiceTest.java",
+  "javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/SyncTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java",
   "javatests/src/org/chromium/chrome/browser/sync/TypedUrlsTest.java",
diff --git a/chrome/android/java/res/layout/account_picker_bottom_sheet_view.xml b/chrome/android/java/res/layout/account_picker_bottom_sheet_view.xml
index c17a84d..731513ce 100644
--- a/chrome/android/java/res/layout/account_picker_bottom_sheet_view.xml
+++ b/chrome/android/java/res/layout/account_picker_bottom_sheet_view.xml
@@ -37,7 +37,7 @@
         android:textAppearance="@style/TextAppearance.TextLarge.Primary"
         android:text="@string/signin_account_picker_dialog_title" />
 
-    <TextView
+    <org.chromium.ui.widget.TextViewWithLeading
         android:id="@+id/account_picker_bottom_sheet_subtitle"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -46,7 +46,8 @@
         android:layout_marginBottom="16dp"
         android:gravity="center_horizontal"
         android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
-        android:text="@string/signin_account_picker_bottom_sheet_subtitle" />
+        android:text="@string/signin_account_picker_bottom_sheet_subtitle"
+        app:leading="@dimen/text_size_medium_leading" />
 
     <View
         android:id="@+id/account_picker_horizontal_divider"
diff --git a/chrome/android/java/res/layout/incognito_interstitial_bottom_sheet_view.xml b/chrome/android/java/res/layout/incognito_interstitial_bottom_sheet_view.xml
index b10e059..0ca7c56 100644
--- a/chrome/android/java/res/layout/incognito_interstitial_bottom_sheet_view.xml
+++ b/chrome/android/java/res/layout/incognito_interstitial_bottom_sheet_view.xml
@@ -12,13 +12,14 @@
     android:paddingEnd="24dp"
     android:orientation="vertical">
 
-    <TextView
+    <org.chromium.ui.widget.TextViewWithLeading
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginBottom="16dp"
         android:gravity="center_vertical"
         android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
-        android:text="@string/incognito_interstitial_message" />
+        android:text="@string/incognito_interstitial_message"
+        app:leading="@dimen/text_size_medium_leading" />
 
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeVersionInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeVersionInfo.java
index c4fc232..8960f25 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeVersionInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeVersionInfo.java
@@ -4,18 +4,8 @@
 
 package org.chromium.chrome.browser;
 
-import android.content.Context;
-import android.content.pm.PackageManager;
-
-import com.google.android.gms.common.GoogleApiAvailability;
-
-import org.chromium.base.ContextUtils;
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
 import org.chromium.components.version_info.Channel;
 
-import java.util.Locale;
-
 /**
  * A utility class for querying information about the current Chrome build.
  * Intentionally doesn't depend on native so that the data can be accessed before
@@ -84,47 +74,4 @@
     public static int getBuildVersion() {
         return ChromeVersionConstants.PRODUCT_BUILD_VERSION;
     }
-
-    /**
-     * Returns info about the Google Play services setup for Chrome and the device.
-     *
-     * Contains the version number of the SDK Chrome was built with and the one for the installed
-     * Play Services app. It also contains whether First Party APIs are available.
-     */
-    @CalledByNative
-    public static String getGmsInfo() {
-        Context context = ContextUtils.getApplicationContext();
-
-        final long sdkVersion = GoogleApiAvailability.GOOGLE_PLAY_SERVICES_VERSION_CODE;
-        final long installedGmsVersion = getPlayServicesApkVersionNumber(context);
-
-        final String accessType;
-        ExternalAuthUtils externalAuthUtils = AppHooks.get().getExternalAuthUtils();
-        if (externalAuthUtils.canUseFirstPartyGooglePlayServices()) {
-            accessType = "1p";
-        } else if (externalAuthUtils.canUseGooglePlayServices()) {
-            accessType = "3p";
-        } else {
-            accessType = "none";
-        }
-
-        return String.format(Locale.US,
-                "SDK=%s; Installed=%s; Access=%s", sdkVersion, installedGmsVersion, accessType);
-    }
-
-    /**
-     *
-     * @param context A Context with which to retrieve the PackageManager.
-     * @return The version code for the Google Play Services installed on the device or 0 if the
-     *         package is not found.
-     */
-    public static int getPlayServicesApkVersionNumber(Context context) {
-        try {
-            return context.getPackageManager()
-                    .getPackageInfo(GoogleApiAvailability.GOOGLE_PLAY_SERVICES_PACKAGE, 0)
-                    .versionCode;
-        } catch (PackageManager.NameNotFoundException e) {
-            return 0;
-        }
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/PlayServicesVersionInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/PlayServicesVersionInfo.java
new file mode 100644
index 0000000..b0ba76f
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/PlayServicesVersionInfo.java
@@ -0,0 +1,64 @@
+// Copyright 2020 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;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import com.google.android.gms.common.GoogleApiAvailability;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
+
+import java.util.Locale;
+
+/**
+ * A utility class for querying information about Play Services Version.
+ */
+public class PlayServicesVersionInfo {
+    /**
+     * Returns info about the Google Play services setup for Chrome and the device.
+     *
+     * Contains the version number of the SDK Chrome was built with and the one for the installed
+     * Play Services app. It also contains whether First Party APIs are available.
+     */
+    @CalledByNative
+    public static String getGmsInfo() {
+        Context context = ContextUtils.getApplicationContext();
+
+        final long sdkVersion = GoogleApiAvailability.GOOGLE_PLAY_SERVICES_VERSION_CODE;
+        final long installedGmsVersion = getApkVersionNumber(context);
+
+        final String accessType;
+        ExternalAuthUtils externalAuthUtils = AppHooks.get().getExternalAuthUtils();
+        if (externalAuthUtils.canUseFirstPartyGooglePlayServices()) {
+            accessType = "1p";
+        } else if (externalAuthUtils.canUseGooglePlayServices()) {
+            accessType = "3p";
+        } else {
+            accessType = "none";
+        }
+
+        return String.format(Locale.US, "SDK=%s; Installed=%s; Access=%s", sdkVersion,
+                installedGmsVersion, accessType);
+    }
+
+    /**
+     *
+     * @param context A Context with which to retrieve the PackageManager.
+     * @return The version code for the Google Play Services installed on the device or 0 if the
+     *         package is not found.
+     */
+    public static int getApkVersionNumber(Context context) {
+        try {
+            return context.getPackageManager()
+                    .getPackageInfo(GoogleApiAvailability.GOOGLE_PLAY_SERVICES_PACKAGE, 0)
+                    .versionCode;
+        } catch (PackageManager.NameNotFoundException e) {
+            return 0;
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index 18442d1..a7c8ecf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -56,12 +56,12 @@
 import org.chromium.chrome.browser.AppHooks;
 import org.chromium.chrome.browser.ChromeActivitySessionTracker;
 import org.chromium.chrome.browser.ChromeApplication;
-import org.chromium.chrome.browser.ChromeVersionInfo;
 import org.chromium.chrome.browser.ChromeWindow;
 import org.chromium.chrome.browser.DeferredStartupHandler;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.IntentHandler.IntentHandlerDelegate;
 import org.chromium.chrome.browser.IntentHandler.TabOpenType;
+import org.chromium.chrome.browser.PlayServicesVersionInfo;
 import org.chromium.chrome.browser.TabbedModeTabDelegateFactory;
 import org.chromium.chrome.browser.WarmupManager;
 import org.chromium.chrome.browser.accessibility.FontSizePrefs;
@@ -1081,7 +1081,7 @@
             }
 
             recordDisplayDimensions();
-            int playServicesVersion = ChromeVersionInfo.getPlayServicesApkVersionNumber(this);
+            int playServicesVersion = PlayServicesVersionInfo.getApkVersionNumber(this);
             RecordHistogram.recordBooleanHistogram(
                     "Android.PlayServices.Installed", playServicesVersion > 0);
             RecordHistogram.recordSparseHistogram(
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 561e8315..c5f0113 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
@@ -42,8 +42,6 @@
 import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
 import org.chromium.chrome.browser.omnibox.styles.OmniboxTheme;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteController.OnSuggestionsReceivedListener;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.query_tiles.QueryTileUtils;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
index efe9b33..5623cc5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
@@ -23,7 +23,6 @@
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteResult.GroupDetails;
 import org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.BasicSuggestionProcessor;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.clipboard.ClipboardSuggestionProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.editurl.EditUrlSuggestionProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.entity.EntitySuggestionProcessor;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionHost.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHost.java
similarity index 88%
rename from chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionHost.java
rename to chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHost.java
index 9accacde..3bc4d8d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionHost.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHost.java
@@ -2,10 +2,8 @@
 // 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.suggestions.basic;
+package org.chromium.chrome.browser.omnibox.suggestions;
 
-import org.chromium.chrome.browser.omnibox.suggestions.DropdownItemProcessor;
-import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** A mechanism for creating {@link SuggestionViewDelegate}s. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewDelegate.java
similarity index 93%
rename from chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewDelegate.java
rename to chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewDelegate.java
index 5814d52..11a40c4c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewDelegate.java
@@ -2,8 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.omnibox.suggestions.basic;
-
+package org.chromium.chrome.browser.omnibox.suggestions;
 
 /**
  * Handler for actions that happen on suggestion view.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
index fcb3402e..f5ec997 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
@@ -17,9 +17,9 @@
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
 import org.chromium.components.omnibox.AnswerType;
 import org.chromium.components.omnibox.SuggestionAnswer;
 import org.chromium.ui.modelutil.PropertyModel;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
index 375026d4..9a516dc0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
@@ -16,7 +16,7 @@
 import androidx.appcompat.widget.AppCompatImageView;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionViewDelegate;
 import org.chromium.chrome.browser.util.KeyNavigationUtil;
 import org.chromium.components.browser_ui.widget.RoundedCornerImageView;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java
index 57388245..676ef829f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java
@@ -18,10 +18,10 @@
 import org.chromium.chrome.browser.omnibox.MatchClassificationStyle;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion.MatchClassification;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionProcessor;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionViewDelegate;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties.Action;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
 import org.chromium.chrome.browser.ui.favicon.LargeIconBridge;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.url.GURL;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProperties.java
index d4ee425..c0d3f7e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProperties.java
@@ -10,7 +10,7 @@
 import androidx.annotation.StringRes;
 
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionViewDelegate;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
index 5c57be1..f5c9a92c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
@@ -18,6 +18,7 @@
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionSpannable;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessor.java
index 73a00e4e..08230c3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessor.java
@@ -15,10 +15,10 @@
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionSpannable;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties;
 import org.chromium.chrome.browser.ui.favicon.LargeIconBridge;
 import org.chromium.ui.modelutil.PropertyModel;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java
index 4292f3c..6313a4d6fd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java
@@ -14,12 +14,12 @@
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.UrlBarDelegate;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties.Action;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionSpannable;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.share.ShareDelegate;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessor.java
index 93d5f8b..3194595 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessor.java
@@ -22,9 +22,9 @@
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
 import org.chromium.components.browser_ui.util.ConversionUtils;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.url.GURL;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderProcessor.java
index 41759fc00..1259577 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderProcessor.java
@@ -10,8 +10,8 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.suggestions.DropdownItemProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.UrlBarDelegate;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** A class that handles model and view creation for the suggestion headers. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/TailSuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/TailSuggestionProcessor.java
index 3b5d5cc..ce67170 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/TailSuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/TailSuggestionProcessor.java
@@ -10,10 +10,10 @@
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionSpannable;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.modelutil.PropertyModel;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java
index d0cdb72..7eab8c4e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java
@@ -38,9 +38,9 @@
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionBuilderForTest;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
 import org.chromium.components.omnibox.AnswerTextStyle;
 import org.chromium.components.omnibox.AnswerTextType;
 import org.chromium.components.omnibox.AnswerType;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionProcessorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionProcessorTest.java
index e3d89b9..179a7c5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionProcessorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionProcessorTest.java
@@ -30,7 +30,7 @@
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionBuilderForTest;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.ui.favicon.LargeIconBridge;
 import org.chromium.chrome.browser.ui.favicon.LargeIconBridge.LargeIconCallback;
 import org.chromium.ui.modelutil.PropertyModel;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorUnitTest.java
index 9c8f14f..d398056 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorUnitTest.java
@@ -36,6 +36,7 @@
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionBuilderForTest;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties.SuggestionIcon;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessorTest.java
index 46c0323..5fc9daa1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessorTest.java
@@ -35,9 +35,9 @@
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionBuilderForTest;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewViewBinder;
 import org.chromium.chrome.browser.ui.favicon.LargeIconBridge;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionUnitTest.java
index c27b059..0b1e045 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionUnitTest.java
@@ -33,10 +33,10 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.UrlBarDelegate;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties.Action;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.share.ShareDelegate;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessorUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessorUnitTest.java
index 58bf47d..7333d8a3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessorUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessorUnitTest.java
@@ -40,9 +40,9 @@
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionBuilderForTest;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
 import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.url.GURL;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
index a2e46c37..a2160ed8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
@@ -108,6 +108,7 @@
 public class SearchActivityTest {
     private static final long OMNIBOX_SHOW_TIMEOUT_MS = 5000L;
     private static final String TEST_PNG_IMAGE_FILE_EXTENSION = ".png";
+    private static final int INVALID_INDEX = -1;
 
     @ParameterAnnotations.ClassParameter
     private static List<ParameterSet> sClassParams =
@@ -571,7 +572,7 @@
                 AutocompleteCoordinatorTestUtils.getSuggestionsDropdown(
                         locationBar.getAutocompleteCoordinator());
 
-        int imageSuggestionIndex = -1;
+        int imageSuggestionIndex = INVALID_INDEX;
         // Find the index of the image clipboard suggestion.
         for (int i = 0; i < suggestionsDropdown.getItemCount(); ++i) {
             OmniboxSuggestion suggestion = AutocompleteCoordinatorTestUtils.getOmniboxSuggestionAt(
@@ -582,8 +583,8 @@
                 break;
             }
         }
-        Assert.assertNotEquals(
-                "Cannot find the image clipboard Omnibox suggestion", -1, imageSuggestionIndex);
+        Assert.assertNotEquals("Cannot find the image clipboard Omnibox suggestion", INVALID_INDEX,
+                imageSuggestionIndex);
 
         OmniboxSuggestion imageSuggestion = AutocompleteCoordinatorTestUtils.getOmniboxSuggestionAt(
                 locationBar.getAutocompleteCoordinator(), imageSuggestionIndex);
@@ -598,13 +599,6 @@
                 "The image clipboard suggestion should not contains am empty post data.", 0,
                 imageSuggestion.getPostData().length);
 
-        // Find the index of clipboard suggestion in the dropdown list.
-        final int clipboardSuggestionIndexInDropdown =
-                AutocompleteCoordinatorTestUtils.getIndexForFirstSuggestionOfType(
-                        locationBar.getAutocompleteCoordinator(),
-                        OmniboxSuggestionUiType.CLIPBOARD_SUGGESTION);
-        Assert.assertNotEquals("Cannot find the image clipboard Omnibox suggestion in UI.", -1,
-                clipboardSuggestionIndexInDropdown);
 
         // Make sure the new tab is launched.
         final ChromeTabbedActivity cta = ActivityUtils.waitForActivity(
@@ -612,7 +606,7 @@
                 new Callable<Void>() {
                     @Override
                     public Void call() throws InterruptedException {
-                        clickSuggestionAt(suggestionsDropdown, clipboardSuggestionIndexInDropdown);
+                        clickFirstClipboardSuggestion(locationBar);
                         return null;
                     }
                 });
@@ -652,7 +646,7 @@
                 AutocompleteCoordinatorTestUtils.getSuggestionsDropdown(
                         locationBar.getAutocompleteCoordinator());
 
-        int imageSuggestionIndex = -1;
+        int imageSuggestionIndex = INVALID_INDEX;
         // Find the index of the image clipboard suggestion.
         for (int i = 0; i < suggestionsDropdown.getItemCount(); ++i) {
             OmniboxSuggestion suggestion = AutocompleteCoordinatorTestUtils.getOmniboxSuggestionAt(
@@ -663,8 +657,8 @@
                 break;
             }
         }
-        Assert.assertNotEquals(
-                "Cannot find the image clipboard Omnibox suggestion", -1, imageSuggestionIndex);
+        Assert.assertNotEquals("Cannot find the image clipboard Omnibox suggestion", INVALID_INDEX,
+                imageSuggestionIndex);
 
         OmniboxSuggestion imageSuggestion = AutocompleteCoordinatorTestUtils.getOmniboxSuggestionAt(
                 locationBar.getAutocompleteCoordinator(), imageSuggestionIndex);
@@ -712,13 +706,27 @@
         });
     }
 
-    private void clickSuggestionAt(OmniboxSuggestionsDropdown suggestionsDropdown, int index)
+    private void clickFirstClipboardSuggestion(SearchActivityLocationBarLayout locationBar)
             throws InterruptedException {
-        // Wait a bit since the button may not able to click.
-        ViewGroup viewGroup = suggestionsDropdown.getViewGroup();
-        BaseSuggestionView baseSuggestionView = (BaseSuggestionView) viewGroup.getChildAt(index);
-        TestTouchUtils.performClickOnMainSync(InstrumentationRegistry.getInstrumentation(),
-                baseSuggestionView.getDecoratedSuggestionView());
+        CriteriaHelper.pollUiThread(() -> {
+            // Find the index of clipboard suggestion in the dropdown list.
+            final int clipboardSuggestionIndexInDropdown =
+                    AutocompleteCoordinatorTestUtils.getIndexForFirstSuggestionOfType(
+                            locationBar.getAutocompleteCoordinator(),
+                            OmniboxSuggestionUiType.CLIPBOARD_SUGGESTION);
+            Criteria.checkThat("Cannot find the clipboard Omnibox suggestion in ModelList.",
+                    clipboardSuggestionIndexInDropdown, Matchers.not(INVALID_INDEX));
+            OmniboxSuggestionsDropdown dropdown =
+                    AutocompleteCoordinatorTestUtils.getSuggestionsDropdown(
+                            locationBar.getAutocompleteCoordinator());
+            ViewGroup viewGroup = dropdown.getViewGroup();
+            BaseSuggestionView baseSuggestionView =
+                    (BaseSuggestionView) viewGroup.getChildAt(clipboardSuggestionIndexInDropdown);
+            Criteria.checkThat("Cannot find the clipboard Omnibox suggestion in UI.",
+                    baseSuggestionView, Matchers.notNullValue());
+            TestTouchUtils.performClickOnMainSync(InstrumentationRegistry.getInstrumentation(),
+                    baseSuggestionView.getDecoratedSuggestionView());
+        });
     }
 
     private SearchActivity startSearchActivity() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
index a7b28cc..7077c961 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
@@ -25,6 +25,7 @@
     private boolean mTrustedVaultKeyRequired;
     private boolean mTrustedVaultKeyRequiredForPreferredDataTypes;
     private boolean mEncryptEverythingEnabled;
+    private boolean mRequiresClientUpgrade;
     private Set<Integer> mChosenTypes = new HashSet<>();
     private boolean mCanSyncFeatureStart;
     @GoogleServiceAuthError.State
@@ -139,4 +140,13 @@
     public void setCanSyncFeatureStart(boolean canSyncFeatureStart) {
         mCanSyncFeatureStart = canSyncFeatureStart;
     }
+
+    @Override
+    public boolean requiresClientUpgrade() {
+        return mRequiresClientUpgrade;
+    }
+
+    public void setRequiresClientUpgrade(boolean requiresClientUpgrade) {
+        mRequiresClientUpgrade = requiresClientUpgrade;
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java
new file mode 100644
index 0000000..0fa5809
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java
@@ -0,0 +1,268 @@
+// Copyright 2020 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.sync;
+
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.accounts.Account;
+import android.view.View;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import org.chromium.base.test.params.ParameterAnnotations;
+import org.chromium.base.test.params.ParameterizedRunner;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.app.ChromeActivity;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.night_mode.ChromeNightModeTestUtils;
+import org.chromium.chrome.browser.settings.SettingsActivityTestRule;
+import org.chromium.chrome.browser.sync.settings.ManageSyncSettings;
+import org.chromium.chrome.browser.sync.settings.SyncSettingsUtils;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
+import org.chromium.chrome.test.util.ChromeRenderTestRule;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
+import org.chromium.components.signin.AccountUtils;
+import org.chromium.components.signin.ProfileDataSource;
+import org.chromium.components.signin.base.GoogleServiceAuthError;
+import org.chromium.components.signin.test.util.FakeProfileDataSource;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.test.util.NightModeTestUtils;
+
+/**
+ * Test suite for SyncErrorCardPreference
+ */
+@RunWith(ParameterizedRunner.class)
+@ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@Features.EnableFeatures(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)
+public class SyncErrorCardPreferenceTest {
+    // FakeProfileDataSource is required to create the ProfileDataCache entry with sync_error badge
+    // for Sync error card.
+    private final FakeProfileDataSource mFakeProfileDataSource = new FakeProfileDataSource();
+
+    @Rule
+    public final AccountManagerTestRule mAccountManagerTestRule =
+            new AccountManagerTestRule(mFakeProfileDataSource);
+
+    @Rule
+    public final ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
+            new ChromeActivityTestRule<>(ChromeActivity.class);
+
+    @Rule
+    public final SettingsActivityTestRule<ManageSyncSettings> mSettingsActivityTestRule =
+            new SettingsActivityTestRule<>(ManageSyncSettings.class, true);
+
+    @Rule
+    public final ChromeRenderTestRule mRenderTestRule =
+            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+
+    @Mock
+    private AndroidSyncSettings mAndroidSyncSettingsMock;
+
+    private FakeProfileSyncService mFakeProfileSyncService;
+
+    @ParameterAnnotations.UseMethodParameterBefore(NightModeTestUtils.NightModeParams.class)
+    public void setupNightMode(boolean nightModeEnabled) {
+        ChromeNightModeTestUtils.setUpNightModeForChromeActivity(nightModeEnabled);
+        mRenderTestRule.setNightModeEnabled(nightModeEnabled);
+    }
+
+    @BeforeClass
+    public static void setUpBeforeActivityLaunched() {
+        ChromeNightModeTestUtils.setUpNightModeBeforeChromeActivityLaunched();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        initMocks(this);
+        // Start main activity before because native side needs to be initialized before overriding
+        // ProfileSyncService.
+        mActivityTestRule.startMainActivityOnBlankPage();
+
+        Account account =
+                AccountUtils.createAccountFromName(AccountManagerTestRule.TEST_ACCOUNT_EMAIL);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mFakeProfileSyncService = new FakeProfileSyncService();
+            ProfileSyncService.overrideForTests(mFakeProfileSyncService);
+            // TODO(https://crbug.com/1125452): Use test resource for profile avatar instead of
+            // null.
+            mFakeProfileDataSource.setProfileData(account.name,
+                    new ProfileDataSource.ProfileData(
+                            account.name, null, "Full Name", "Given Name"));
+            AndroidSyncSettings.overrideForTests(mAndroidSyncSettingsMock);
+            when(mAndroidSyncSettingsMock.isChromeSyncEnabled()).thenReturn(true);
+        });
+    }
+
+    @After
+    public void tearDown() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ProfileSyncService.resetForTests();
+            mFakeProfileDataSource.setProfileData(AccountManagerTestRule.TEST_ACCOUNT_EMAIL, null);
+        });
+    }
+
+    @AfterClass
+    public static void tearDownAfterActivityDestroyed() {
+        ChromeNightModeTestUtils.tearDownNightModeAfterChromeActivityDestroyed();
+    }
+
+    @Test
+    @LargeTest
+    @Feature("RenderTest")
+    @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
+    public void testSyncErrorCardForAndroidSyncDisabled(boolean nightModeEnabled) throws Exception {
+        when(mAndroidSyncSettingsMock.doesMasterSyncSettingAllowChromeSync()).thenReturn(false);
+        mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync(mFakeProfileSyncService);
+        TestThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> Assert.assertEquals("ANDROID_SYNC_DISABLED SyncError should be set",
+                                SyncSettingsUtils.SyncError.ANDROID_SYNC_DISABLED,
+                                SyncSettingsUtils.getSyncError()));
+
+        mSettingsActivityTestRule.startSettingsActivity();
+        mRenderTestRule.render(
+                getPersonalizedSyncPromoView(), "sync_error_card_android_sync_disabled");
+    }
+
+    @Test
+    @LargeTest
+    @Feature("RenderTest")
+    @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
+    public void testSyncErrorCardForAuthError(boolean nightModeEnabled) throws Exception {
+        mFakeProfileSyncService.setAuthError(GoogleServiceAuthError.State.INVALID_GAIA_CREDENTIALS);
+        when(mAndroidSyncSettingsMock.doesMasterSyncSettingAllowChromeSync()).thenReturn(true);
+        mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync(mFakeProfileSyncService);
+        TestThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> Assert.assertEquals("AUTH_ERROR SyncError should be set",
+                                SyncSettingsUtils.SyncError.AUTH_ERROR,
+                                SyncSettingsUtils.getSyncError()));
+
+        mSettingsActivityTestRule.startSettingsActivity();
+        mRenderTestRule.render(getPersonalizedSyncPromoView(), "sync_error_card_auth_error");
+    }
+
+    @Test
+    @LargeTest
+    @Feature("RenderTest")
+    @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
+    public void testSyncErrorCardForClientOutOfDate(boolean nightModeEnabled) throws Exception {
+        mFakeProfileSyncService.setRequiresClientUpgrade(true);
+        when(mAndroidSyncSettingsMock.doesMasterSyncSettingAllowChromeSync()).thenReturn(true);
+        mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync(mFakeProfileSyncService);
+        TestThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> Assert.assertEquals("CLIENT_OUT_OF_DATE SyncError should be set",
+                                SyncSettingsUtils.SyncError.CLIENT_OUT_OF_DATE,
+                                SyncSettingsUtils.getSyncError()));
+
+        mSettingsActivityTestRule.startSettingsActivity();
+        mRenderTestRule.render(
+                getPersonalizedSyncPromoView(), "sync_error_card_client_out_of_date");
+    }
+
+    @Test
+    @LargeTest
+    @Feature("RenderTest")
+    @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
+    public void testSyncErrorCardForOtherErrors(boolean nightModeEnabled) throws Exception {
+        mFakeProfileSyncService.setAuthError(GoogleServiceAuthError.State.CONNECTION_FAILED);
+        when(mAndroidSyncSettingsMock.doesMasterSyncSettingAllowChromeSync()).thenReturn(true);
+        mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync(mFakeProfileSyncService);
+        TestThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> Assert.assertEquals("OTHER_ERRORS SyncError should be set",
+                                SyncSettingsUtils.SyncError.OTHER_ERRORS,
+                                SyncSettingsUtils.getSyncError()));
+
+        mSettingsActivityTestRule.startSettingsActivity();
+        mRenderTestRule.render(getPersonalizedSyncPromoView(), "sync_error_card_other_errors");
+    }
+
+    @Test
+    @LargeTest
+    @Feature("RenderTest")
+    @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
+    public void testSyncErrorCardForPassphraseRequired(boolean nightModeEnabled) throws Exception {
+        mFakeProfileSyncService.setEngineInitialized(true);
+        mFakeProfileSyncService.setPassphraseRequiredForPreferredDataTypes(true);
+        when(mAndroidSyncSettingsMock.doesMasterSyncSettingAllowChromeSync()).thenReturn(true);
+        mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync(mFakeProfileSyncService);
+        TestThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> Assert.assertEquals("PASSPHRASE_REQUIRED SyncError should be set",
+                                SyncSettingsUtils.SyncError.PASSPHRASE_REQUIRED,
+                                SyncSettingsUtils.getSyncError()));
+
+        mSettingsActivityTestRule.startSettingsActivity();
+        mRenderTestRule.render(
+                getPersonalizedSyncPromoView(), "sync_error_card_passphrase_required");
+    }
+
+    @Test
+    @LargeTest
+    @Feature("RenderTest")
+    @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
+    public void testSyncErrorCardForTrustedVaultKey(boolean nightModeEnabled) throws Exception {
+        mFakeProfileSyncService.setEngineInitialized(true);
+        mFakeProfileSyncService.setTrustedVaultKeyRequiredForPreferredDataTypes(true);
+        mFakeProfileSyncService.enableEncryptEverything();
+        when(mAndroidSyncSettingsMock.doesMasterSyncSettingAllowChromeSync()).thenReturn(true);
+        mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync(mFakeProfileSyncService);
+        TestThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> Assert.assertEquals("TRUSTED_VAULT_KEY_REQUIRED SyncError should be set",
+                                SyncSettingsUtils.SyncError
+                                        .TRUSTED_VAULT_KEY_REQUIRED_FOR_EVERYTHING,
+                                SyncSettingsUtils.getSyncError()));
+
+        mSettingsActivityTestRule.startSettingsActivity();
+        mRenderTestRule.render(
+                getPersonalizedSyncPromoView(), "sync_error_card_trusted_vault_key_required");
+    }
+
+    @Test
+    @LargeTest
+    @Feature("RenderTest")
+    @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
+    public void testSyncErrorCardForSyncSetupIncomplete(boolean nightModeEnabled) throws Exception {
+        when(mAndroidSyncSettingsMock.doesMasterSyncSettingAllowChromeSync()).thenReturn(true);
+        // Passing a null ProfileSyncService instance here would sign-in the user but
+        // FirstSetupComplete will be unset.
+        mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync(
+                /* profileSyncService= */ null);
+        TestThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> Assert.assertEquals("SYNC_SETUP_INCOMPLETE SyncError should be set",
+                                SyncSettingsUtils.SyncError.SYNC_SETUP_INCOMPLETE,
+                                SyncSettingsUtils.getSyncError()));
+
+        mSettingsActivityTestRule.startSettingsActivity();
+        mRenderTestRule.render(
+                getPersonalizedSyncPromoView(), "sync_error_card_sync_setup_incomplete");
+    }
+
+    private View getPersonalizedSyncPromoView() {
+        return mSettingsActivityTestRule.getActivity().findViewById(
+                R.id.signin_promo_view_container);
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webshare/WebShareTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webshare/WebShareTest.java
index a7cef55..47ed68b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webshare/WebShareTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webshare/WebShareTest.java
@@ -137,15 +137,17 @@
     @Feature({"WebShare"})
     @Features.DisableFeatures(ChromeFeatureList.CHROME_SHARING_HUB)
     @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP_MR1)
-    public void testWebShareCancel() throws Exception {
-        // Set up ShareHelper to ignore the intent (without showing a picker). This simulates the
-        // user canceling the dialog.
+    public void testWebShareDoubleRequest() throws Exception {
+        // Set up ShareHelper to ignore the intent (without showing a picker),
+        // and request another share.
         ShareHelper.setFakeIntentReceiverForTesting(new FakeIntentReceiverPostLMR1(false));
 
         mActivityTestRule.loadUrl(mTestServer.getURL(TEST_FILE));
         // Click (instead of directly calling the JavaScript function) to simulate a user gesture.
         TouchCommon.singleClickView(mTab.getView());
-        Assert.assertEquals("Fail: AbortError: Share canceled", mUpdateWaiter.waitForUpdate());
+        Assert.assertEquals("Fail: InvalidStateError: Failed to execute 'share' on 'Navigator': "
+                        + "A earlier share had not yet completed.",
+                mUpdateWaiter.waitForUpdate());
     }
 
     /**
@@ -458,10 +460,8 @@
             mReceivedIntent = intent;
 
             if (!mProceed) {
-                // Click again to start another share, which cancels the current share.
-                // This is necessary to work around https://crbug.com/636274 (callback
-                // is not canceled until next share is initiated).
-                // This also serves as a regression test for https://crbug.com/640324.
+                // Click again to start another share, which fails as a share is already in
+                // progress.
                 TouchCommon.singleClickView(mTab.getView());
                 return;
             }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java
index 1566534..a2a1ee05 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java
@@ -23,7 +23,7 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionViewDelegate;
 import org.chromium.testing.local.LocalRobolectricTestRunner;
 
 /**
diff --git a/chrome/app/chrome_exe_main_mac.cc b/chrome/app/chrome_exe_main_mac.cc
index fbd2dad..df2dcbb2 100644
--- a/chrome/app/chrome_exe_main_mac.cc
+++ b/chrome/app/chrome_exe_main_mac.cc
@@ -23,7 +23,7 @@
 #include "chrome/common/chrome_version.h"
 
 #if defined(HELPER_EXECUTABLE)
-#include "sandbox/mac/seatbelt_exec.h"
+#include "sandbox/mac/seatbelt_exec.h"  // nogncheck
 #endif  // defined(HELPER_EXECUTABLE)
 
 extern "C" {
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index d0fc5b16..4c04a94 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -482,6 +482,9 @@
   <message name="IDS_SETTINGS_PASSWORD_EDIT_FOOTNOTE" desc="A footnote for the dialog which allows the user to edit a saved password.">
     Make sure the password you are saving matches your password for <ph name="WEBSITE">$1<ex>airbnb.com</ex></ph>
   </message>
+  <message name="IDS_SETTINGS_PASSWORD_USERNAME_ALREADY_USED" desc="An error message when user tried editing the username to a value which is already used for the same site.">
+    You already saved this username for this site
+  </message>
   <message name="IDS_SETTINGS_PASSWORD_COPY" desc="Label for a context menu item that allows to copy the selected password into clipboard.">
     Copy password
   </message>
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_USERNAME_ALREADY_USED.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_USERNAME_ALREADY_USED.png.sha1
new file mode 100644
index 0000000..17feb49
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_USERNAME_ALREADY_USED.png.sha1
@@ -0,0 +1 @@
+65b985cbdd9905b01170b331f7d9ce3b19e60d90
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 71f3d84d..4a25373 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3606,6 +3606,8 @@
       "renderer_context_menu/accessibility_labels_bubble_model.h",
       "renderer_context_menu/accessibility_labels_menu_observer.cc",
       "renderer_context_menu/accessibility_labels_menu_observer.h",
+      "renderer_context_menu/copy_link_to_text_menu_observer.cc",
+      "renderer_context_menu/copy_link_to_text_menu_observer.h",
       "renderer_context_menu/render_view_context_menu.cc",
       "renderer_context_menu/render_view_context_menu.h",
       "renderer_context_menu/spelling_bubble_model.cc",
@@ -4462,6 +4464,8 @@
       "themes/theme_helper_win.cc",
       "themes/theme_helper_win.h",
       "upgrade_detector/get_installed_version_win.cc",
+      "webshare/win/show_share_ui_for_window_operation.cc",
+      "webshare/win/show_share_ui_for_window_operation.h",
       "win/app_icon.cc",
       "win/app_icon.h",
       "win/automation_controller.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index b7a13ff..92dea753 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1203,6 +1203,8 @@
      base::size(kOmniboxNTPZPSRemoteLocal), /* variation_id */ "t3317569"},
     {"Contextual Web", kOmniboxNTPZPSRemoteLocal,
      base::size(kOmniboxNTPZPSRemoteLocal), /* variation_id */ "t3317605"},
+    {"Trending Queries", kOmniboxNTPZPSRemoteLocal,
+     base::size(kOmniboxNTPZPSRemoteLocal), /* variation_id */ "t3317858"},
 #else   // !defined(OS_ANDROID)
     {"NTP Omnibox - Remote History, Local History", kNTPOmniboxZPSRemoteLocal,
      base::size(kNTPOmniboxZPSRemoteLocal), nullptr /* variation_id */},
@@ -4064,11 +4066,6 @@
      SINGLE_VALUE_TYPE(extensions::switches::kForceEmptyCorbAllowlist)},
 #endif
 
-    {"cross-origin-opener-policy",
-     flag_descriptions::kCrossOriginOpenerPolicyName,
-     flag_descriptions::kCrossOriginOpenerPolicyDescription, kOsAll,
-     FEATURE_VALUE_TYPE(network::features::kCrossOriginOpenerPolicy)},
-
     {"cross-origin-opener-policy-reporting",
      flag_descriptions::kCrossOriginOpenerPolicyReportingName,
      flag_descriptions::kCrossOriginOpenerPolicyReportingDescription, kOsAll,
@@ -5153,11 +5150,6 @@
      flag_descriptions::kAllowScrollSettingsDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kAllowScrollSettings)},
 
-    {"enable-streamlined-usb-printer-setup",
-     flag_descriptions::kStreamlinedUsbPrinterSetupName,
-     flag_descriptions::kStreamlinedUsbPrinterSetupDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(features::kStreamlinedUsbPrinterSetup)},
-
     {"enable-media-session-notifications",
      flag_descriptions::kMediaSessionNotificationsName,
      flag_descriptions::kMediaSessionNotificationsDescription, kOsCrOS,
diff --git a/chrome/browser/chromeos/crosapi/browser_util.cc b/chrome/browser/chromeos/crosapi/browser_util.cc
index bf8f381a..b451bce 100644
--- a/chrome/browser/chromeos/crosapi/browser_util.cc
+++ b/chrome/browser/chromeos/crosapi/browser_util.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_paths.h"
+#include "chromeos/crosapi/cpp/crosapi_constants.h"
 #include "chromeos/crosapi/mojom/crosapi.mojom.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/metrics/metrics_pref_names.h"
@@ -114,9 +115,6 @@
   const std::string* app_id = exo::GetShellApplicationId(window);
   if (!app_id)
     return false;
-  // TODO(jamescook): Move this constant to //chromeos/crosapi/cpp and share it
-  // with //ui/ozone/wayland.
-  const char kLacrosAppIdPrefix[] = "org.chromium.lacros";
   return base::StartsWith(*app_id, kLacrosAppIdPrefix);
 }
 
diff --git a/chrome/browser/chromeos/login/screens/family_link_notice_browsertest.cc b/chrome/browser/chromeos/login/screens/family_link_notice_browsertest.cc
index ac42add..1922273 100644
--- a/chrome/browser/chromeos/login/screens/family_link_notice_browsertest.cc
+++ b/chrome/browser/chromeos/login/screens/family_link_notice_browsertest.cc
@@ -108,18 +108,9 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-#if defined(MEMORY_SANITIZER)
-// This entire suite is slow enough under MSan that it flakily times out:
-// https://crbug.com/1131570
-#define DISABLE_UNDER_MSAN(name) DISABLED_##name
-#else
-#define DISABLE_UNDER_MSAN(name) name
-#endif
-
 // Verify that regular account user should not see family link notice screen
 // after log in.
-IN_PROC_BROWSER_TEST_F(FamilyLinkNoticeScreenTest,
-                       DISABLE_UNDER_MSAN(RegularAccount)) {
+IN_PROC_BROWSER_TEST_F(FamilyLinkNoticeScreenTest, RegularAccount) {
   WizardController::default_controller()
       ->get_wizard_context_for_testing()
       ->sign_in_as_child = false;
@@ -131,8 +122,7 @@
 
 // Verify user should see family link notice screen when selecting to sign in
 // as a child account but log in as a regular account.
-IN_PROC_BROWSER_TEST_F(FamilyLinkNoticeScreenTest,
-                       DISABLE_UNDER_MSAN(NonSupervisedChildAccount)) {
+IN_PROC_BROWSER_TEST_F(FamilyLinkNoticeScreenTest, NonSupervisedChildAccount) {
   WizardController::default_controller()
       ->get_wizard_context_for_testing()
       ->sign_in_as_child = true;
@@ -168,8 +158,7 @@
 
 // Verify child account user should not see family link notice screen after log
 // in.
-IN_PROC_BROWSER_TEST_F(FamilyLinkNoticeScreenChildTest,
-                       DISABLE_UNDER_MSAN(ChildAccount)) {
+IN_PROC_BROWSER_TEST_F(FamilyLinkNoticeScreenChildTest, ChildAccount) {
   WizardController::default_controller()
       ->get_wizard_context_for_testing()
       ->sign_in_as_child = true;
@@ -182,7 +171,7 @@
 // Verify child account user should not see family link notice screen after log
 // in if not selecting sign in as child.
 IN_PROC_BROWSER_TEST_F(FamilyLinkNoticeScreenChildTest,
-                       DISABLE_UNDER_MSAN(ChildAccountSignInAsRegular)) {
+                       ChildAccountSignInAsRegular) {
   WizardController::default_controller()
       ->get_wizard_context_for_testing()
       ->sign_in_as_child = false;
@@ -206,8 +195,7 @@
   UserPolicyMixin user_policy_mixin_{&mixin_host_, test_user_.account_id};
 };
 
-IN_PROC_BROWSER_TEST_F(FamilyLinkNoticeScreenManagedTest,
-                       DISABLE_UNDER_MSAN(ManagedAccount)) {
+IN_PROC_BROWSER_TEST_F(FamilyLinkNoticeScreenManagedTest, ManagedAccount) {
   WizardController::default_controller()
       ->get_wizard_context_for_testing()
       ->sign_in_as_child = true;
diff --git a/chrome/browser/chromeos/printing/automatic_usb_printer_configurer.cc b/chrome/browser/chromeos/printing/automatic_usb_printer_configurer.cc
index 7040a88..3dc2025 100644
--- a/chrome/browser/chromeos/printing/automatic_usb_printer_configurer.cc
+++ b/chrome/browser/chromeos/printing/automatic_usb_printer_configurer.cc
@@ -8,10 +8,9 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/feature_list.h"
+#include "base/logging.h"
 #include "base/stl_util.h"
 #include "chrome/browser/chromeos/printing/usb_printer_notification_controller.h"
-#include "chrome/common/chrome_features.h"
 
 namespace chromeos {
 namespace {
@@ -45,10 +44,6 @@
     const std::vector<Printer>& printers) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_);
 
-  if (!base::FeatureList::IsEnabled(features::kStreamlinedUsbPrinterSetup)) {
-    return;
-  }
-
   if (printer_class == PrinterClass::kAutomatic) {
     // Remove any notifications for printers that are no longer in the automatic
     // class and setup any USB printers we haven't seen yet.
diff --git a/chrome/browser/chromeos/printing/automatic_usb_printer_configurer_unittest.cc b/chrome/browser/chromeos/printing/automatic_usb_printer_configurer_unittest.cc
index 769a4655..5eb7b8c0 100644
--- a/chrome/browser/chromeos/printing/automatic_usb_printer_configurer_unittest.cc
+++ b/chrome/browser/chromeos/printing/automatic_usb_printer_configurer_unittest.cc
@@ -10,12 +10,10 @@
 
 #include "base/containers/flat_set.h"
 #include "base/optional.h"
-#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/chromeos/printing/cups_printers_manager.h"
 #include "chrome/browser/chromeos/printing/printers_map.h"
 #include "chrome/browser/chromeos/printing/test_printer_configurer.h"
 #include "chrome/browser/chromeos/printing/usb_printer_notification_controller.h"
-#include "chrome/common/chrome_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
@@ -143,8 +141,6 @@
 class AutomaticUsbPrinterConfigurerTest : public testing::Test {
  public:
   AutomaticUsbPrinterConfigurerTest() {
-    scoped_feature_list_.InitAndEnableFeature(
-        features::kStreamlinedUsbPrinterSetup);
     fake_installation_manager_ =
         std::make_unique<FakePrinterInstallationManager>();
     auto printer_configurer = std::make_unique<TestPrinterConfigurer>();
@@ -171,9 +167,6 @@
       fake_notification_controller_;
   std::unique_ptr<AutomaticUsbPrinterConfigurer> auto_usb_printer_configurer_;
 
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(AutomaticUsbPrinterConfigurerTest);
 };
 
diff --git a/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc b/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc
index ae0ce0d..f5e3caaf 100644
--- a/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc
+++ b/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc
@@ -16,7 +16,6 @@
 #include "base/containers/flat_set.h"
 #include "base/sequenced_task_runner.h"
 #include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/chromeos/printing/enterprise_printers_provider.h"
@@ -345,8 +344,6 @@
                                 public CupsPrintersManager::Observer {
  public:
   CupsPrintersManagerTest() : ppd_provider_(new FakePpdProvider) {
-    scoped_feature_list_.InitAndEnableFeature(
-        features::kStreamlinedUsbPrinterSetup);
     // Zeroconf and usb detector ownerships are taken by the manager, so we have
     // to keep raw pointers to them.
     auto zeroconf_detector = std::make_unique<FakePrinterDetector>();
@@ -399,7 +396,6 @@
 
  protected:
   base::test::TaskEnvironment task_environment_;
-  base::test::ScopedFeatureList scoped_feature_list_;
 
   // Captured printer lists from observer callbacks.
   base::flat_map<PrinterClass, std::vector<Printer>> observed_printers_;
diff --git a/chrome/browser/chromeos/printing/usb_printer_notification_controller.cc b/chrome/browser/chromeos/printing/usb_printer_notification_controller.cc
index fe9663a..0d57de4 100644
--- a/chrome/browser/chromeos/printing/usb_printer_notification_controller.cc
+++ b/chrome/browser/chromeos/printing/usb_printer_notification_controller.cc
@@ -4,11 +4,9 @@
 
 #include "chrome/browser/chromeos/printing/usb_printer_notification_controller.h"
 
-#include "base/feature_list.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/chrome_features.h"
 
 namespace chromeos {
 
@@ -47,10 +45,6 @@
  private:
   void ShowNotification(const Printer& printer,
                         UsbPrinterNotification::Type type) {
-    if (!base::FeatureList::IsEnabled(features::kStreamlinedUsbPrinterSetup)) {
-      return;
-    }
-
     if (base::Contains(notifications_, printer.id())) {
       return;
     }
diff --git a/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc b/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc
index b3262c3..35f623a 100644
--- a/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc
+++ b/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc
@@ -285,6 +285,15 @@
                            disable_reasons);
 }
 
+void ForceInstalledMetrics::ReportMetricsOnExtensionsReady() {
+  for (const auto& extension : tracker_->extensions()) {
+    if (extension.second.status != ExtensionStatus::READY)
+      return;
+  }
+  base::UmaHistogramLongTimes("Extensions.ForceInstalledReadyTime",
+                              base::Time::Now() - start_time_);
+}
+
 void ForceInstalledMetrics::ReportMetrics() {
   base::UmaHistogramCounts100("Extensions.ForceInstalledTotalCandidateCount",
                               tracker_->extensions().size());
@@ -305,7 +314,7 @@
                                 base::Time::Now() - start_time_);
     // TODO(burunduk): Remove VLOGs after resolving crbug/917700 and
     // crbug/904600.
-    VLOG(2) << "All forced extensions seems to be installed";
+    VLOG(2) << "All forced extensions seem to be installed";
     return;
   }
   size_t enabled_missing_count = missing_forced_extensions.size();
@@ -385,15 +394,24 @@
 }
 
 void ForceInstalledMetrics::OnForceInstalledExtensionsLoaded() {
-  if (reported_)
+  if (load_reported_)
     return;
   // Report only if there was non-empty list of force-installed extensions.
   if (!tracker_->extensions().empty())
     ReportMetrics();
-  reported_ = true;
+  load_reported_ = true;
   timer_->Stop();
 }
 
+void ForceInstalledMetrics::OnForceInstalledExtensionsReady() {
+  if (ready_reported_)
+    return;
+  // Report only if there was non-empty list of force-installed extensions.
+  if (!tracker_->extensions().empty())
+    ReportMetricsOnExtensionsReady();
+  ready_reported_ = true;
+}
+
 void ForceInstalledMetrics::OnExtensionDownloadCacheStatusRetrieved(
     const ExtensionId& id,
     ExtensionDownloaderDelegate::CacheStatus cache_status) {
diff --git a/chrome/browser/extensions/forced_extensions/force_installed_metrics.h b/chrome/browser/extensions/forced_extensions/force_installed_metrics.h
index 958221b..06fe74f 100644
--- a/chrome/browser/extensions/forced_extensions/force_installed_metrics.h
+++ b/chrome/browser/extensions/forced_extensions/force_installed_metrics.h
@@ -63,6 +63,10 @@
   // observers.
   void OnForceInstalledExtensionsLoaded() override;
 
+  // Calls ReportMetricsOnExtensionsReady method if there is a non-empty list of
+  // force-installed extensions.
+  void OnForceInstalledExtensionsReady() override;
+
   // Reports cache status for the force installed extensions.
   void OnExtensionDownloadCacheStatusRetrieved(
       const ExtensionId& id,
@@ -82,6 +86,10 @@
   // why they were not installed.
   void ReportMetrics();
 
+  // Reports metrics for sessions when all force installed extensions are ready
+  // for use.
+  void ReportMetricsOnExtensionsReady();
+
   ExtensionRegistry* const registry_;
   Profile* const profile_;
   ForceInstalledTracker* const tracker_;
@@ -89,8 +97,12 @@
   // Moment when the class was initialized.
   base::Time start_time_;
 
-  // Tracks whether stats were already reported for the session.
-  bool reported_ = false;
+  // Tracks whether extensions load stats were already for the session.
+  bool load_reported_ = false;
+
+  // Tracks whether extensions ready stats were already reported for the
+  // session.
+  bool ready_reported_ = false;
 
   ScopedObserver<ForceInstalledTracker, ForceInstalledTracker::Observer>
       tracker_observer_{this};
diff --git a/chrome/browser/extensions/forced_extensions/force_installed_metrics_unittest.cc b/chrome/browser/extensions/forced_extensions/force_installed_metrics_unittest.cc
index 8e9cb8c..897c589 100644
--- a/chrome/browser/extensions/forced_extensions/force_installed_metrics_unittest.cc
+++ b/chrome/browser/extensions/forced_extensions/force_installed_metrics_unittest.cc
@@ -49,6 +49,7 @@
 const int kResponseCode = 401;
 
 constexpr char kLoadTimeStats[] = "Extensions.ForceInstalledLoadTime";
+constexpr char kReadyTimeStats[] = "Extensions.ForceInstalledReadyTime";
 constexpr char kTimedOutStats[] = "Extensions.ForceInstalledTimedOutCount";
 constexpr char kTimedOutNotInstalledStats[] =
     "Extensions.ForceInstalledTimedOutAndNotInstalledCount";
@@ -181,6 +182,7 @@
   EXPECT_FALSE(fake_timer_->IsRunning());
   // Don't report metrics when the Forcelist is empty.
   histogram_tester_.ExpectTotalCount(kLoadTimeStats, 0);
+  histogram_tester_.ExpectTotalCount(kReadyTimeStats, 0);
   histogram_tester_.ExpectTotalCount(kTimedOutStats, 0);
   histogram_tester_.ExpectTotalCount(kTimedOutNotInstalledStats, 0);
   histogram_tester_.ExpectTotalCount(kFailureReasonsCWS, 0);
@@ -201,6 +203,7 @@
   force_installed_tracker()->OnExtensionLoaded(profile(), ext2.get());
 
   histogram_tester_.ExpectTotalCount(kLoadTimeStats, 1);
+  histogram_tester_.ExpectTotalCount(kReadyTimeStats, 0);
   histogram_tester_.ExpectTotalCount(kTimedOutStats, 0);
   histogram_tester_.ExpectTotalCount(kTimedOutNotInstalledStats, 0);
   histogram_tester_.ExpectTotalCount(kFailureReasonsCWS, 0);
@@ -573,7 +576,8 @@
 }
 
 // Regression test to check if the metrics are collected properly for the
-// extensions which are in state READY.
+// extensions which are in state READY. Also verifies that the failure reported
+// after READY state is not reflected in the statistics.
 TEST_F(ForceInstalledMetricsTest, ExtensionsReady) {
   SetupForceList();
   auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
@@ -588,8 +592,28 @@
   // loaded or failed.
   EXPECT_FALSE(fake_timer_->IsRunning());
   histogram_tester_.ExpectTotalCount(kLoadTimeStats, 1);
+  histogram_tester_.ExpectTotalCount(kReadyTimeStats, 1);
   histogram_tester_.ExpectTotalCount(kTimedOutStats, 0);
   histogram_tester_.ExpectTotalCount(kTimedOutNotInstalledStats, 0);
+  histogram_tester_.ExpectTotalCount(kFailureReasonsCWS, 0);
+}
+
+// Regression test to check if no metrics are reported for READY state when some
+// extensions are failed.
+TEST_F(ForceInstalledMetricsTest, AllExtensionsNotReady) {
+  SetupForceList();
+  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
+  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
+  force_installed_tracker()->OnExtensionReady(profile(), ext1.get());
+  install_stage_tracker()->ReportFailure(
+      kExtensionId2, InstallStageTracker::FailureReason::INVALID_ID);
+  // ForceInstalledMetrics shuts down timer because all extension are either
+  // loaded or failed.
+  EXPECT_FALSE(fake_timer_->IsRunning());
+  histogram_tester_.ExpectTotalCount(kLoadTimeStats, 0);
+  histogram_tester_.ExpectTotalCount(kReadyTimeStats, 0);
+  histogram_tester_.ExpectBucketCount(
+      kFailureReasonsCWS, InstallStageTracker::FailureReason::INVALID_ID, 1);
 }
 
 // Verifies that the installation stage is not overwritten by a previous stage.
@@ -1068,6 +1092,7 @@
   EXPECT_TRUE(fake_timer_->IsRunning());
   fake_timer_->Fire();
   histogram_tester_.ExpectTotalCount(kLoadTimeStats, 0);
+  histogram_tester_.ExpectTotalCount(kReadyTimeStats, 0);
   histogram_tester_.ExpectTotalCount(kTimedOutStats, 0);
   histogram_tester_.ExpectTotalCount(kTimedOutNotInstalledStats, 0);
   histogram_tester_.ExpectTotalCount(kFailureReasonsCWS, 0);
diff --git a/chrome/browser/extensions/forced_extensions/force_installed_tracker_unittest.cc b/chrome/browser/extensions/forced_extensions/force_installed_tracker_unittest.cc
index 5ad4090..c64ae14 100644
--- a/chrome/browser/extensions/forced_extensions/force_installed_tracker_unittest.cc
+++ b/chrome/browser/extensions/forced_extensions/force_installed_tracker_unittest.cc
@@ -161,4 +161,18 @@
   EXPECT_FALSE(ready_called_);
 }
 
+// This test verifies that READY state observer is called when each force
+// installed extension is either ready for use or failed.
+TEST_F(ForceInstalledTrackerTest, AllExtensionsReady) {
+  SetupForceList();
+  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
+  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
+  force_installed_tracker()->OnExtensionReady(profile(), ext1.get());
+  force_installed_tracker()->OnExtensionInstallationFailed(
+      kExtensionId2, InstallStageTracker::FailureReason::INVALID_ID);
+  EXPECT_TRUE(loaded_called_);
+  EXPECT_TRUE(ready_called_);
+  EXPECT_TRUE(force_installed_tracker()->IsDoneLoading());
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 03c7ecb3..783b5bf6 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -672,11 +672,6 @@
     "expiry_milestone": 88
   },
   {
-    "name": "cross-origin-opener-policy",
-    "owners": [ "ahemery", "clamy", "pmeuleman" ],
-    "expiry_milestone": 85
-  },
-  {
     "name": "cross-origin-opener-policy-access-reporting",
     "owners": [ "ahemery", "arthursonzogni", "clamy", "pmeuleman" ],
     "expiry_milestone": 88
@@ -2094,11 +2089,6 @@
     "expiry_milestone": 85
   },
   {
-    "name": "enable-streamlined-usb-printer-setup",
-    "owners": [ "baileyberro" ],
-    "expiry_milestone": 77
-  },
-  {
     "name": "enable-subresource-redirect",
     "owners": [ "rajendrant", "mcrouse", "tbansal" ],
     "expiry_milestone": 87
@@ -4625,13 +4615,18 @@
     "expiry_milestone": 89
   },
   {
+    "name": "webpage-alternative-text-zoom",
+    "owners": [ "rkgibson@google.com", "bling-flags@google.com" ],
+    "expiry_milestone": 86
+  },
+  {
     "name": "webpage-default-zoom-from-dynamic-type",
-    "owners": [ "rkgibson", "bling-flags" ],
+    "owners": [ "rkgibson@google.com", "bling-flags@google.com" ],
     "expiry_milestone": 87
   },
   {
     "name": "webpage-text-accessibility",
-    "owners": [ "rkgibson", "bling-flags" ],
+    "owners": [ "rkgibson@google.com", "bling-flags@google.com" ],
     "expiry_milestone": 86
   },
   {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index d3ac39d..39009ac 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -868,10 +868,6 @@
     "https://www.chromium.org/Home/chromium-security/"
     "extension-content-script-fetches";
 
-const char kCrossOriginOpenerPolicyName[] = "Cross Origin Opener Policy";
-const char kCrossOriginOpenerPolicyDescription[] =
-    "Enables Cross Origin Opener Policy.";
-
 const char kCrossOriginOpenerPolicyReportingName[] =
     "Cross Origin Opener Policy reporting";
 const char kCrossOriginOpenerPolicyReportingDescription[] =
@@ -4313,12 +4309,6 @@
     "OS settings UI to provide controls for OS data types. Requires "
     "#split-settings to be enabled.";
 
-const char kStreamlinedUsbPrinterSetupName[] =
-    "Streamlined USB Printer Setup Flow";
-const char kStreamlinedUsbPrinterSetupDescription[] =
-    "Automatically sets up capable USB printers when plugged in. Shows a "
-    "notification with the setup result.";
-
 const char kSyncWifiConfigurationsName[] = "Sync Wi-Fi network configurations";
 const char kSyncWifiConfigurationsDescription[] =
     "Enables the option to sync Wi-Fi network configurations with Chrome Sync.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index bbd0fe7..12bbe90c 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -477,9 +477,6 @@
 extern const char kForceEmptyCorbAndCorsAllowlistName[];
 extern const char kForceEmptyCorbAndCorsAllowlistDescription[];
 
-extern const char kCrossOriginOpenerPolicyName[];
-extern const char kCrossOriginOpenerPolicyDescription[];
-
 extern const char kCrossOriginOpenerPolicyReportingName[];
 extern const char kCrossOriginOpenerPolicyReportingDescription[];
 
@@ -2513,9 +2510,6 @@
 extern const char kSplitSettingsSyncName[];
 extern const char kSplitSettingsSyncDescription[];
 
-extern const char kStreamlinedUsbPrinterSetupName[];
-extern const char kStreamlinedUsbPrinterSetupDescription[];
-
 extern const char kSyncWifiConfigurationsName[];
 extern const char kSyncWifiConfigurationsDescription[];
 
diff --git a/chrome/browser/net/system_network_context_manager.h b/chrome/browser/net/system_network_context_manager.h
index daaad39..af02c3a 100644
--- a/chrome/browser/net/system_network_context_manager.h
+++ b/chrome/browser/net/system_network_context_manager.h
@@ -19,7 +19,6 @@
 #include "components/prefs/pref_member.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "net/dns/dns_config.h"
 #include "services/network/public/mojom/host_resolver.mojom-forward.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "services/network/public/mojom/network_service.mojom-forward.h"
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac.h b/chrome/browser/notifications/notification_platform_bridge_mac.h
index 4ad91159..f9dee25 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac.h
+++ b/chrome/browser/notifications/notification_platform_bridge_mac.h
@@ -47,14 +47,6 @@
   void SetReadyCallback(NotificationBridgeReadyCallback callback) override;
   void DisplayServiceShutDown(Profile* profile) override;
 
-  // Processes a notification response generated from a user action
-  // (click close, etc.).
-  static void ProcessNotificationResponse(NSDictionary* response);
-
-  // Validates contents of the |response| dictionary as received from the system
-  // when a notification gets activated.
-  static bool VerifyNotificationData(NSDictionary* response) WARN_UNUSED_RESULT;
-
   // Returns if alerts are supported on this machine.
   static bool SupportsAlerts();
 
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac.mm b/chrome/browser/notifications/notification_platform_bridge_mac.mm
index 14446ed..c2905bf 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac.mm
@@ -26,13 +26,11 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/system/sys_info.h"
 #include "chrome/browser/browser_features.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/notifications/notification_common.h"
 #include "chrome/browser/notifications/notification_display_service_impl.h"
 #include "chrome/browser/notifications/notification_platform_bridge_mac_utils.h"
 #include "chrome/browser/notifications/platform_notification_service_impl.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/cocoa/notifications/notification_builder_mac.h"
 #include "chrome/browser/ui/cocoa/notifications/notification_constants_mac.h"
 #import "chrome/browser/ui/cocoa/notifications/notification_delivery.h"
@@ -41,7 +39,6 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/crash/core/app/crashpad.h"
 #include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
 #include "third_party/blink/public/common/notifications/notification_constants.h"
 #include "third_party/crashpad/crashpad/client/crashpad_client.h"
 #include "ui/base/l10n/l10n_util_mac.h"
@@ -69,35 +66,6 @@
 
 namespace {
 
-// Loads the profile and process the Notification response
-void DoProcessNotificationResponse(NotificationCommon::Operation operation,
-                                   NotificationHandler::Type type,
-                                   const std::string& profile_id,
-                                   bool incognito,
-                                   const GURL& origin,
-                                   const std::string& notification_id,
-                                   const base::Optional<int>& action_index,
-                                   const base::Optional<base::string16>& reply,
-                                   const base::Optional<bool>& by_user) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  // Profile ID can be empty for system notifications, which are not bound to a
-  // profile, but system notifications are transient and thus not handled by
-  // this NotificationPlatformBridge.
-  // When transient notifications are supported, this should route the
-  // notification response to the system NotificationDisplayService.
-  DCHECK(!profile_id.empty());
-
-  ProfileManager* profileManager = g_browser_process->profile_manager();
-  DCHECK(profileManager);
-
-  profileManager->LoadProfile(
-      profile_id, incognito,
-      base::Bind(&NotificationDisplayServiceImpl::ProfileLoadedCallback,
-                 operation, type, origin, notification_id, action_index, reply,
-                 by_user));
-}
-
 // This enum backs an UMA histogram, so it should be treated as append-only.
 enum XPCConnectionEvent {
   INTERRUPTED = 0,
@@ -320,124 +288,6 @@
 void NotificationPlatformBridgeMac::DisplayServiceShutDown(Profile* profile) {}
 
 // static
-void NotificationPlatformBridgeMac::ProcessNotificationResponse(
-    NSDictionary* response) {
-  if (!NotificationPlatformBridgeMac::VerifyNotificationData(response))
-    return;
-
-  NSNumber* button_index =
-      [response objectForKey:notification_constants::kNotificationButtonIndex];
-  NSNumber* operation =
-      [response objectForKey:notification_constants::kNotificationOperation];
-
-  std::string notification_origin = base::SysNSStringToUTF8(
-      [response objectForKey:notification_constants::kNotificationOrigin]);
-  std::string notification_id = base::SysNSStringToUTF8(
-      [response objectForKey:notification_constants::kNotificationId]);
-  std::string profile_id = base::SysNSStringToUTF8(
-      [response objectForKey:notification_constants::kNotificationProfileId]);
-  NSNumber* is_incognito =
-      [response objectForKey:notification_constants::kNotificationIncognito];
-  NSNumber* notification_type =
-      [response objectForKey:notification_constants::kNotificationType];
-
-  base::Optional<int> action_index;
-  if (button_index.intValue !=
-      notification_constants::kNotificationInvalidButtonIndex) {
-    action_index = button_index.intValue;
-  }
-
-  content::GetUIThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(DoProcessNotificationResponse,
-                     static_cast<NotificationCommon::Operation>(
-                         operation.unsignedIntValue),
-                     static_cast<NotificationHandler::Type>(
-                         notification_type.unsignedIntValue),
-                     profile_id, [is_incognito boolValue],
-                     GURL(notification_origin), notification_id, action_index,
-                     base::nullopt /* reply */, true /* by_user */));
-}
-
-// static
-bool NotificationPlatformBridgeMac::VerifyNotificationData(
-    NSDictionary* response) {
-  if (![response
-          objectForKey:notification_constants::kNotificationButtonIndex] ||
-      ![response objectForKey:notification_constants::kNotificationOperation] ||
-      ![response objectForKey:notification_constants::kNotificationId] ||
-      ![response objectForKey:notification_constants::kNotificationProfileId] ||
-      ![response objectForKey:notification_constants::kNotificationIncognito] ||
-      ![response
-          objectForKey:notification_constants::kNotificationCreatorPid] ||
-      ![response objectForKey:notification_constants::kNotificationType]) {
-    LOG(ERROR) << "Missing required key";
-    return false;
-  }
-
-  NSNumber* button_index =
-      [response objectForKey:notification_constants::kNotificationButtonIndex];
-  NSNumber* operation =
-      [response objectForKey:notification_constants::kNotificationOperation];
-  NSString* notification_id =
-      [response objectForKey:notification_constants::kNotificationId];
-  NSString* profile_id =
-      [response objectForKey:notification_constants::kNotificationProfileId];
-  NSNumber* notification_type =
-      [response objectForKey:notification_constants::kNotificationType];
-  NSNumber* creator_pid =
-      [response objectForKey:notification_constants::kNotificationCreatorPid];
-
-  if (creator_pid.unsignedIntValue != static_cast<NSInteger>(getpid())) {
-    return false;
-  }
-
-  if (button_index.intValue <
-          notification_constants::kNotificationInvalidButtonIndex ||
-      button_index.intValue >=
-          static_cast<int>(blink::kNotificationMaxActions)) {
-    LOG(ERROR) << "Invalid number of buttons supplied "
-               << button_index.intValue;
-    return false;
-  }
-
-  if (operation.unsignedIntValue > NotificationCommon::OPERATION_MAX) {
-    LOG(ERROR) << operation.unsignedIntValue
-               << " does not correspond to a valid operation.";
-    return false;
-  }
-
-  if (notification_id.length <= 0) {
-    LOG(ERROR) << "Notification Id is empty";
-    return false;
-  }
-
-  if (profile_id.length <= 0) {
-    LOG(ERROR) << "ProfileId not provided";
-    return false;
-  }
-
-  if (notification_type.unsignedIntValue >
-      static_cast<unsigned int>(NotificationHandler::Type::MAX)) {
-    LOG(ERROR) << notification_type.unsignedIntValue
-               << " Does not correspond to a valid operation.";
-    return false;
-  }
-
-  // Origin is not actually required but if it's there it should be a valid one.
-  NSString* origin =
-      [response objectForKey:notification_constants::kNotificationOrigin];
-  if (origin && origin.length) {
-    std::string notificationOrigin = base::SysNSStringToUTF8(origin);
-    GURL url(notificationOrigin);
-    if (!url.is_valid())
-      return false;
-  }
-
-  return true;
-}
-
-// static
 bool NotificationPlatformBridgeMac::SupportsAlerts() {
   // Cache result as SysInfo::OperatingSystemVersionNumbers might be expensive.
   static bool supports_alerts = SupportsAlertsImpl();
@@ -450,8 +300,7 @@
        didActivateNotification:(NSUserNotification*)notification {
   NSDictionary* notificationResponse =
       [NotificationResponseBuilder buildActivatedDictionary:notification];
-  NotificationPlatformBridgeMac::ProcessNotificationResponse(
-      notificationResponse);
+  ProcessMacNotificationResponse(notificationResponse);
 }
 
 // Overriden from _NSUserNotificationCenterDelegatePrivate.
@@ -464,8 +313,7 @@
                didDismissAlert:(NSUserNotification*)notification {
   NSDictionary* notificationResponse =
       [NotificationResponseBuilder buildDismissedDictionary:notification];
-  NotificationPlatformBridgeMac::ProcessNotificationResponse(
-      notificationResponse);
+  ProcessMacNotificationResponse(notificationResponse);
 }
 
 // Overriden from _NSUserNotificationCenterDelegatePrivate.
@@ -477,8 +325,7 @@
   for (NSUserNotification* notification in notifications) {
     NSDictionary* notificationResponse =
         [NotificationResponseBuilder buildDismissedDictionary:notification];
-    NotificationPlatformBridgeMac::ProcessNotificationResponse(
-        notificationResponse);
+    ProcessMacNotificationResponse(notificationResponse);
   }
 }
 
@@ -591,8 +438,7 @@
 
 // NotificationReply:
 - (void)notificationClick:(NSDictionary*)notificationResponseData {
-  NotificationPlatformBridgeMac::ProcessNotificationResponse(
-      notificationResponseData);
+  ProcessMacNotificationResponse(notificationResponseData);
 }
 
 // Private methods:
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm b/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm
index 3cb4efb..1f0f2d9 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm
@@ -47,32 +47,6 @@
   }
 
  protected:
-  NSUserNotification* BuildNotification() {
-    base::scoped_nsobject<NotificationBuilder> builder(
-        [[NotificationBuilder alloc] initWithCloseLabel:@"Close"
-                                           optionsLabel:@"More"
-                                          settingsLabel:@"Settings"]);
-    [builder setTitle:@"Title"];
-    [builder setOrigin:@"https://www.miguel.com/"];
-    [builder setContextMessage:@""];
-    [builder setButtons:@"Button1" secondaryButton:@"Button2"];
-    [builder setTag:@"tag1"];
-    [builder setIcon:[NSImage imageNamed:@"NSApplicationIcon"]];
-    [builder setNotificationId:@"notification_id"];
-    [builder
-        setProfileId:base::SysUTF8ToNSString(
-                         NotificationPlatformBridge::GetProfileId(profile()))];
-    [builder setIncognito:profile()->IsOffTheRecord()];
-    [builder setCreatorPid:@(getpid())];
-    [builder setNotificationType:
-                 [NSNumber numberWithInteger:
-                               static_cast<int>(
-                                   NotificationHandler::Type::WEB_PERSISTENT)]];
-    [builder setShowSettingsButton:true];
-
-    return [builder buildUserNotification];
-  }
-
   static void StoreNotificationCount(int* out_notification_count,
                                      std::set<std::string> notifications,
                                      bool supports_synchronization) {
@@ -135,13 +109,6 @@
     return notification;
   }
 
-  NSMutableDictionary* BuildDefaultNotificationResponse() {
-    return [NSMutableDictionary
-        dictionaryWithDictionary:
-            [NotificationResponseBuilder
-                buildActivatedDictionary:BuildNotification()]];
-  }
-
   NSUserNotificationCenter* notification_center() {
     return notification_center_.get();
   }
@@ -153,76 +120,6 @@
   base::scoped_nsobject<StubAlertDispatcher> alert_dispatcher_;
 };
 
-TEST_F(NotificationPlatformBridgeMacTest, TestNotificationVerifyValidResponse) {
-  NSDictionary* response = BuildDefaultNotificationResponse();
-  EXPECT_TRUE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
-}
-
-TEST_F(NotificationPlatformBridgeMacTest, TestNotificationUnknownType) {
-  NSMutableDictionary* response = BuildDefaultNotificationResponse();
-  [response setValue:[NSNumber numberWithInt:210581]
-              forKey:notification_constants::kNotificationType];
-  EXPECT_FALSE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
-}
-
-TEST_F(NotificationPlatformBridgeMacTest,
-       TestNotificationVerifyUnknownOperation) {
-  NSMutableDictionary* response = BuildDefaultNotificationResponse();
-  [response setValue:[NSNumber numberWithInt:40782]
-              forKey:notification_constants::kNotificationOperation];
-  EXPECT_FALSE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
-}
-
-TEST_F(NotificationPlatformBridgeMacTest,
-       TestNotificationVerifyMissingOperation) {
-  NSMutableDictionary* response = BuildDefaultNotificationResponse();
-  [response removeObjectForKey:notification_constants::kNotificationOperation];
-  EXPECT_FALSE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
-}
-
-TEST_F(NotificationPlatformBridgeMacTest, TestNotificationVerifyNoProfileId) {
-  NSMutableDictionary* response = BuildDefaultNotificationResponse();
-  [response removeObjectForKey:notification_constants::kNotificationProfileId];
-  EXPECT_FALSE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
-}
-
-TEST_F(NotificationPlatformBridgeMacTest,
-       TestNotificationVerifyNoNotificationId) {
-  NSMutableDictionary* response = BuildDefaultNotificationResponse();
-  [response setValue:@"" forKey:notification_constants::kNotificationId];
-  EXPECT_FALSE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
-}
-
-TEST_F(NotificationPlatformBridgeMacTest, TestNotificationVerifyInvalidButton) {
-  NSMutableDictionary* response = BuildDefaultNotificationResponse();
-  [response setValue:[NSNumber numberWithInt:-5]
-              forKey:notification_constants::kNotificationButtonIndex];
-  EXPECT_FALSE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
-}
-
-TEST_F(NotificationPlatformBridgeMacTest,
-       TestNotificationVerifyMissingButtonIndex) {
-  NSMutableDictionary* response = BuildDefaultNotificationResponse();
-  [response
-      removeObjectForKey:notification_constants::kNotificationButtonIndex];
-  EXPECT_FALSE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
-}
-
-TEST_F(NotificationPlatformBridgeMacTest, TestNotificationVerifyOrigin) {
-  NSMutableDictionary* response = BuildDefaultNotificationResponse();
-  [response setValue:@"invalidorigin"
-              forKey:notification_constants::kNotificationOrigin];
-  EXPECT_FALSE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
-
-  // If however the origin is not present the response should be fine.
-  [response removeObjectForKey:notification_constants::kNotificationOrigin];
-  EXPECT_TRUE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
-
-  // Empty origin should be fine.
-  [response setValue:@"" forKey:notification_constants::kNotificationOrigin];
-  EXPECT_TRUE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
-}
-
 TEST_F(NotificationPlatformBridgeMacTest, TestDisplayNoButtons) {
   std::unique_ptr<Notification> notification =
       CreateBanner("Title", "Context", "https://gmail.com", nullptr, nullptr);
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac_unnotification.mm b/chrome/browser/notifications/notification_platform_bridge_mac_unnotification.mm
index 46f99056..bda723d 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac_unnotification.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac_unnotification.mm
@@ -12,9 +12,9 @@
 #include "chrome/browser/notifications/notification_platform_bridge_mac_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/cocoa/notifications/unnotification_builder_mac.h"
+#import "chrome/browser/ui/cocoa/notifications/unnotification_response_builder_mac.h"
 #include "ui/message_center/public/cpp/notification.h"
 
-@class NSNotification;
 @class UNMutableNotificationContent;
 @class UNUserNotificationCenter;
 
@@ -160,6 +160,9 @@
 - (void)userNotificationCenter:(UNUserNotificationCenter*)center
     didReceiveNotificationResponse:(UNNotificationResponse*)response
              withCompletionHandler:(void (^)(void))completionHandler {
+  NSDictionary* notificationResponse =
+      [UNNotificationResponseBuilder buildDictionary:response];
+  ProcessMacNotificationResponse(notificationResponse);
   completionHandler();
 }
 
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac_utils.h b/chrome/browser/notifications/notification_platform_bridge_mac_utils.h
index a53a422..67e9643 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac_utils.h
+++ b/chrome/browser/notifications/notification_platform_bridge_mac_utils.h
@@ -6,8 +6,13 @@
 #define CHROME_BROWSER_NOTIFICATIONS_NOTIFICATION_PLATFORM_BRIDGE_MAC_UTILS_H_
 
 #include "base/strings/string16.h"
+#include "chrome/browser/notifications/notification_common.h"
 #include "ui/message_center/public/cpp/notification.h"
 
+namespace message_center {
+class Notification;
+}  // namespace message_center
+
 // This file is a combination of methods that are shared between the macOS
 // notification bridges.
 
@@ -19,4 +24,12 @@
     const message_center::Notification& notification,
     bool requires_attribution);
 
+// Validates contents of the |response| dictionary as received from the system
+// when a notification gets activated.
+bool VerifyMacNotificationData(NSDictionary* response) WARN_UNUSED_RESULT;
+
+// Processes a notification response generated from a user action
+// (click close, etc.).
+void ProcessMacNotificationResponse(NSDictionary* response);
+
 #endif  // CHROME_BROWSER_NOTIFICATIONS_NOTIFICATION_PLATFORM_BRIDGE_MAC_UTILS_H_
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac_utils.mm b/chrome/browser/notifications/notification_platform_bridge_mac_utils.mm
index 1c00abc8..140d2af1 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac_utils.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac_utils.mm
@@ -5,12 +5,55 @@
 #include "chrome/browser/notifications/notification_platform_bridge_mac_utils.h"
 
 #include "base/i18n/number_formatting.h"
+#include "base/optional.h"
+#include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/notifications/notification_display_service_impl.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/cocoa/notifications/notification_constants_mac.h"
 #include "components/url_formatter/elide_url.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "third_party/blink/public/common/notifications/notification_constants.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
+namespace {
+
+// Loads the profile and process the Notification response
+void DoProcessMacNotificationResponse(
+    NotificationCommon::Operation operation,
+    NotificationHandler::Type type,
+    const std::string& profileId,
+    bool incognito,
+    const GURL& origin,
+    const std::string& notificationId,
+    const base::Optional<int>& actionIndex,
+    const base::Optional<base::string16>& reply,
+    const base::Optional<bool>& byUser) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // Profile ID can be empty for system notifications, which are not bound to a
+  // profile, but system notifications are transient and thus not handled by
+  // this NotificationPlatformBridge.
+  // When transient notifications are supported, this should route the
+  // notification response to the system NotificationDisplayService.
+  DCHECK(!profileId.empty());
+
+  ProfileManager* profileManager = g_browser_process->profile_manager();
+  DCHECK(profileManager);
+
+  profileManager->LoadProfile(
+      profileId, incognito,
+      base::Bind(&NotificationDisplayServiceImpl::ProfileLoadedCallback,
+                 operation, type, origin, notificationId, actionIndex, reply,
+                 byUser));
+}
+
+}  // namespace
+
 base::string16 CreateMacNotificationTitle(
     const message_center::Notification& notification) {
   base::string16 title;
@@ -26,10 +69,10 @@
 }
 
 base::string16 CreateMacNotificationContext(
-    bool is_persistent,
+    bool isPersistent,
     const message_center::Notification& notification,
-    bool requires_attribution) {
-  if (!requires_attribution)
+    bool requiresAttribution) {
+  if (!requiresAttribution)
     return notification.context_message();
 
   // Mac OS notifications don't provide a good way to elide the domain (or tell
@@ -44,14 +87,14 @@
   constexpr size_t kMaxDomainLengthAlert = 19;
   constexpr size_t kMaxDomainLengthBanner = 28;
 
-  size_t max_characters =
-      is_persistent ? kMaxDomainLengthAlert : kMaxDomainLengthBanner;
+  size_t maxCharacters =
+      isPersistent ? kMaxDomainLengthAlert : kMaxDomainLengthBanner;
 
   base::string16 origin = url_formatter::FormatOriginForSecurityDisplay(
       url::Origin::Create(notification.origin_url()),
       url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
 
-  if (origin.size() <= max_characters)
+  if (origin.size() <= maxCharacters)
     return origin;
 
   // Too long, use etld+1
@@ -66,3 +109,116 @@
 
   return etldplusone;
 }
+
+bool VerifyMacNotificationData(NSDictionary* response) {
+  if (![response
+          objectForKey:notification_constants::kNotificationButtonIndex] ||
+      ![response objectForKey:notification_constants::kNotificationOperation] ||
+      ![response objectForKey:notification_constants::kNotificationId] ||
+      ![response objectForKey:notification_constants::kNotificationProfileId] ||
+      ![response objectForKey:notification_constants::kNotificationIncognito] ||
+      ![response
+          objectForKey:notification_constants::kNotificationCreatorPid] ||
+      ![response objectForKey:notification_constants::kNotificationType]) {
+    LOG(ERROR) << "Missing required key";
+    return false;
+  }
+
+  NSNumber* buttonIndex =
+      [response objectForKey:notification_constants::kNotificationButtonIndex];
+  NSNumber* operation =
+      [response objectForKey:notification_constants::kNotificationOperation];
+  NSString* notificationId =
+      [response objectForKey:notification_constants::kNotificationId];
+  NSString* profileId =
+      [response objectForKey:notification_constants::kNotificationProfileId];
+  NSNumber* notificationType =
+      [response objectForKey:notification_constants::kNotificationType];
+  NSNumber* creatorPid =
+      [response objectForKey:notification_constants::kNotificationCreatorPid];
+
+  if (creatorPid.unsignedIntValue != static_cast<NSInteger>(getpid())) {
+    return false;
+  }
+
+  if (buttonIndex.intValue <
+          notification_constants::kNotificationInvalidButtonIndex ||
+      buttonIndex.intValue >=
+          static_cast<int>(blink::kNotificationMaxActions)) {
+    LOG(ERROR) << "Invalid number of buttons supplied " << buttonIndex.intValue;
+    return false;
+  }
+
+  if (operation.unsignedIntValue > NotificationCommon::OPERATION_MAX) {
+    LOG(ERROR) << operation.unsignedIntValue
+               << " does not correspond to a valid operation.";
+    return false;
+  }
+
+  if (notificationId.length <= 0) {
+    LOG(ERROR) << "Notification Id is empty";
+    return false;
+  }
+
+  if (profileId.length <= 0) {
+    LOG(ERROR) << "ProfileId not provided";
+    return false;
+  }
+
+  if (notificationType.unsignedIntValue >
+      static_cast<unsigned int>(NotificationHandler::Type::MAX)) {
+    LOG(ERROR) << notificationType.unsignedIntValue
+               << " Does not correspond to a valid operation.";
+    return false;
+  }
+
+  // Origin is not actually required but if it's there it should be a valid one.
+  NSString* origin =
+      [response objectForKey:notification_constants::kNotificationOrigin];
+  if (origin && origin.length) {
+    std::string notificationOrigin = base::SysNSStringToUTF8(origin);
+    GURL url(notificationOrigin);
+    if (!url.is_valid())
+      return false;
+  }
+
+  return true;
+}
+
+void ProcessMacNotificationResponse(NSDictionary* response) {
+  if (!VerifyMacNotificationData(response))
+    return;
+
+  NSNumber* buttonIndex =
+      [response objectForKey:notification_constants::kNotificationButtonIndex];
+  NSNumber* operation =
+      [response objectForKey:notification_constants::kNotificationOperation];
+
+  std::string notificationOrigin = base::SysNSStringToUTF8(
+      [response objectForKey:notification_constants::kNotificationOrigin]);
+  std::string notificationId = base::SysNSStringToUTF8(
+      [response objectForKey:notification_constants::kNotificationId]);
+  std::string profileId = base::SysNSStringToUTF8(
+      [response objectForKey:notification_constants::kNotificationProfileId]);
+  NSNumber* isIncognito =
+      [response objectForKey:notification_constants::kNotificationIncognito];
+  NSNumber* notificationType =
+      [response objectForKey:notification_constants::kNotificationType];
+
+  base::Optional<int> actionIndex;
+  if (buttonIndex.intValue !=
+      notification_constants::kNotificationInvalidButtonIndex) {
+    actionIndex = buttonIndex.intValue;
+  }
+
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE,
+      base::BindOnce(DoProcessMacNotificationResponse,
+                     static_cast<NotificationCommon::Operation>(
+                         operation.unsignedIntValue),
+                     static_cast<NotificationHandler::Type>(
+                         notificationType.unsignedIntValue),
+                     profileId, [isIncognito boolValue],
+                     GURL(notificationOrigin), notificationId, actionIndex,
+                     base::nullopt /* reply */, true /* byUser */));
+}
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac_utils_unittest.mm b/chrome/browser/notifications/notification_platform_bridge_mac_utils_unittest.mm
new file mode 100644
index 0000000..03b76a5c5
--- /dev/null
+++ b/chrome/browser/notifications/notification_platform_bridge_mac_utils_unittest.mm
@@ -0,0 +1,127 @@
+// Copyright 2020 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 <AppKit/AppKit.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "base/strings/sys_string_conversions.h"
+#include "chrome/browser/notifications/notification_platform_bridge.h"
+#include "chrome/browser/notifications/notification_platform_bridge_mac_utils.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/cocoa/notifications/notification_builder_mac.h"
+#include "chrome/browser/ui/cocoa/notifications/notification_constants_mac.h"
+#include "chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/message_center/public/cpp/notification.h"
+
+class NotificationPlatformBridgeMacUtilsTest
+    : public BrowserWithTestWindowTest {
+ public:
+  void SetUp() override {
+    BrowserWithTestWindowTest::SetUp();
+    response_ = BuildDefaultNotificationResponse();
+  }
+
+ protected:
+  NSMutableDictionary* BuildDefaultNotificationResponse() {
+    return [NSMutableDictionary
+        dictionaryWithDictionary:
+            [NotificationResponseBuilder
+                buildActivatedDictionary:BuildNotification()]];
+  }
+
+  NSMutableDictionary* response_;
+
+ private:
+  NSUserNotification* BuildNotification() {
+    base::scoped_nsobject<NotificationBuilder> builder(
+        [[NotificationBuilder alloc] initWithCloseLabel:@"Close"
+                                           optionsLabel:@"More"
+                                          settingsLabel:@"Settings"]);
+    [builder setTitle:@"Title"];
+    [builder setOrigin:@"https://www.moe.com/"];
+    [builder setContextMessage:@""];
+    [builder setButtons:@"Button1" secondaryButton:@"Button2"];
+    [builder setTag:@"tag1"];
+    [builder setIcon:[NSImage imageNamed:@"NSApplicationIcon"]];
+    [builder setNotificationId:@"notification_id"];
+    [builder
+        setProfileId:base::SysUTF8ToNSString(
+                         NotificationPlatformBridge::GetProfileId(profile()))];
+    [builder setIncognito:profile()->IsOffTheRecord()];
+    [builder setCreatorPid:@(getpid())];
+    [builder setNotificationType:
+                 [NSNumber numberWithInteger:
+                               static_cast<int>(
+                                   NotificationHandler::Type::WEB_PERSISTENT)]];
+    [builder setShowSettingsButton:true];
+
+    return [builder buildUserNotification];
+  }
+};
+
+TEST_F(NotificationPlatformBridgeMacUtilsTest,
+       TestNotificationVerifyValidResponse) {
+  EXPECT_TRUE(VerifyMacNotificationData(response_));
+}
+
+TEST_F(NotificationPlatformBridgeMacUtilsTest, TestNotificationUnknownType) {
+  [response_ setValue:[NSNumber numberWithInt:210581]
+               forKey:notification_constants::kNotificationType];
+  EXPECT_FALSE(VerifyMacNotificationData(response_));
+}
+
+TEST_F(NotificationPlatformBridgeMacUtilsTest,
+       TestNotificationVerifyUnknownOperation) {
+  [response_ setValue:[NSNumber numberWithInt:40782]
+               forKey:notification_constants::kNotificationOperation];
+  EXPECT_FALSE(VerifyMacNotificationData(response_));
+}
+
+TEST_F(NotificationPlatformBridgeMacUtilsTest,
+       TestNotificationVerifyMissingOperation) {
+  [response_ removeObjectForKey:notification_constants::kNotificationOperation];
+  EXPECT_FALSE(VerifyMacNotificationData(response_));
+}
+
+TEST_F(NotificationPlatformBridgeMacUtilsTest,
+       TestNotificationVerifyNoProfileId) {
+  [response_ removeObjectForKey:notification_constants::kNotificationProfileId];
+  EXPECT_FALSE(VerifyMacNotificationData(response_));
+}
+
+TEST_F(NotificationPlatformBridgeMacUtilsTest,
+       TestNotificationVerifyNoNotificationId) {
+  [response_ setValue:@"" forKey:notification_constants::kNotificationId];
+  EXPECT_FALSE(VerifyMacNotificationData(response_));
+}
+
+TEST_F(NotificationPlatformBridgeMacUtilsTest,
+       TestNotificationVerifyInvalidButton) {
+  [response_ setValue:[NSNumber numberWithInt:-5]
+               forKey:notification_constants::kNotificationButtonIndex];
+  EXPECT_FALSE(VerifyMacNotificationData(response_));
+}
+
+TEST_F(NotificationPlatformBridgeMacUtilsTest,
+       TestNotificationVerifyMissingButtonIndex) {
+  [response_
+      removeObjectForKey:notification_constants::kNotificationButtonIndex];
+  EXPECT_FALSE(VerifyMacNotificationData(response_));
+}
+
+TEST_F(NotificationPlatformBridgeMacUtilsTest, TestNotificationVerifyOrigin) {
+  [response_ setValue:@"invalidorigin"
+               forKey:notification_constants::kNotificationOrigin];
+  EXPECT_FALSE(VerifyMacNotificationData(response_));
+
+  // If however the origin is not present the response should be fine.
+  [response_ removeObjectForKey:notification_constants::kNotificationOrigin];
+  EXPECT_TRUE(VerifyMacNotificationData(response_));
+
+  // Empty origin should be fine.
+  [response_ setValue:@"" forKey:notification_constants::kNotificationOrigin];
+  EXPECT_TRUE(VerifyMacNotificationData(response_));
+}
diff --git a/chrome/browser/paint_preview/paint_preview_compositor_browsertest.cc b/chrome/browser/paint_preview/paint_preview_compositor_browsertest.cc
index 2524069..425dabc 100644
--- a/chrome/browser/paint_preview/paint_preview_compositor_browsertest.cc
+++ b/chrome/browser/paint_preview/paint_preview_compositor_browsertest.cc
@@ -39,14 +39,14 @@
     std::unique_ptr<PaintPreviewCompositorService, base::OnTaskRunnerDeleter>
         service) {
   return std::unique_ptr<PaintPreviewCompositorServiceImpl>(
-      reinterpret_cast<PaintPreviewCompositorServiceImpl*>(service.release()));
+      static_cast<PaintPreviewCompositorServiceImpl*>(service.release()));
 }
 
 std::unique_ptr<PaintPreviewCompositorClientImpl> ToCompositorClientImpl(
     std::unique_ptr<PaintPreviewCompositorClient, base::OnTaskRunnerDeleter>
         client) {
   return std::unique_ptr<PaintPreviewCompositorClientImpl>(
-      reinterpret_cast<PaintPreviewCompositorClientImpl*>(client.release()));
+      static_cast<PaintPreviewCompositorClientImpl*>(client.release()));
 }
 
 bool IsBoundAndConnected(PaintPreviewCompositorClientImpl* compositor) {
diff --git a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckBridge.java b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckBridge.java
index e17078c3..5dcaf18 100644
--- a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckBridge.java
+++ b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckBridge.java
@@ -64,14 +64,15 @@
     // TODO(crbug.com/1102025): Add call from native.
     void onCompromisedCredentialFound(String signonRealm, GURL origin, String username,
             String displayOrigin, String displayUsername, String password, String passwordChangeUrl,
-            String associatedApp, long creationTime, boolean hasAutoChangeButton) {
+            String associatedApp, long creationTime, boolean hasScript,
+            boolean hasAutoChangeButton) {
         assert signonRealm != null;
         assert displayOrigin != null;
         assert username != null;
         assert password != null;
         mPasswordCheckObserver.onCompromisedCredentialFound(new CompromisedCredential(signonRealm,
                 origin, username, displayOrigin, displayUsername, password, passwordChangeUrl,
-                associatedApp, creationTime, true, false, hasAutoChangeButton));
+                associatedApp, creationTime, true, false, hasScript, hasAutoChangeButton));
     }
 
     @CalledByNative
@@ -98,10 +99,11 @@
     private static void insertCredential(CompromisedCredential[] credentials, int index,
             String signonRealm, GURL origin, String username, String displayOrigin,
             String displayUsername, String password, String passwordChangeUrl, String associatedApp,
-            long creationTime, boolean leaked, boolean phished, boolean hasAutoChangeButton) {
+            long creationTime, boolean leaked, boolean phished, boolean hasScript,
+            boolean hasAutoChangeButton) {
         credentials[index] = new CompromisedCredential(signonRealm, origin, username, displayOrigin,
                 displayUsername, password, passwordChangeUrl, associatedApp, creationTime, leaked,
-                phished, hasAutoChangeButton);
+                phished, hasScript, hasAutoChangeButton);
     }
 
     /**
diff --git a/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/CompromisedCredential.java b/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/CompromisedCredential.java
index 76b8ad9..af17b83 100644
--- a/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/CompromisedCredential.java
+++ b/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/CompromisedCredential.java
@@ -31,15 +31,16 @@
                     final String passwordChangeUrl = in.readString();
                     final String associatedApp = in.readString();
                     final long creationTime = in.readLong();
-                    boolean[] boolArguments = new boolean[3];
+                    boolean[] boolArguments = new boolean[4];
                     in.readBooleanArray(boolArguments);
                     final boolean leaked = boolArguments[0];
                     final boolean phished = boolArguments[1];
-                    final boolean hasAutoChangeButton = boolArguments[2];
+                    final boolean hasScript = boolArguments[2];
+                    final boolean hasAutoChangeButton = boolArguments[3];
 
                     return new CompromisedCredential(signonRealm, origin, username, displayOrigin,
                             displayUsername, password, passwordChangeUrl, associatedApp,
-                            creationTime, leaked, phished, hasAutoChangeButton);
+                            creationTime, leaked, phished, hasScript, hasAutoChangeButton);
                 }
 
                 @Override
@@ -59,6 +60,7 @@
     private final long mCreationTime;
     private final boolean mLeaked;
     private final boolean mPhished;
+    private final boolean mHasScript;
     private final boolean mHasAutoChangeButton;
 
     /**
@@ -75,12 +77,14 @@
      *        time at which the compromised credential was first found to be compromised during
      *        a check.
      * @param phished True iff the credential was entered on an unsafe site.
-     * @param hasAutoChangeButton True iff the credential can be automatically fixed.
+     * @param hasScript True iff there is a script to automatically fix the credential.
+     * @param hasAutoChangeButton True iff the button to automatically change the credential should
+     *         be shown.
      */
     public CompromisedCredential(String signonRealm, GURL origin, String username,
             String displayOrigin, String displayUsername, String password, String passwordChangeUrl,
             String associatedApp, long creationTime, boolean leaked, boolean phished,
-            boolean hasAutoChangeButton) {
+            boolean hasScript, boolean hasAutoChangeButton) {
         assert origin != null : "Credential origin is null! Pass an empty one instead.";
         assert signonRealm != null;
         assert passwordChangeUrl != null : "Change URL may be empty but not null!";
@@ -89,6 +93,8 @@
                 || !associatedApp.isEmpty()
             : "Change URL and app name may not be empty at the same time!";
         assert leaked || phished : "A compromised credential must be leaked or phished!";
+        assert hasScript
+                || !hasAutoChangeButton : "Auto change button cannot be shown without a script!";
         mSignonRealm = signonRealm;
         mOrigin = origin;
         mUsername = username;
@@ -100,6 +106,7 @@
         mCreationTime = creationTime;
         mLeaked = leaked;
         mPhished = phished;
+        mHasScript = hasScript;
         mHasAutoChangeButton = hasAutoChangeButton;
     }
 
@@ -140,6 +147,9 @@
     public boolean isPhished() {
         return mPhished;
     }
+    public boolean hasScript() {
+        return mHasScript;
+    }
     public boolean hasAutoChangeButton() {
         return mHasAutoChangeButton;
     }
@@ -156,6 +166,7 @@
                 && mPasswordChangeUrl.equals(that.mPasswordChangeUrl)
                 && mAssociatedApp.equals(that.mAssociatedApp) && mCreationTime == that.mCreationTime
                 && mLeaked == that.mLeaked && mPhished == that.mPhished
+                && mHasScript == that.mHasScript
                 && mHasAutoChangeButton == that.mHasAutoChangeButton;
     }
 
@@ -167,14 +178,15 @@
                 + ", displayUsername='" + mDisplayUsername + '\'' + ", password='" + mPassword
                 + '\'' + ", passwordChangeUrl='" + mPasswordChangeUrl + '\'' + ", associatedApp='"
                 + mAssociatedApp + '\'' + ", creationTime=" + mCreationTime + ", leaked=" + mLeaked
-                + ", phished=" + mPhished + ", hasAutoChangeButton=" + mHasAutoChangeButton + '}';
+                + ", phished=" + mPhished + ", hasScript=" + mHasScript
+                + ", hasAutoChangeButton=" + mHasAutoChangeButton + '}';
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mSignonRealm, mOrigin.getPossiblyInvalidSpec(), mUsername,
                 mDisplayOrigin, mDisplayUsername, mPassword, mPasswordChangeUrl, mAssociatedApp,
-                mCreationTime, mLeaked, mPhished, mHasAutoChangeButton);
+                mCreationTime, mLeaked, mPhished, mHasScript, mHasAutoChangeButton);
     }
 
     @Override
@@ -188,7 +200,8 @@
         parcel.writeString(mPasswordChangeUrl);
         parcel.writeString(mAssociatedApp);
         parcel.writeLong(mCreationTime);
-        parcel.writeBooleanArray(new boolean[] {mLeaked, mPhished, mHasAutoChangeButton});
+        parcel.writeBooleanArray(
+                new boolean[] {mLeaked, mPhished, mHasScript, mHasAutoChangeButton});
     }
 
     @Override
diff --git a/chrome/browser/password_check/android/javatests/src/org/chromium/chrome/browser/password_check/PasswordCheckEditViewTest.java b/chrome/browser/password_check/android/javatests/src/org/chromium/chrome/browser/password_check/PasswordCheckEditViewTest.java
index ab99d15..0c1396f 100644
--- a/chrome/browser/password_check/android/javatests/src/org/chromium/chrome/browser/password_check/PasswordCheckEditViewTest.java
+++ b/chrome/browser/password_check/android/javatests/src/org/chromium/chrome/browser/password_check/PasswordCheckEditViewTest.java
@@ -65,10 +65,10 @@
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 @EnableFeatures({ChromeFeatureList.PASSWORD_CHECK})
 public class PasswordCheckEditViewTest {
-    private static final CompromisedCredential ANA =
-            new CompromisedCredential("https://some-url.com/signin",
-                    new GURL("https://some-url.com/"), "Ana", "some-url.com", "Ana", "password",
-                    "https://some-url.com/.well-known/change-password", "", 1, true, false, false);
+    private static final CompromisedCredential ANA = new CompromisedCredential(
+            "https://some-url.com/signin", new GURL("https://some-url.com/"), "Ana", "some-url.com",
+            "Ana", "password", "https://some-url.com/.well-known/change-password", "", 1, true,
+            false, false, false);
 
     private PasswordCheckEditFragmentView mPasswordCheckEditView;
 
diff --git a/chrome/browser/password_check/android/javatests/src/org/chromium/chrome/browser/password_check/PasswordCheckViewTest.java b/chrome/browser/password_check/android/javatests/src/org/chromium/chrome/browser/password_check/PasswordCheckViewTest.java
index 0841e37..eb3e87f 100644
--- a/chrome/browser/password_check/android/javatests/src/org/chromium/chrome/browser/password_check/PasswordCheckViewTest.java
+++ b/chrome/browser/password_check/android/javatests/src/org/chromium/chrome/browser/password_check/PasswordCheckViewTest.java
@@ -102,26 +102,26 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class PasswordCheckViewTest {
-    private static final CompromisedCredential ANA =
-            new CompromisedCredential("https://some-url.com/signin",
-                    new GURL("https://some-url.com/"), "Ana", "some-url.com", "Ana", "password",
-                    "https://some-url.com/.well-known/change-password", "", 1, true, false, false);
-    private static final CompromisedCredential PHISHED =
-            new CompromisedCredential("http://example.com/signin", new GURL("http://example.com/"),
-                    "", "http://example.com", "(No username)", "DoSomething",
-                    "http://example.com/.well-known/change-password", "", 1, false, true, false);
+    private static final CompromisedCredential ANA = new CompromisedCredential(
+            "https://some-url.com/signin", new GURL("https://some-url.com/"), "Ana", "some-url.com",
+            "Ana", "password", "https://some-url.com/.well-known/change-password", "", 1, true,
+            false, false, false);
+    private static final CompromisedCredential PHISHED = new CompromisedCredential(
+            "http://example.com/signin", new GURL("http://example.com/"), "", "http://example.com",
+            "(No username)", "DoSomething", "http://example.com/.well-known/change-password", "", 1,
+            false, true, false, false);
     private static final CompromisedCredential LEAKED =
             new CompromisedCredential("https://some-other-url.com/signin",
                     new GURL("https://some-other-url.com/"), "AZiegler", "some-other-url.com",
-                    "AZiegler", "N0M3rcy", "", "com.other.package", 1, true, false, false);
+                    "AZiegler", "N0M3rcy", "", "com.other.package", 1, true, false, false, false);
     private static final CompromisedCredential LEAKED_AND_PHISHED =
             new CompromisedCredential("https://super-important.com/signin",
                     new GURL("https://super-important.com/"), "HSong", "super-important.com",
-                    "HSong", "N3rfTh1s", "", "com.important.super", 1, true, true, false);
-    private static final CompromisedCredential SCRIPTED =
-            new CompromisedCredential("https://script.com/signin", new GURL("https://script.com/"),
-                    "Charlie", "script.com", "Charlie", "secret",
-                    "https://script.com/.well-known/change-password", "", 1, true, false, true);
+                    "HSong", "N3rfTh1s", "", "com.important.super", 1, true, true, false, false);
+    private static final CompromisedCredential SCRIPTED = new CompromisedCredential(
+            "https://script.com/signin", new GURL("https://script.com/"), "Charlie", "script.com",
+            "Charlie", "secret", "https://script.com/.well-known/change-password", "", 1, true,
+            false, true, true);
 
     private static final int LEAKS_COUNT = 2;
 
diff --git a/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java b/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java
index 043bc85..1cbf2dee 100644
--- a/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java
+++ b/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java
@@ -84,10 +84,11 @@
 public class PasswordCheckControllerTest {
     private static final CompromisedCredential ANA =
             new CompromisedCredential("https://m.a.xyz/signin", mock(GURL.class), "Ana", "m.a.xyz",
-                    "Ana", "password", "", "xyz.a.some.package", 2, true, false, false);
-    private static final CompromisedCredential BOB = new CompromisedCredential(
-            "http://www.b.ch/signin", mock(GURL.class), "", "http://www.b.ch", "(No username)",
-            "DoneSth", "http://www.b.ch/.well-known/change-password", "", 1, true, false, true);
+                    "Ana", "password", "", "xyz.a.some.package", 2, true, false, false, false);
+    private static final CompromisedCredential BOB =
+            new CompromisedCredential("http://www.b.ch/signin", mock(GURL.class), "",
+                    "http://www.b.ch", "(No username)", "DoneSth",
+                    "http://www.b.ch/.well-known/change-password", "", 1, true, false, true, true);
     private static final Pair<Integer, Integer> PROGRESS_UPDATE = new Pair<>(2, 19);
     private static final String PASSWORD_CHECK_REFERRER_HISTOGRAM =
             "PasswordManager.BulkCheck.PasswordCheckReferrerAndroid";
@@ -616,7 +617,7 @@
     private CompromisedCredential makeCredential(
             String origin, String username, long creationTime, boolean leaked, boolean phished) {
         return new CompromisedCredential(origin, mock(GURL.class), username, origin, username,
-                "password", origin, new String(), creationTime, leaked, phished, false);
+                "password", origin, new String(), creationTime, leaked, phished, false, false);
     }
 
     private PropertyModel getHeaderModel() {
diff --git a/chrome/browser/password_check/android/password_check_bridge.cc b/chrome/browser/password_check/android/password_check_bridge.cc
index 5119067..30349cb 100644
--- a/chrome/browser/password_check/android/password_check_bridge.cc
+++ b/chrome/browser/password_check/android/password_check_bridge.cc
@@ -90,7 +90,7 @@
          password_manager::InsecureCredentialTypeFlags::kCredentialLeaked),
         (credential.insecure_type ==
          password_manager::InsecureCredentialTypeFlags::kCredentialPhished),
-        credential.has_auto_change_button);
+        credential.has_script, credential.has_auto_change_button);
   }
 }
 
diff --git a/chrome/browser/password_check/android/password_check_manager.cc b/chrome/browser/password_check/android/password_check_manager.cc
index 4dcce61b..e0f72afa 100644
--- a/chrome/browser/password_check/android/password_check_manager.cc
+++ b/chrome/browser/password_check/android/password_check_manager.cc
@@ -224,10 +224,13 @@
       credential.signon_realm);
 
   ui_credential.display_username = GetDisplayUsername(credential.username);
-  ui_credential.has_auto_change_button =
+  ui_credential.has_script =
       !credential.username.empty() && ShouldOfferAutomaticPasswordChange() &&
       password_script_fetcher_->IsScriptAvailable(
           url::Origin::Create(credential.url.GetOrigin()));
+  // TODO(crbug.com/1132230): Implement separate logic for scripts fetching and
+  // auto buttons. For now, has_auto_change_button <=> has_script.
+  ui_credential.has_auto_change_button = ui_credential.has_script;
 
   if (facet.IsValidAndroidFacetURI()) {
     const PasswordForm& android_form =
diff --git a/chrome/browser/password_check/android/password_check_manager.h b/chrome/browser/password_check/android/password_check_manager.h
index 9243883..f70c270 100644
--- a/chrome/browser/password_check/android/password_check_manager.h
+++ b/chrome/browser/password_check/android/password_check_manager.h
@@ -51,6 +51,7 @@
     base::string16 display_origin;
     std::string package_name;
     std::string change_password_url;
+    bool has_script = false;
     bool has_auto_change_button = false;
   };
 
diff --git a/chrome/browser/profiles/profiles_state.cc b/chrome/browser/profiles/profiles_state.cc
index 8319bd9..b338e9f 100644
--- a/chrome/browser/profiles/profiles_state.cc
+++ b/chrome/browser/profiles/profiles_state.cc
@@ -274,20 +274,6 @@
 #endif
   return false;
 }
-
-#if !defined(OS_CHROMEOS)
-base::string16 GetDefaultNameForNewSignedInProfile(
-    const AccountInfo& account_info) {
-  DCHECK(account_info.IsValid());
-  bool is_consumer = account_info.hosted_domain.empty() ||
-                     account_info.hosted_domain == kNoHostedDomainFound;
-  if (is_consumer)
-    return base::UTF8ToUTF16(account_info.given_name);
-  return l10n_util::GetStringUTF16(
-      IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_PROFILE_NAME);
-}
-#endif  // !defined(OS_CHROMEOS)
-
 #endif  // !defined(OS_ANDROID)
 
 }  // namespace profiles
diff --git a/chrome/browser/profiles/profiles_state.h b/chrome/browser/profiles/profiles_state.h
index 367ec12..3a1d0713 100644
--- a/chrome/browser/profiles/profiles_state.h
+++ b/chrome/browser/profiles/profiles_state.h
@@ -16,7 +16,6 @@
 #include "chrome/browser/profiles/avatar_menu.h"
 #endif
 
-struct AccountInfo;
 class Browser;
 class PrefRegistrySimple;
 class Profile;
@@ -109,14 +108,6 @@
 
 // Returns whether public session restrictions are enabled.
 bool ArePublicSessionRestrictionsEnabled();
-
-#if !defined(OS_CHROMEOS)
-// Returns the default name for a new signed-in profile, based on
-// `account_info`.
-base::string16 GetDefaultNameForNewSignedInProfile(
-    const AccountInfo& account_info);
-#endif  // !defined(OS_CHROMEOS)
-
 #endif  // !defined(OS_ANDROID)
 
 }  // namespace profiles
diff --git a/chrome/browser/renderer_context_menu/copy_link_to_text_menu_observer.cc b/chrome/browser/renderer_context_menu/copy_link_to_text_menu_observer.cc
new file mode 100644
index 0000000..5dc9cf8
--- /dev/null
+++ b/chrome/browser/renderer_context_menu/copy_link_to_text_menu_observer.cc
@@ -0,0 +1,88 @@
+// Copyright 2020 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/renderer_context_menu/copy_link_to_text_menu_observer.h"
+
+#include <memory>
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/renderer_context_menu/render_view_context_menu_proxy.h"
+#include "content/public/browser/context_menu_params.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/clipboard/scoped_clipboard_writer.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+constexpr char kTextFragmentUrlClassifier[] = "#:~:text=";
+}
+
+CopyLinkToTextMenuObserver::CopyLinkToTextMenuObserver(
+    RenderViewContextMenuProxy* proxy)
+    : proxy_(proxy) {}
+CopyLinkToTextMenuObserver::~CopyLinkToTextMenuObserver() = default;
+
+void CopyLinkToTextMenuObserver::InitMenu(
+    const content::ContextMenuParams& params) {
+  url_ = params.page_url;
+  selected_text_ = params.selection_text;
+
+  proxy_->AddMenuItem(
+      IDC_CONTENT_CONTEXT_COPYLINKTOTEXT,
+      l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_COPYLINKTOTEXT));
+}
+
+bool CopyLinkToTextMenuObserver::IsCommandIdSupported(int command_id) {
+  return command_id == IDC_CONTENT_CONTEXT_COPYLINKTOTEXT;
+}
+
+bool CopyLinkToTextMenuObserver::IsCommandIdEnabled(int command_id) {
+  // This should only be called for the command for copying link to text.
+  DCHECK(IsCommandIdSupported(command_id));
+  return true;
+}
+
+void CopyLinkToTextMenuObserver::ExecuteCommand(int command_id) {
+  // This should only be called for the command for copying link to text.
+  DCHECK(IsCommandIdSupported(command_id));
+
+  if (generated_selector_for_testing_.has_value()) {
+    OnGeneratedSelector(nullptr, generated_selector_for_testing_.value());
+    return;
+  }
+
+  // Make a call to the renderer to generate a string that uniquely represents
+  // the selected text and any context around the text to distinguish it from
+  // the rest of the contents. GenerateSelector will call a callback with
+  // the generated string if it succeeds or an empty string if it fails.
+  content::RenderFrameHost* main_frame =
+      proxy_->GetWebContents()->GetMainFrame();
+  if (main_frame) {
+    main_frame->GetRemoteInterfaces()->GetInterface(
+        remote_.BindNewPipeAndPassReceiver());
+    remote_->GenerateSelector(
+        base::BindOnce(&CopyLinkToTextMenuObserver::OnGeneratedSelector,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       std::make_unique<ui::ClipboardDataEndpoint>(
+                           main_frame->GetLastCommittedOrigin())));
+  }
+}
+
+void CopyLinkToTextMenuObserver::OnGeneratedSelector(
+    std::unique_ptr<ui::ClipboardDataEndpoint> endpoint,
+    const std::string& selector) {
+  ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste,
+                                std::move(endpoint));
+  std::string url = url_.spec();
+  if (!selector.empty())
+    url += kTextFragmentUrlClassifier + selector;
+  scw.WriteText(selected_text_ + base::UTF8ToUTF16("\n" + url));
+}
+
+void CopyLinkToTextMenuObserver::OverrideGeneratedSelectorForTesting(
+    const std::string& selector) {
+  generated_selector_for_testing_ = selector;
+}
diff --git a/chrome/browser/renderer_context_menu/copy_link_to_text_menu_observer.h b/chrome/browser/renderer_context_menu/copy_link_to_text_menu_observer.h
new file mode 100644
index 0000000..9a36e6eb
--- /dev/null
+++ b/chrome/browser/renderer_context_menu/copy_link_to_text_menu_observer.h
@@ -0,0 +1,49 @@
+// Copyright 2020 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_RENDERER_CONTEXT_MENU_COPY_LINK_TO_TEXT_MENU_OBSERVER_H_
+#define CHROME_BROWSER_RENDERER_CONTEXT_MENU_COPY_LINK_TO_TEXT_MENU_OBSERVER_H_
+
+#include "components/renderer_context_menu/render_view_context_menu_observer.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/mojom/link_to_text/link_to_text.mojom.h"
+#include "url/gurl.h"
+
+class RenderViewContextMenuProxy;
+namespace ui {
+class ClipboardDataEndpoint;
+}
+
+// A class that implements the menu item for copying selected text and a link
+// to the selected text to the user's clipboard.
+class CopyLinkToTextMenuObserver : public RenderViewContextMenuObserver {
+ public:
+  explicit CopyLinkToTextMenuObserver(RenderViewContextMenuProxy* proxy);
+  CopyLinkToTextMenuObserver(const CopyLinkToTextMenuObserver&) = delete;
+  CopyLinkToTextMenuObserver& operator=(const CopyLinkToTextMenuObserver&) =
+      delete;
+  ~CopyLinkToTextMenuObserver() override;
+
+  // RenderViewContextMenuObserver.
+  void InitMenu(const content::ContextMenuParams& params) override;
+  bool IsCommandIdSupported(int command_id) override;
+  bool IsCommandIdEnabled(int command_id) override;
+  void ExecuteCommand(int command_id) override;
+
+  void OnGeneratedSelector(std::unique_ptr<ui::ClipboardDataEndpoint> endpoint,
+                           const std::string& selector);
+  // Convenience method for overriding the generated selector to bypass making
+  // calls to the remote interface during tests.
+  void OverrideGeneratedSelectorForTesting(const std::string& selector);
+
+ private:
+  mojo::Remote<blink::mojom::TextFragmentSelectorProducer> remote_;
+  RenderViewContextMenuProxy* proxy_;
+  GURL url_;
+  base::string16 selected_text_;
+  base::Optional<std::string> generated_selector_for_testing_;
+  base::WeakPtrFactory<CopyLinkToTextMenuObserver> weak_ptr_factory_{this};
+};
+
+#endif  // CHROME_BROWSER_RENDERER_CONTEXT_MENU_COPY_LINK_TO_TEXT_MENU_OBSERVER_H_
diff --git a/chrome/browser/renderer_context_menu/copy_link_to_text_menu_observer_interactive_uitest.cc b/chrome/browser/renderer_context_menu/copy_link_to_text_menu_observer_interactive_uitest.cc
new file mode 100644
index 0000000..ee20311
--- /dev/null
+++ b/chrome/browser/renderer_context_menu/copy_link_to_text_menu_observer_interactive_uitest.cc
@@ -0,0 +1,99 @@
+// Copyright 2020 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/renderer_context_menu/copy_link_to_text_menu_observer.h"
+
+#include "base/macros.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/renderer_context_menu/mock_render_view_context_menu.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/context_menu_params.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/clipboard/clipboard.h"
+
+namespace {
+
+class CopyLinkToTextMenuObserverTest : public InProcessBrowserTest {
+ public:
+  CopyLinkToTextMenuObserverTest();
+
+  void SetUp() override { InProcessBrowserTest::SetUp(); }
+  void SetUpOnMainThread() override { Reset(false); }
+  void TearDownOnMainThread() override {
+    observer_.reset();
+    menu_.reset();
+  }
+
+  void Reset(bool incognito) {
+    menu_ = std::make_unique<MockRenderViewContextMenu>(incognito);
+    observer_ = std::make_unique<CopyLinkToTextMenuObserver>(menu_.get());
+    menu_->SetObserver(observer_.get());
+  }
+
+  void InitMenu(content::ContextMenuParams params) {
+    observer_->InitMenu(params);
+  }
+
+  ~CopyLinkToTextMenuObserverTest() override;
+  MockRenderViewContextMenu* menu() { return menu_.get(); }
+  CopyLinkToTextMenuObserver* observer() { return observer_.get(); }
+
+ private:
+  std::unique_ptr<CopyLinkToTextMenuObserver> observer_;
+  std::unique_ptr<MockRenderViewContextMenu> menu_;
+  DISALLOW_COPY_AND_ASSIGN(CopyLinkToTextMenuObserverTest);
+};
+
+CopyLinkToTextMenuObserverTest::CopyLinkToTextMenuObserverTest() = default;
+CopyLinkToTextMenuObserverTest::~CopyLinkToTextMenuObserverTest() = default;
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(CopyLinkToTextMenuObserverTest, AddsMenuItem) {
+  content::ContextMenuParams params;
+  InitMenu(params);
+  EXPECT_EQ(1u, menu()->GetMenuSize());
+  MockRenderViewContextMenu::MockMenuItem item;
+  menu()->GetMenuItem(0, &item);
+  EXPECT_EQ(IDC_CONTENT_CONTEXT_COPYLINKTOTEXT, item.command_id);
+  EXPECT_TRUE(item.enabled);
+  EXPECT_FALSE(item.checked);
+  EXPECT_FALSE(item.hidden);
+}
+
+IN_PROC_BROWSER_TEST_F(CopyLinkToTextMenuObserverTest, CopiesLinkToText) {
+  content::BrowserTestClipboardScope test_clipboard_scope;
+  content::ContextMenuParams params;
+  params.page_url = GURL("http://foo.com/");
+  params.selection_text = base::UTF8ToUTF16("hello world");
+  observer()->OverrideGeneratedSelectorForTesting("hello%20world");
+  InitMenu(params);
+  menu()->ExecuteCommand(IDC_CONTENT_CONTEXT_COPYLINKTOTEXT, 0);
+
+  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
+  base::string16 text;
+  clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, nullptr, &text);
+  EXPECT_EQ(
+      base::UTF8ToUTF16("hello world\nhttp://foo.com/#:~:text=hello%20world"),
+      text);
+}
+
+IN_PROC_BROWSER_TEST_F(CopyLinkToTextMenuObserverTest,
+                       CopiesLinkForEmptySelector) {
+  content::BrowserTestClipboardScope test_clipboard_scope;
+  content::ContextMenuParams params;
+  params.page_url = GURL("http://foo.com/");
+  params.selection_text = base::UTF8ToUTF16("hello world");
+  observer()->OverrideGeneratedSelectorForTesting("");
+  InitMenu(params);
+  menu()->ExecuteCommand(IDC_CONTENT_CONTEXT_COPYLINKTOTEXT, 0);
+
+  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
+  base::string16 text;
+  clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, nullptr, &text);
+  EXPECT_EQ(base::UTF8ToUTF16("hello world\nhttp://foo.com/"), text);
+}
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 2f70596..b5bc18f 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -55,6 +55,7 @@
 #include "chrome/browser/profiles/profile_window.h"
 #include "chrome/browser/renderer_context_menu/accessibility_labels_menu_observer.h"
 #include "chrome/browser/renderer_context_menu/context_menu_content_type_factory.h"
+#include "chrome/browser/renderer_context_menu/copy_link_to_text_menu_observer.h"
 #include "chrome/browser/renderer_context_menu/spelling_menu_observer.h"
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -369,13 +370,14 @@
        {IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES, 109},
        {IDC_CONTENT_CONTEXT_GENERATE_QR_CODE, 110},
        {IDC_CONTENT_CLIPBOARD_HISTORY_MENU, 111},
+       {IDC_CONTENT_CONTEXT_COPYLINKTOTEXT, 112},
        // To add new items:
        //   - Add one more line above this comment block, using the UMA value
        //     from the line below this comment block.
        //   - Increment the UMA value in that latter line.
        //   - Add the new item to the RenderViewContextMenuItem enum in
        //     tools/metrics/histograms/enums.xml.
-       {0, 112}});
+       {0, 113}});
 
   // These UMA values are for the the ContextMenuOptionDesktop enum, used for
   // the ContextMenu.SelectedOptionDesktop histograms.
@@ -400,13 +402,14 @@
        {IDC_CONTENT_CONTEXT_CUT, 17},
        {IDC_CONTENT_CONTEXT_PASTE, 18},
        {IDC_CONTENT_CONTEXT_GOTOURL, 19},
+       {IDC_CONTENT_CONTEXT_COPYLINKTOTEXT, 20},
        // To add new items:
        //   - Add one more line above this comment block, using the UMA value
        //     from the line below this comment block.
        //   - Increment the UMA value in that latter line.
        //   - Add the new item to the ContextMenuOptionDesktop enum in
        //     tools/metrics/histograms/enums.xml.
-       {0, 20}});
+       {0, 21}});
 
   return *(type == UmaEnumIdLookupType::GeneralEnumId ? kGeneralMap
                                                       : kSpecificMap);
@@ -1573,8 +1576,12 @@
 }
 
 void RenderViewContextMenu::AppendCopyLinkToTextItem() {
-  menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYLINKTOTEXT,
-                                  IDS_CONTENT_CONTEXT_COPYLINKTOTEXT);
+  if (!copy_link_to_text_menu_observer_) {
+    copy_link_to_text_menu_observer_ =
+        std::make_unique<CopyLinkToTextMenuObserver>(this);
+    observers_.AddObserver(copy_link_to_text_menu_observer_.get());
+    copy_link_to_text_menu_observer_->InitMenu(params_);
+  }
 }
 
 void RenderViewContextMenu::AppendPrintItem() {
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h
index 20998384..cd477e3 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h
@@ -35,6 +35,7 @@
 
 class AccessibilityLabelsMenuObserver;
 class ClickToCallContextMenuObserver;
+class CopyLinkToTextMenuObserver;
 class PrintPreviewContextMenuObserver;
 class Profile;
 class QuickAnswersMenuObserver;
@@ -295,6 +296,8 @@
   std::unique_ptr<PrintPreviewContextMenuObserver> print_preview_menu_observer_;
 #endif
 
+  std::unique_ptr<CopyLinkToTextMenuObserver> copy_link_to_text_menu_observer_;
+
   // In the case of a MimeHandlerView this will point to the WebContents that
   // embeds the MimeHandlerViewGuest. Otherwise this will be the same as
   // |source_web_contents_|.
diff --git a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.html b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.html
index ba266b4f..43bb9cdb 100644
--- a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.html
+++ b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.html
@@ -173,6 +173,7 @@
   <div id="start">
     <cr-icon-button id="sidenavToggle" iron-icon="cr20:menu"
         title="$i18n{menu}" aria-label="$i18n{menu}"
+        aria-expanded$="[[getAriaExpanded_(sidenavCollapsed)]]"
 <if expr="chromeos">
         disabled="[[annotationMode]]"
 </if>
diff --git a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.js b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.js
index 27d61b8..c18e0591 100644
--- a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.js
+++ b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.js
@@ -76,6 +76,7 @@
         observer: 'viewportZoomChanged_',
       },
 
+      sidenavCollapsed: Boolean,
       twoUpViewEnabled: Boolean,
 
       moreMenuOpen_: {
@@ -111,6 +112,9 @@
   constructor() {
     super();
 
+    /** @type {boolean} */
+    this.sidenavCollapsed = false;
+
     /** @private {!FittingType} */
     this.fittingType_ = FittingType.FIT_TO_PAGE;
 
@@ -230,6 +234,11 @@
     return checked ? 'true' : 'false';
   }
 
+  /** @return {string} */
+  getAriaExpanded_() {
+    return this.sidenavCollapsed ? 'false' : 'true';
+  }
+
   /** @private */
   toggleTwoPageViewClick_() {
     const newTwoUpViewEnabled = !this.twoUpViewEnabled;
diff --git a/chrome/browser/resources/pdf/pdf_viewer.html b/chrome/browser/resources/pdf/pdf_viewer.html
index 730c425b..591b7a4 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.html
+++ b/chrome/browser/resources/pdf/pdf_viewer.html
@@ -158,6 +158,7 @@
     pdf-form-save-enabled="[[pdfFormSaveEnabled_]]"
     printing-enabled="[[printingEnabled_]]" rotated="[[rotated_]]"
     is-form-field-focused="[[isFormFieldFocused_]]"
+    sidenav-collapsed="[[sidenavCollapsed_]]"
     two-up-view-enabled="[[twoUpViewEnabled_]]"
     viewport-zoom="[[viewportZoom_]]"
 <if expr="chromeos">
diff --git a/chrome/browser/resources/pdf/pdf_viewer.js b/chrome/browser/resources/pdf/pdf_viewer.js
index ab8e305..953788e0 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.js
+++ b/chrome/browser/resources/pdf/pdf_viewer.js
@@ -141,6 +141,7 @@
 
       title_: String,
 
+      sidenavCollapsed_: Boolean,
       twoUpViewEnabled_: Boolean,
 
       isFormFieldFocused_: Boolean,
diff --git a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
index 413f36d..beb2373 100644
--- a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
+++ b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
@@ -1,8 +1,4 @@
-    <style include="settings-shared passwords-shared">
-      cr-input {
-        --cr-input-error-display: none;
-      }
-
+<style include="settings-shared passwords-shared">
       cr-input:not(:last-of-type) {
         margin-bottom: var(--cr-form-field-bottom-spacing);
       }
@@ -27,14 +23,15 @@
             value="[[entry.urls.link]]" on-blur="onInputBlur_" readonly>
         </cr-input>
         <cr-input id="usernameInput" label="$i18n{editPasswordUsernameLabel}"
-            value="[[entry.username]]" on-blur="onInputBlur_" readonly>
+            readonly="[[!isEditDialog_]]" invalid="[[usernameInputInvalid_]]"
+            on-value-changed="validateUsername_" value="[[entry.username]]"
+            error-message="$i18n{usernameAlreadyUsed}">
         </cr-input>
         <cr-input id="passwordInput" label="$i18n{editPasswordPasswordLabel}"
             type="[[getPasswordInputType_(isPasswordVisible_, entry.password)]]"
-            value="[[getPassword_(entry.password)]]" on-blur="onInputBlur_"
-            class="password-input" readonly="[[!isEditDialog_]]"
-            required="[[isEditDialog_]]" auto-validate="[[isEditDialog_]]"
-            invalid="{{inputInvalid_}}">
+            value="[[getPassword_(entry.password)]]" class="password-input"
+            readonly="[[!isEditDialog_]]" invalid="{{passwordInputInvalid_}}"
+            required="[[isEditDialog_]]" auto-validate="[[isEditDialog_]]">
           <cr-icon-button id="showPasswordButton"
               class$="[[getIconClass_(isPasswordVisible_, entry.password)]]"
               slot="suffix" hidden$="[[entry.federationText]]"
@@ -54,7 +51,7 @@
           $i18n{cancel}
         </cr-button>
         <cr-button id="actionButton" class="action-button"
-            on-click="onActionButtonTap_" disabled="[[inputInvalid_]]">
+            on-click="onActionButtonTap_" disabled="[[isSaveButtonDisabled_]]">
           [[getActionButtonName_(isEditDialog_)]]
         </cr-button>
       </div>
diff --git a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.js b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.js
index 2d7fd530..142eefe 100644
--- a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.js
+++ b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.js
@@ -22,6 +22,7 @@
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {loadTimeData} from '../i18n_setup.js';
+import {MultiStorePasswordUiEntry} from './multi_store_password_ui_entry.js';
 
 import {PasswordManagerImpl} from './password_manager_proxy.js';
 import {ShowPasswordBehavior} from './show_password_behavior.js';
@@ -36,6 +37,27 @@
   properties: {
     shouldShowStorageDetails: {type: Boolean, value: false},
 
+    /**
+     * Saved passwords after deduplicating versions that are repeated in the
+     * account and on the device.
+     * @type {!Array<!MultiStorePasswordUiEntry>}
+     */
+    savedPasswords: {
+      type: Array,
+      value: () => [],
+    },
+
+    /**
+     * Usernames for the same website. Used for the fast check whether edited
+     * username is already used.
+     * @private {?Set<string>}
+     */
+    usernamesForSameOrigin: {
+      type: Object,
+      value: null,
+    },
+
+
     /** @private */
     editPasswordsInSettings_: {
       type: Boolean,
@@ -64,15 +86,36 @@
     },
 
     /**
-     * Whether the input is invalid.
+     * Whether the username input is invalid.
      * @private
      */
-    inputInvalid_: Boolean,
+    usernameInputInvalid_: Boolean,
+
+    /**
+     * Whether the password input is invalid.
+     * @private
+     */
+    passwordInputInvalid_: Boolean,
+
+    /**
+     * If either username or password entered incorrectly the save button will
+     * be disabled.
+     * @private
+     * */
+    isSaveButtonDisabled_: {
+      type: Boolean,
+      computed:
+          'computeIsSaveButtonDisabled_(usernameInputInvalid_, passwordInputInvalid_)'
+    }
   },
 
   /** @override */
   attached() {
     this.$.dialog.showModal();
+    this.usernamesForSameOrigin =
+        new Set(this.savedPasswords
+                    .filter(item => item.urls.shown === this.entry.urls.shown)
+                    .map(item => item.username));
   },
 
   /**
@@ -241,5 +284,29 @@
    */
   getFootnote_() {
     return this.i18n('editPasswordFootnote', this.entry.urls.shown);
-  }
+  },
+
+  /**
+   * Helper function that checks if save button should be disabled.
+   * @return {boolean}
+   * @private
+   */
+  computeIsSaveButtonDisabled_() {
+    return this.usernameInputInvalid_ || this.passwordInputInvalid_;
+  },
+
+  /**
+   * Helper function that checks whether edited username is not used for the
+   * same website.
+   * @private
+   */
+  validateUsername_() {
+    if (this.entry.username !== this.$.usernameInput.value) {
+      this.usernameInputInvalid_ =
+          this.usernamesForSameOrigin.has(this.$.usernameInput.value);
+    } else {
+      this.usernameInputInvalid_ = false;
+    }
+  },
+
 });
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_list_handler.html b/chrome/browser/resources/settings/autofill_page/passwords_list_handler.html
index 59156a82..0ead0c2e 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_list_handler.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_list_handler.html
@@ -34,9 +34,8 @@
 <if expr="chromeos">
           token-request-manager="[[tokenRequestManager]]"
 </if>
-          entry="[[activePassword.entry]]"
-          should-show-storage-details=
-            "[[shouldShowStorageDetails]]">
+          entry="[[activePassword.entry]]" saved-passwords="[[savedPasswords]]"
+          should-show-storage-details="[[shouldShowStorageDetails]]">
       </password-edit-dialog>
     </template>
 
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_list_handler.js b/chrome/browser/resources/settings/autofill_page/passwords_list_handler.js
index 14f5547..982055ee5 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_list_handler.js
+++ b/chrome/browser/resources/settings/autofill_page/passwords_list_handler.js
@@ -28,6 +28,7 @@
 
 // <if expr="chromeos">
 import {BlockingRequestManager} from './blocking_request_manager.js';
+import {MultiStorePasswordUiEntry} from './multi_store_password_ui_entry.js';
 // </if>
 import {PasswordMoreActionsClickedEvent} from './password_list_item.js';
 import {PasswordManagerImpl, PasswordManagerProxy} from './password_manager_proxy.js';
@@ -39,6 +40,15 @@
   _template: html`{__html_template__}`,
 
   properties: {
+    /**
+     * Saved passwords after deduplicating versions that are repeated in the
+     * account and on the device.
+     * @type {!Array<!MultiStorePasswordUiEntry>}
+     */
+    savedPasswords: {
+      type: Array,
+      value: () => [],
+    },
 
     /**
      * The model for any active menus or dialogs. The value is reset to null
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.html b/chrome/browser/resources/settings/autofill_page/passwords_section.html
index 2c62d14..737197e7 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.html
@@ -165,7 +165,8 @@
 <if expr="chromeos">
       token-request-manager="[[tokenRequestManager_]]"
 </if>
-      should-show-storage-details="[[shouldShowStorageDetails_]]">
+      should-show-storage-details="[[shouldShowStorageDetails_]]"
+      saved-passwords="[[savedPasswords]]">
       <div slot="body" class="list-frame">
         <div hidden$="[[!eligibleForAccountStorage_]]"
             id="accountStorageButtonsContainer">
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/internet_page/BUILD.gn
index f5d99e5..437a93ab 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/internet_page/BUILD.gn
@@ -202,7 +202,7 @@
     ":internet_shared_css.m",
 #    ":internet_subpage.m",
     ":network_proxy_section.m",
-#    ":network_summary.m",
+    ":network_summary.m",
     ":network_summary_item.m",
 #    ":tether_connection_dialog.m"
   ]
@@ -328,7 +328,11 @@
 js_library("network_summary.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/internet_page/network_summary.m.js" ]
   deps = [
-    # TODO: Fill those in.
+    ":network_summary_item.m",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_components/chromeos/network:mojo_interface_provider.m",
+    "//ui/webui/resources/cr_components/chromeos/network:network_listener_behavior.m",
+    "//ui/webui/resources/cr_components/chromeos/network:onc_mojo.m",
   ]
   extra_deps = [ ":network_summary_module" ]
 }
@@ -429,6 +433,8 @@
   js_file = "network_summary.js"
   html_file = "network_summary.html"
   html_type = "dom-module"
+  auto_imports = os_settings_auto_imports
+  namespace_rewrites = os_settings_namespace_rewrites
 }
 
 polymer_modulizer("network_summary_item") {
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/network_summary.html b/chrome/browser/resources/settings/chromeos/internet_page/network_summary.html
index 486743b2..47492be 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/network_summary.html
+++ b/chrome/browser/resources/settings/chromeos/internet_page/network_summary.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_components/chromeos/network/mojo_interface_provider.html">
 <link rel="import" href="chrome://resources/cr_components/chromeos/network/network_listener_behavior.html">
 <link rel="import" href="chrome://resources/cr_components/chromeos/network/onc_mojo.html">
 <link rel="import" href="network_summary_item.html">
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.js b/chrome/browser/resources/settings/chromeos/os_settings.js
index 8bf205f..de2c4f6 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings.js
@@ -11,6 +11,7 @@
 import './internet_page/internet_config.m.js';
 import './internet_page/internet_known_networks_page.m.js';
 import './internet_page/network_proxy_section.m.js';
+import './internet_page/network_summary.m.js';
 import './internet_page/network_summary_item.m.js';
 import './nearby_share_page/nearby_share_receive_dialog.m.js';
 import './nearby_share_page/nearby_share_subpage.m.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp b/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp
index 9090a74a..fd33baf 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp
+++ b/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp
@@ -65,6 +65,11 @@
            use_base_dir="false"
            compress="false"
            type="BINDATA" />
+  <include name="IDR_OS_SETTINGS_NETWORK_SUMMARY_M_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/chromeos/internet_page/network_summary.m.js"
+           use_base_dir="false"
+           compress="false"
+           type="BINDATA" />
   <include name="IDR_OS_SETTINGS_NETWORK_SUMMARY_ITEM_M_JS"
            file="${root_gen_dir}/chrome/browser/resources/settings/chromeos/internet_page/network_summary_item.m.js"
            use_base_dir="false"
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.cc b/chrome/browser/signin/dice_web_signin_interceptor.cc
index 708776d..47a566e 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_metrics.h"
-#include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/signin/dice_intercepted_session_startup_helper.h"
 #include "chrome/browser/signin/dice_signed_in_profile_creator.h"
 #include "chrome/browser/signin/dice_web_signin_interceptor_factory.h"
@@ -31,6 +30,7 @@
 #include "chrome/browser/ui/signin/profile_colors_util.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/themes/autogenerated_theme_util.h"
+#include "chrome/grit/generated_resources.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -333,7 +333,12 @@
           ->FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId(
               account_id_);
   if (account_info) {
-    profile_name = profiles::GetDefaultNameForNewSignedInProfile(*account_info);
+    bool is_managed = !account_info->hosted_domain.empty() &&
+                      account_info->hosted_domain != kNoHostedDomainFound;
+    profile_name =
+        is_managed ? l10n_util::GetStringUTF16(
+                         IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_PROFILE_NAME)
+                   : base::UTF8ToUTF16(account_info->given_name);
   }
 
   DCHECK(!dice_signed_in_profile_creator_);
diff --git a/chrome/browser/ssl/sct_reporting_service_browsertest.cc b/chrome/browser/ssl/sct_reporting_service_browsertest.cc
index 5642aab..f9f46979 100644
--- a/chrome/browser/ssl/sct_reporting_service_browsertest.cc
+++ b/chrome/browser/ssl/sct_reporting_service_browsertest.cc
@@ -206,7 +206,7 @@
 
 // Tests that disabling Extended Reporting causes the cache to be cleared.
 IN_PROC_BROWSER_TEST_F(SCTReportingServiceBrowserTest,
-                       OptingOutClearsSCTAuditingCache) {
+                       DISABLED_OptingOutClearsSCTAuditingCache) {
   // Enable SCT auditing and enqueue a report.
   SetExtendedReportingEnabled(true);
 
@@ -231,7 +231,7 @@
 // Tests that reports are still sent for opted-in profiles after the network
 // service crashes and is restarted.
 IN_PROC_BROWSER_TEST_F(SCTReportingServiceBrowserTest,
-                       ReportsSentAfterNetworkServiceRestart) {
+                       DISABLED_ReportsSentAfterNetworkServiceRestart) {
   // This test is only applicable to out-of-process network service because it
   // tests what happens when the network service crashes and restarts.
   if (content::IsInProcessNetworkService()) {
diff --git a/chrome/browser/supervised_user/supervised_user_features.cc b/chrome/browser/supervised_user/supervised_user_features.cc
index 1c0ee95..ddac5e8 100644
--- a/chrome/browser/supervised_user/supervised_user_features.cc
+++ b/chrome/browser/supervised_user/supervised_user_features.cc
@@ -7,7 +7,7 @@
 namespace supervised_users {
 
 const base::Feature kSupervisedUserIframeFilter{
-    "SupervisedUserIframeFilter", base::FEATURE_ENABLED_BY_DEFAULT};
+    "SupervisedUserIframeFilter", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kSupervisedUserInitiatedExtensionInstall{
     "SupervisedUserInitiatedExtensionInstall",
diff --git a/chrome/browser/sync/test/integration/single_client_search_engines_sync_test.cc b/chrome/browser/sync/test/integration/single_client_search_engines_sync_test.cc
index 504be6c..28e4214 100644
--- a/chrome/browser/sync/test/integration/single_client_search_engines_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_search_engines_sync_test.cc
@@ -3,9 +3,11 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/sync/test/integration/search_engines_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
+#include "chrome/test/base/search_test_utils.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/sync/driver/profile_sync_service.h"
 #include "content/public/test/browser_test.h"
@@ -16,6 +18,20 @@
 
   ~SingleClientSearchEnginesSyncTest() override {}
 
+  bool SetupClients() override {
+    if (!SyncTest::SetupClients()) {
+      return false;
+    }
+
+    // Wait for models to load.
+    search_test_utils::WaitForTemplateURLServiceToLoad(
+        TemplateURLServiceFactory::GetForProfile(verifier()));
+    search_test_utils::WaitForTemplateURLServiceToLoad(
+        TemplateURLServiceFactory::GetForProfile(GetProfile(0)));
+
+    return true;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(SingleClientSearchEnginesSyncTest);
 };
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index 4f04c343..85124c2 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -26,7 +26,6 @@
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profiles_state.h"
-#include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
@@ -39,7 +38,6 @@
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/test/base/search_test_utils.h"
 #include "components/bookmarks/test/bookmark_test_helpers.h"
 #include "components/gcm_driver/gcm_profile_service.h"
 #include "components/invalidation/impl/fake_invalidation_service.h"
@@ -54,7 +52,6 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/os_crypt/os_crypt_mocker.h"
 #include "components/prefs/scoped_user_pref_update.h"
-#include "components/search_engines/template_url_service.h"
 #include "components/signin/public/identity_manager/consent_level.h"
 #include "components/sync/base/invalidation_helper.h"
 #include "components/sync/base/sync_base_switches.h"
@@ -1059,8 +1056,6 @@
   bookmarks::test::WaitForBookmarkModelToLoad(
       BookmarkModelFactory::GetForBrowserContext(profile));
 
-  search_test_utils::WaitForTemplateURLServiceToLoad(
-      TemplateURLServiceFactory::GetForProfile(profile));
 #if defined(OS_CHROMEOS)
   printers_helper::WaitForPrinterStoreToLoad(profile);
 #endif
diff --git a/chrome/browser/sync/test/integration/two_client_search_engines_sync_test.cc b/chrome/browser/sync/test/integration/two_client_search_engines_sync_test.cc
index 29e38c69..7b34465 100644
--- a/chrome/browser/sync/test/integration/two_client_search_engines_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_search_engines_sync_test.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
+#include "chrome/test/base/search_test_utils.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
 #include "content/public/test/browser_test.h"
@@ -22,6 +23,22 @@
 
   ~TwoClientSearchEnginesSyncTest() override {}
 
+  bool SetupClients() override {
+    if (!SyncTest::SetupClients()) {
+      return false;
+    }
+
+    // Wait for models to load.
+    search_test_utils::WaitForTemplateURLServiceToLoad(
+        TemplateURLServiceFactory::GetForProfile(verifier()));
+    search_test_utils::WaitForTemplateURLServiceToLoad(
+        TemplateURLServiceFactory::GetForProfile(GetProfile(0)));
+    search_test_utils::WaitForTemplateURLServiceToLoad(
+        TemplateURLServiceFactory::GetForProfile(GetProfile(1)));
+
+    return true;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(TwoClientSearchEnginesSyncTest);
 };
diff --git a/chrome/browser/tracing/chrome_tracing_delegate_browsertest.cc b/chrome/browser/tracing/chrome_tracing_delegate_browsertest.cc
index 1359217..1e47bc39 100644
--- a/chrome/browser/tracing/chrome_tracing_delegate_browsertest.cc
+++ b/chrome/browser/tracing/chrome_tracing_delegate_browsertest.cc
@@ -76,8 +76,9 @@
   }
 
   void TriggerPreemptiveScenario(
-      const base::Closure& on_started_finalization_callback) {
-    on_started_finalization_callback_ = on_started_finalization_callback;
+      base::OnceClosure on_started_finalization_callback) {
+    on_started_finalization_callback_ =
+        std::move(on_started_finalization_callback);
     trigger_handle_ =
         content::BackgroundTracingManager::GetInstance()->RegisterTriggerType(
             "test");
@@ -131,12 +132,12 @@
 
     if (!on_started_finalization_callback_.is_null()) {
       content::GetUIThreadTaskRunner({})->PostTask(
-          FROM_HERE, on_started_finalization_callback_);
+          FROM_HERE, std::move(on_started_finalization_callback_));
     }
   }
 
   std::unique_ptr<base::RunLoop> wait_for_upload_;
-  base::Closure on_started_finalization_callback_;
+  base::OnceClosure on_started_finalization_callback_;
   int receive_count_;
   int started_finalizations_count_;
   content::BackgroundTracingManager::TriggerHandle trigger_handle_;
@@ -148,7 +149,7 @@
   EXPECT_TRUE(StartPreemptiveScenario(
       content::BackgroundTracingManager::NO_DATA_FILTERING));
 
-  TriggerPreemptiveScenario(base::Closure());
+  TriggerPreemptiveScenario(base::OnceClosure());
 
   WaitForUpload();
 
@@ -181,7 +182,7 @@
   EXPECT_TRUE(StartPreemptiveScenario(
       content::BackgroundTracingManager::NO_DATA_FILTERING));
 
-  TriggerPreemptiveScenario(base::Closure());
+  TriggerPreemptiveScenario(base::OnceClosure());
 
   WaitForUpload();
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index c975ca4..f5f1fb65 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -603,6 +603,7 @@
       "//chrome/browser/notifications/scheduler/public",
       "//chrome/browser/ui/webui/explore_sites_internals:mojo_bindings",
       "//components/browser_ui/util/android",
+      "//components/infobars/content",
       "//components/query_tiles",
       "//components/security_state/content/android",
     ]
@@ -2774,8 +2775,6 @@
       "views/profiles/badged_profile_photo.h",
       "views/profiles/profile_picker_view.cc",
       "views/profiles/profile_picker_view.h",
-      "views/profiles/profile_picker_view_sync_delegate.cc",
-      "views/profiles/profile_picker_view_sync_delegate.h",
       "views/profiles/user_manager_view.cc",
       "views/profiles/user_manager_view.h",
       "webui/app_launcher_page_ui.cc",
diff --git a/chrome/browser/ui/android/android_about_app_info.cc b/chrome/browser/ui/android/android_about_app_info.cc
index db9ae42..c57ef06 100644
--- a/chrome/browser/ui/android/android_about_app_info.cc
+++ b/chrome/browser/ui/android/android_about_app_info.cc
@@ -9,13 +9,13 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/system/sys_info.h"
-#include "chrome/android/chrome_jni_headers/ChromeVersionInfo_jni.h"
+#include "chrome/android/chrome_jni_headers/PlayServicesVersionInfo_jni.h"
 #include "content/public/common/user_agent.h"
 
 std::string AndroidAboutAppInfo::GetGmsInfo() {
   JNIEnv* env = base::android::AttachCurrentThread();
   const base::android::ScopedJavaLocalRef<jstring> info =
-      Java_ChromeVersionInfo_getGmsInfo(env);
+      Java_PlayServicesVersionInfo_getGmsInfo(env);
   return base::android::ConvertJavaStringToUTF8(env, info);
 }
 
diff --git a/chrome/browser/ui/android/content_settings/ads_blocked_infobar_delegate.cc b/chrome/browser/ui/android/content_settings/ads_blocked_infobar_delegate.cc
index 85b59ea..814fc1d 100644
--- a/chrome/browser/ui/android/content_settings/ads_blocked_infobar_delegate.cc
+++ b/chrome/browser/ui/android/content_settings/ads_blocked_infobar_delegate.cc
@@ -9,10 +9,10 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/android/android_theme_resources.h"
-#include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/subresource_filter/chrome_subresource_filter_client.h"
 #include "chrome/browser/ui/android/infobars/ads_blocked_infobar.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/infobars/content/content_infobar_manager.h"
 #include "components/infobars/core/infobar.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
@@ -20,8 +20,9 @@
 #include "ui/base/l10n/l10n_util.h"
 
 // static
-void AdsBlockedInfobarDelegate::Create(InfoBarService* infobar_service) {
-  infobar_service->AddInfoBar(std::make_unique<AdsBlockedInfoBar>(
+void AdsBlockedInfobarDelegate::Create(
+    infobars::ContentInfoBarManager* infobar_manager) {
+  infobar_manager->AddInfoBar(std::make_unique<AdsBlockedInfoBar>(
       base::WrapUnique(new AdsBlockedInfobarDelegate())));
 }
 
@@ -78,7 +79,7 @@
 
 bool AdsBlockedInfobarDelegate::Cancel() {
   auto* filter_client = ChromeSubresourceFilterClient::FromWebContents(
-      InfoBarService::WebContentsFromInfoBar(infobar()));
+      infobars::ContentInfoBarManager::WebContentsFromInfoBar(infobar()));
   filter_client->OnReloadRequested();
   return true;
 }
diff --git a/chrome/browser/ui/android/content_settings/ads_blocked_infobar_delegate.h b/chrome/browser/ui/android/content_settings/ads_blocked_infobar_delegate.h
index 17c2235f..b56590e 100644
--- a/chrome/browser/ui/android/content_settings/ads_blocked_infobar_delegate.h
+++ b/chrome/browser/ui/android/content_settings/ads_blocked_infobar_delegate.h
@@ -8,7 +8,9 @@
 #include "base/macros.h"
 #include "components/infobars/core/confirm_infobar_delegate.h"
 
-class InfoBarService;
+namespace infobars {
+class ContentInfoBarManager;
+}
 
 // This infobar appears when the user proceeds through Safe Browsing warning
 // interstitials to a site with deceptive embedded content. It tells the user
@@ -19,8 +21,8 @@
 class AdsBlockedInfobarDelegate : public ConfirmInfoBarDelegate {
  public:
   // Creates a subresource filter infobar and delegate and adds the infobar to
-  // |infobar_service|.
-  static void Create(InfoBarService* infobar_service);
+  // |infobar_manager|.
+  static void Create(infobars::ContentInfoBarManager* infobar_manager);
 
   ~AdsBlockedInfobarDelegate() override;
 
diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
index d9635dc6..01e6203 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -534,7 +534,7 @@
       ChromeAppListItem* item = model_updater_->ItemAtForTest(i);
       if (item->GetItemType() == AppServiceAppItem::kItemType) {
         if (arc_count++ == index) {
-          arc_item = reinterpret_cast<AppServiceAppItem*>(item);
+          arc_item = static_cast<AppServiceAppItem*>(item);
           break;
         }
       }
diff --git a/chrome/browser/ui/ash/ambient/ambient_client_impl.cc b/chrome/browser/ui/ash/ambient/ambient_client_impl.cc
index 096619e1..b6fc4cc 100644
--- a/chrome/browser/ui/ash/ambient/ambient_client_impl.cc
+++ b/chrome/browser/ui/ash/ambient/ambient_client_impl.cc
@@ -19,6 +19,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/identity_manager/access_token_fetcher.h"
 #include "components/signin/public/identity_manager/access_token_info.h"
+#include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/consent_level.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/scope_set.h"
@@ -121,7 +122,6 @@
 
   CoreAccountInfo account_info = identity_manager->GetPrimaryAccountInfo(
       signin::ConsentLevel::kNotRequired);
-
   const signin::ScopeSet scopes{kPhotosOAuthScope, kBackdropOAuthScope};
   // TODO(b/148463064): Handle retry refresh token and multiple requests.
   // Currently only one request is allowed.
@@ -148,6 +148,15 @@
   content::GetDeviceService().BindWakeLockProvider(std::move(receiver));
 }
 
+bool AmbientClientImpl::ShouldUseProdServer() {
+  if (chromeos::features::IsAmbientModeDevUseProdEnabled())
+    return true;
+
+  auto channel = chrome::GetChannel();
+  return channel == version_info::Channel::STABLE ||
+         channel == version_info::Channel::BETA;
+}
+
 void AmbientClientImpl::GetAccessToken(
     GetAccessTokenCallback callback,
     const std::string& gaia_id,
@@ -167,11 +176,3 @@
   }
 }
 
-bool AmbientClientImpl::ShouldUseProdServer() {
-  if (chromeos::features::IsAmbientModeDevUseProdEnabled())
-    return true;
-
-  auto channel = chrome::GetChannel();
-  return channel == version_info::Channel::STABLE ||
-         channel == version_info::Channel::BETA;
-}
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index f3b1ffc..3ad24f4 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -575,7 +575,7 @@
     sync_preferences::PrefServiceSyncable* pref_sync =
         profile()->GetTestingPrefService();
     sync_preferences::PrefModelAssociator* pref_sync_service =
-        reinterpret_cast<sync_preferences::PrefModelAssociator*>(
+        static_cast<sync_preferences::PrefModelAssociator*>(
             pref_sync->GetSyncableService(GetPreferencesModelType()));
     return pref_sync_service;
   }
diff --git a/chrome/browser/ui/cocoa/notifications/BUILD.gn b/chrome/browser/ui/cocoa/notifications/BUILD.gn
index 0574ce7..be88b8a 100644
--- a/chrome/browser/ui/cocoa/notifications/BUILD.gn
+++ b/chrome/browser/ui/cocoa/notifications/BUILD.gn
@@ -58,10 +58,13 @@
     "notification_constants_mac.h",
     "notification_constants_mac.mm",
     "notification_delivery.h",
+    "notification_operation.h",
     "notification_response_builder_mac.h",
     "notification_response_builder_mac.mm",
     "unnotification_builder_mac.h",
     "unnotification_builder_mac.mm",
+    "unnotification_response_builder_mac.h",
+    "unnotification_response_builder_mac.mm",
     "xpc_mach_port.h",
     "xpc_mach_port.mm",
   ]
diff --git a/chrome/browser/ui/cocoa/notifications/notification_operation.h b/chrome/browser/ui/cocoa/notifications/notification_operation.h
new file mode 100644
index 0000000..a75f279c
--- /dev/null
+++ b/chrome/browser/ui/cocoa/notifications/notification_operation.h
@@ -0,0 +1,20 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COCOA_NOTIFICATIONS_NOTIFICATION_OPERATION_H_
+#define CHROME_BROWSER_UI_COCOA_NOTIFICATIONS_NOTIFICATION_OPERATION_H_
+
+// Make sure this Obj-C enum is kept in sync with the
+// NotificationCommon::Operation enum.
+// The latter cannot be reused because the XPC service is not aware of
+// PlatformNotificationCenter.
+enum class NotificationOperation {
+  NOTIFICATION_CLICK = 0,
+  NOTIFICATION_CLOSE = 1,
+  NOTIFICATION_DISABLE_PERMISSION = 2,
+  NOTIFICATION_SETTINGS = 3,
+  NOTIFICATION_OPERATION_MAX = NOTIFICATION_SETTINGS
+};
+
+#endif  // CHROME_BROWSER_UI_COCOA_NOTIFICATIONS_NOTIFICATION_OPERATION_H_
diff --git a/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.h b/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.h
index ac2b13f..7f04533c 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.h
+++ b/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.h
@@ -9,18 +9,6 @@
 
 @class NSUserNotification;
 
-// Make sure this Obj-C enum is kept in sync with the
-// NotificationCommon::Operation enum.
-// The latter cannot be reused because the XPC service is not aware of
-// PlatformNotificationCenter.
-enum NotificationOperation {
-  NOTIFICATION_CLICK = 0,
-  NOTIFICATION_CLOSE = 1,
-  NOTIFICATION_DISABLE_PERMISSION = 2,
-  NOTIFICATION_SETTINGS = 3,
-  NOTIFICATION_OPERATION_MAX = NOTIFICATION_SETTINGS
-};
-
 // Provides a marshallable way for storing the information related to a
 // notification response action, clicking on it, clicking on a button etc.
 @interface NotificationResponseBuilder : NSObject
diff --git a/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.mm b/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.mm
index e6a19e2..963848d 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.mm
+++ b/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.mm
@@ -6,6 +6,7 @@
 
 #include "base/check.h"
 #include "chrome/browser/ui/cocoa/notifications/notification_constants_mac.h"
+#include "chrome/browser/ui/cocoa/notifications/notification_operation.h"
 
 @implementation NotificationResponseBuilder
 
@@ -45,8 +46,8 @@
                 : notification.activationType;
   NotificationOperation operation =
       activationType == NSUserNotificationActivationTypeNone
-          ? NOTIFICATION_CLOSE
-          : NOTIFICATION_CLICK;
+          ? NotificationOperation::NOTIFICATION_CLOSE
+          : NotificationOperation::NOTIFICATION_CLICK;
   int buttonIndex = notification_constants::kNotificationInvalidButtonIndex;
 
   // Determine whether the user clicked on a button, and if they did, whether it
@@ -65,7 +66,7 @@
     // No developer actions, just the settings button.
     if (!multipleButtons) {
       DCHECK(settingsButtonRequired);
-      operation = NOTIFICATION_SETTINGS;
+      operation = NotificationOperation::NOTIFICATION_SETTINGS;
       buttonIndex = notification_constants::kNotificationInvalidButtonIndex;
     } else {
       // 0 based array containing.
@@ -76,8 +77,8 @@
           [notification valueForKey:@"_alternateActionIndex"];
       operation = settingsButtonRequired && (actionIndex.unsignedLongValue ==
                                              alternateButtons.count - 1)
-                      ? NOTIFICATION_SETTINGS
-                      : NOTIFICATION_CLICK;
+                      ? NotificationOperation::NOTIFICATION_SETTINGS
+                      : NotificationOperation::NOTIFICATION_CLICK;
       buttonIndex =
           settingsButtonRequired &&
                   (actionIndex.unsignedLongValue == alternateButtons.count - 1)
@@ -94,8 +95,8 @@
     notification_constants::kNotificationCreatorPid : creatorPid ? creatorPid
                                                                  : @0,
     notification_constants::kNotificationType : notificationType,
-    notification_constants::
-    kNotificationOperation : [NSNumber numberWithInt:operation],
+    notification_constants::kNotificationOperation :
+        [NSNumber numberWithInt:static_cast<int>(operation)],
     notification_constants::
     kNotificationButtonIndex : [NSNumber numberWithInt:buttonIndex],
   };
diff --git a/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm b/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm
index 4d86284..e24a294 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm
+++ b/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm
@@ -9,6 +9,7 @@
 #include "chrome/browser/notifications/notification_handler.h"
 #include "chrome/browser/ui/cocoa/notifications/notification_builder_mac.h"
 #include "chrome/browser/ui/cocoa/notifications/notification_constants_mac.h"
+#include "chrome/browser/ui/cocoa/notifications/notification_operation.h"
 #include "chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -16,13 +17,15 @@
   static_assert(static_cast<int>(a) == static_cast<int>(b), \
                 "mismatching enums: " #a)
 
-STATIC_ASSERT_ENUM(NOTIFICATION_CLICK, NotificationCommon::OPERATION_CLICK);
-STATIC_ASSERT_ENUM(NOTIFICATION_CLOSE, NotificationCommon::OPERATION_CLOSE);
-STATIC_ASSERT_ENUM(NOTIFICATION_DISABLE_PERMISSION,
+STATIC_ASSERT_ENUM(NotificationOperation::NOTIFICATION_CLICK,
+                   NotificationCommon::OPERATION_CLICK);
+STATIC_ASSERT_ENUM(NotificationOperation::NOTIFICATION_CLOSE,
+                   NotificationCommon::OPERATION_CLOSE);
+STATIC_ASSERT_ENUM(NotificationOperation::NOTIFICATION_DISABLE_PERMISSION,
                    NotificationCommon::OPERATION_DISABLE_PERMISSION);
-STATIC_ASSERT_ENUM(NOTIFICATION_SETTINGS,
+STATIC_ASSERT_ENUM(NotificationOperation::NOTIFICATION_SETTINGS,
                    NotificationCommon::OPERATION_SETTINGS);
-STATIC_ASSERT_ENUM(NOTIFICATION_OPERATION_MAX,
+STATIC_ASSERT_ENUM(NotificationOperation::NOTIFICATION_OPERATION_MAX,
                    NotificationCommon::OPERATION_MAX);
 
 #undef STATIC_ASSERT_ENUM
@@ -85,7 +88,8 @@
   NSNumber* buttonIndex =
       [response objectForKey:notification_constants::kNotificationButtonIndex];
 
-  EXPECT_EQ(NOTIFICATION_CLICK, operation.intValue);
+  EXPECT_EQ(static_cast<int>(NotificationOperation::NOTIFICATION_CLICK),
+            operation.intValue);
   EXPECT_EQ(notification_constants::kNotificationInvalidButtonIndex,
             buttonIndex.intValue);
 }
@@ -107,7 +111,8 @@
   NSNumber* buttonIndex =
       [response objectForKey:notification_constants::kNotificationButtonIndex];
 
-  EXPECT_EQ(NOTIFICATION_SETTINGS, operation.intValue);
+  EXPECT_EQ(static_cast<int>(NotificationOperation::NOTIFICATION_SETTINGS),
+            operation.intValue);
   EXPECT_EQ(notification_constants::kNotificationInvalidButtonIndex,
             buttonIndex.intValue);
 }
@@ -132,7 +137,8 @@
       [response objectForKey:notification_constants::kNotificationOperation];
   NSNumber* buttonIndex =
       [response objectForKey:notification_constants::kNotificationButtonIndex];
-  EXPECT_EQ(NOTIFICATION_CLICK, operation.intValue);
+  EXPECT_EQ(static_cast<int>(NotificationOperation::NOTIFICATION_CLICK),
+            operation.intValue);
   EXPECT_EQ(0, buttonIndex.intValue);
 }
 
@@ -157,7 +163,8 @@
       [response objectForKey:notification_constants::kNotificationOperation];
   NSNumber* buttonIndex =
       [response objectForKey:notification_constants::kNotificationButtonIndex];
-  EXPECT_EQ(NOTIFICATION_CLICK, operation.intValue);
+  EXPECT_EQ(static_cast<int>(NotificationOperation::NOTIFICATION_CLICK),
+            operation.intValue);
   EXPECT_EQ(1, buttonIndex.intValue);
 }
 
@@ -185,7 +192,8 @@
       [response objectForKey:notification_constants::kNotificationOperation];
   NSNumber* buttonIndex =
       [response objectForKey:notification_constants::kNotificationButtonIndex];
-  EXPECT_EQ(NOTIFICATION_SETTINGS, operation.intValue);
+  EXPECT_EQ(static_cast<int>(NotificationOperation::NOTIFICATION_SETTINGS),
+            operation.intValue);
   EXPECT_EQ(notification_constants::kNotificationInvalidButtonIndex,
             buttonIndex.intValue);
 }
@@ -207,7 +215,8 @@
       [response objectForKey:notification_constants::kNotificationOperation];
   NSNumber* buttonIndex =
       [response objectForKey:notification_constants::kNotificationButtonIndex];
-  EXPECT_EQ(NOTIFICATION_CLOSE, operation.intValue);
+  EXPECT_EQ(static_cast<int>(NotificationOperation::NOTIFICATION_CLOSE),
+            operation.intValue);
   EXPECT_EQ(notification_constants::kNotificationInvalidButtonIndex,
             buttonIndex.intValue);
 }
@@ -234,7 +243,8 @@
       [response objectForKey:notification_constants::kNotificationOperation];
   NSNumber* buttonIndex =
       [response objectForKey:notification_constants::kNotificationButtonIndex];
-  EXPECT_EQ(NOTIFICATION_CLICK, operation.intValue);
+  EXPECT_EQ(static_cast<int>(NotificationOperation::NOTIFICATION_CLICK),
+            operation.intValue);
   EXPECT_EQ(1, buttonIndex.intValue);
 }
 
@@ -256,7 +266,8 @@
   NSNumber* buttonIndex =
       [response objectForKey:notification_constants::kNotificationButtonIndex];
 
-  EXPECT_EQ(NOTIFICATION_CLOSE, operation.intValue);
+  EXPECT_EQ(static_cast<int>(NotificationOperation::NOTIFICATION_CLOSE),
+            operation.intValue);
   EXPECT_EQ(notification_constants::kNotificationInvalidButtonIndex,
             buttonIndex.intValue);
 }
diff --git a/chrome/browser/ui/cocoa/notifications/unnotification_response_builder_mac.h b/chrome/browser/ui/cocoa/notifications/unnotification_response_builder_mac.h
new file mode 100644
index 0000000..0243b28c
--- /dev/null
+++ b/chrome/browser/ui/cocoa/notifications/unnotification_response_builder_mac.h
@@ -0,0 +1,21 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COCOA_NOTIFICATIONS_UNNOTIFICATION_RESPONSE_BUILDER_MAC_H_
+#define CHROME_BROWSER_UI_COCOA_NOTIFICATIONS_UNNOTIFICATION_RESPONSE_BUILDER_MAC_H_
+
+#import <Foundation/Foundation.h>
+
+@class UNNotificationResponse;
+
+// Provides a marshallable way for storing the information related to a
+// notification response action, clicking on it, clicking on a button etc.
+API_AVAILABLE(macosx(10.14))
+@interface UNNotificationResponseBuilder : NSObject
+
++ (NSDictionary*)buildDictionary:(UNNotificationResponse*)response;
+
+@end
+
+#endif  // CHROME_BROWSER_UI_COCOA_NOTIFICATIONS_UNNOTIFICATION_RESPONSE_BUILDER_MAC_H_
diff --git a/chrome/browser/ui/cocoa/notifications/unnotification_response_builder_mac.mm b/chrome/browser/ui/cocoa/notifications/unnotification_response_builder_mac.mm
new file mode 100644
index 0000000..b6ed1f66
--- /dev/null
+++ b/chrome/browser/ui/cocoa/notifications/unnotification_response_builder_mac.mm
@@ -0,0 +1,65 @@
+// Copyright 2020 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 "chrome/browser/ui/cocoa/notifications/unnotification_response_builder_mac.h"
+
+#import <UserNotifications/UserNotifications.h>
+
+#include "base/check.h"
+#include "chrome/browser/ui/cocoa/notifications/notification_constants_mac.h"
+#include "chrome/browser/ui/cocoa/notifications/notification_operation.h"
+
+@implementation UNNotificationResponseBuilder
+
++ (NSDictionary*)buildDictionary:(UNNotificationResponse*)response {
+  NSDictionary* userInfo =
+      [[[[response notification] request] content] userInfo];
+
+  NSString* origin =
+      [userInfo objectForKey:notification_constants::kNotificationOrigin]
+          ? [userInfo objectForKey:notification_constants::kNotificationOrigin]
+          : @"";
+  DCHECK([userInfo objectForKey:notification_constants::kNotificationId]);
+  NSString* notificationId =
+      [userInfo objectForKey:notification_constants::kNotificationId];
+
+  DCHECK(
+      [userInfo objectForKey:notification_constants::kNotificationProfileId]);
+  NSString* profileId =
+      [userInfo objectForKey:notification_constants::kNotificationProfileId];
+
+  NSNumber* creatorPid =
+      [userInfo objectForKey:notification_constants::kNotificationCreatorPid];
+
+  DCHECK(
+      [userInfo objectForKey:notification_constants::kNotificationIncognito]);
+  NSNumber* incognito =
+      [userInfo objectForKey:notification_constants::kNotificationIncognito];
+  NSNumber* notificationType =
+      [userInfo objectForKey:notification_constants::kNotificationType];
+
+  NotificationOperation operation =
+      [[response actionIdentifier]
+          isEqual:UNNotificationDismissActionIdentifier]
+          ? NotificationOperation::NOTIFICATION_CLOSE
+          : NotificationOperation::NOTIFICATION_CLICK;
+
+  int buttonIndex = notification_constants::kNotificationInvalidButtonIndex;
+
+  return @{
+    notification_constants::kNotificationOrigin : origin,
+    notification_constants::kNotificationId : notificationId,
+    notification_constants::kNotificationProfileId : profileId,
+    notification_constants::kNotificationIncognito : incognito,
+    notification_constants::kNotificationCreatorPid : creatorPid ? creatorPid
+                                                                 : @0,
+    notification_constants::kNotificationType : notificationType,
+    notification_constants::kNotificationOperation :
+        [NSNumber numberWithInt:static_cast<int>(operation)],
+    notification_constants::
+    kNotificationButtonIndex : [NSNumber numberWithInt:buttonIndex],
+  };
+}
+
+@end
diff --git a/chrome/browser/ui/cocoa/notifications/unnotification_response_builder_mac_unittest.mm b/chrome/browser/ui/cocoa/notifications/unnotification_response_builder_mac_unittest.mm
new file mode 100644
index 0000000..d88cc61a
--- /dev/null
+++ b/chrome/browser/ui/cocoa/notifications/unnotification_response_builder_mac_unittest.mm
@@ -0,0 +1,158 @@
+// Copyright 2020 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 <UserNotifications/UserNotifications.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "chrome/browser/notifications/notification_common.h"
+#include "chrome/browser/notifications/notification_handler.h"
+#include "chrome/browser/ui/cocoa/notifications/notification_constants_mac.h"
+#include "chrome/browser/ui/cocoa/notifications/notification_operation.h"
+#include "chrome/browser/ui/cocoa/notifications/unnotification_builder_mac.h"
+#include "chrome/browser/ui/cocoa/notifications/unnotification_response_builder_mac.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+@interface FakeContent : NSObject
+@property(nonatomic, retain) NSDictionary* userInfo;
+@end
+
+@implementation FakeContent
+@synthesize userInfo;
+@end
+
+@interface FakeRequest : NSObject
+@property(nonatomic, retain) FakeContent* content;
+@end
+
+@implementation FakeRequest
+@synthesize content;
+@end
+
+@interface FakeUNNotification : NSObject
+@property(nonatomic, retain) FakeRequest* request;
+@end
+
+@implementation FakeUNNotification
+@synthesize request;
+@end
+
+@interface FakeUNNotificationResponse : NSObject
+@property(nonatomic, retain) FakeUNNotification* notification;
+@property(nonatomic, copy) NSString* actionIdentifier;
+@end
+
+@implementation FakeUNNotificationResponse
+@synthesize notification;
+@synthesize actionIdentifier;
+@end
+
+namespace {
+
+API_AVAILABLE(macosx(10.14))
+base::scoped_nsobject<UNNotificationBuilder> NewTestBuilder(
+    NotificationHandler::Type type) {
+  base::scoped_nsobject<UNNotificationBuilder> builder(
+      [[UNNotificationBuilder alloc] init]);
+  [builder setTitle:@"Title"];
+  [builder setSubTitle:@"https://www.moe.com"];
+  [builder setContextMessage:@"hey there"];
+  [builder setNotificationId:@"notificationId"];
+  [builder setProfileId:@"profileId"];
+  [builder setIncognito:false];
+  [builder setCreatorPid:@1];
+  [builder setNotificationType:[NSNumber numberWithInt:static_cast<int>(type)]];
+  return builder;
+}
+
+API_AVAILABLE(macosx(10.14))
+FakeUNNotificationResponse* CreateFakeResponse(NSDictionary* userInfo) {
+  FakeContent* fakeContent = [[FakeContent alloc] init];
+  fakeContent.userInfo = userInfo;
+
+  FakeRequest* fakeRequest = [[FakeRequest alloc] init];
+  fakeRequest.content = fakeContent;
+
+  FakeUNNotification* fakeNotification = [[FakeUNNotification alloc] init];
+  fakeNotification.request = fakeRequest;
+
+  FakeUNNotificationResponse* fakeResponse =
+      [[FakeUNNotificationResponse alloc] init];
+  fakeResponse.actionIdentifier = UNNotificationDefaultActionIdentifier;
+  fakeResponse.notification = fakeNotification;
+
+  return fakeResponse;
+}
+
+}  // namespace
+
+TEST(UNNotificationResponseBuilderMacTest, TestNoCreatorPid) {
+  if (@available(macOS 10.14, *)) {
+    base::scoped_nsobject<UNNotificationBuilder> builder =
+        NewTestBuilder(NotificationHandler::Type::WEB_PERSISTENT);
+    UNMutableNotificationContent* content = [builder buildUserNotification];
+    base::scoped_nsobject<NSMutableDictionary> newUserInfo(
+        [[content userInfo] mutableCopy]);
+    [newUserInfo
+        removeObjectForKey:notification_constants::kNotificationCreatorPid];
+
+    FakeUNNotificationResponse* fakeResponse = CreateFakeResponse(newUserInfo);
+
+    NSDictionary* response = [UNNotificationResponseBuilder
+        buildDictionary:static_cast<UNNotificationResponse*>(fakeResponse)];
+    NSNumber* creatorPid =
+        [response objectForKey:notification_constants::kNotificationCreatorPid];
+    EXPECT_TRUE([creatorPid isEqualToNumber:@0]);
+  }
+}
+
+TEST(UNNotificationResponseBuilderMacTest, TestNotificationClick) {
+  if (@available(macOS 10.14, *)) {
+    base::scoped_nsobject<UNNotificationBuilder> builder =
+        NewTestBuilder(NotificationHandler::Type::WEB_PERSISTENT);
+    UNMutableNotificationContent* content = [builder buildUserNotification];
+    base::scoped_nsobject<NSMutableDictionary> userInfo(
+        [[content userInfo] mutableCopy]);
+
+    FakeUNNotificationResponse* fakeResponse = CreateFakeResponse(userInfo);
+
+    NSDictionary* response = [UNNotificationResponseBuilder
+        buildDictionary:static_cast<UNNotificationResponse*>(fakeResponse)];
+
+    NSNumber* operation =
+        [response objectForKey:notification_constants::kNotificationOperation];
+    NSNumber* buttonIndex = [response
+        objectForKey:notification_constants::kNotificationButtonIndex];
+
+    EXPECT_EQ(static_cast<int>(NotificationOperation::NOTIFICATION_CLICK),
+              operation.intValue);
+    EXPECT_EQ(notification_constants::kNotificationInvalidButtonIndex,
+              buttonIndex.intValue);
+  }
+}
+
+TEST(UNNotificationResponseBuilderMacTest, TestNotificationClose) {
+  if (@available(macOS 10.14, *)) {
+    base::scoped_nsobject<UNNotificationBuilder> builder =
+        NewTestBuilder(NotificationHandler::Type::WEB_PERSISTENT);
+    UNMutableNotificationContent* content = [builder buildUserNotification];
+    base::scoped_nsobject<NSMutableDictionary> userInfo(
+        [[content userInfo] mutableCopy]);
+
+    FakeUNNotificationResponse* fakeResponse = CreateFakeResponse(userInfo);
+    fakeResponse.actionIdentifier = UNNotificationDismissActionIdentifier;
+
+    NSDictionary* response = [UNNotificationResponseBuilder
+        buildDictionary:static_cast<UNNotificationResponse*>(fakeResponse)];
+
+    NSNumber* operation =
+        [response objectForKey:notification_constants::kNotificationOperation];
+    NSNumber* buttonIndex = [response
+        objectForKey:notification_constants::kNotificationButtonIndex];
+
+    EXPECT_EQ(static_cast<int>(NotificationOperation::NOTIFICATION_CLOSE),
+              operation.intValue);
+    EXPECT_EQ(notification_constants::kNotificationInvalidButtonIndex,
+              buttonIndex.intValue);
+  }
+}
diff --git a/chrome/browser/ui/passwords/settings/password_manager_presenter_unittest.cc b/chrome/browser/ui/passwords/settings/password_manager_presenter_unittest.cc
index 65956d3a..3fb6446 100644
--- a/chrome/browser/ui/passwords/settings/password_manager_presenter_unittest.cc
+++ b/chrome/browser/ui/passwords/settings/password_manager_presenter_unittest.cc
@@ -336,6 +336,27 @@
 }
 
 TEST_F(PasswordManagerPresenterTest,
+       ChangeSavedPasswordBySortKey_ChangeUsernameAndPasswordForAllEntities) {
+  char kMobileExampleCom[] = "https://m.example.com/";
+  AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
+  AddPasswordEntry(GURL(kMobileExampleCom), kUsername, kPassword);
+  EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(1)));
+  EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
+  UpdatePasswordLists();
+  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
+              ElementsAre(Pair(kUsername, kPassword)));
+  testing::Mock::VerifyAndClearExpectations(&GetUIController());
+
+  ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kNewUser,
+                               kNewPass);
+  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
+              ElementsAre(Pair(kNewUser, kNewPass)));
+  EXPECT_THAT(
+      GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kMobileExampleCom)),
+      ElementsAre(Pair(kNewUser, kNewPass)));
+}
+
+TEST_F(PasswordManagerPresenterTest,
        ChangeSavedPasswordBySortKey_RejectSameUsernameForSameRealm) {
   AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
   AddPasswordEntry(GURL(kExampleCom), kUsername2, kPassword2);
diff --git a/chrome/browser/ui/profile_picker.h b/chrome/browser/ui/profile_picker.h
index 2700775f..1326a8e7 100644
--- a/chrome/browser/ui/profile_picker.h
+++ b/chrome/browser/ui/profile_picker.h
@@ -7,10 +7,6 @@
 
 #include "third_party/skia/include/core/SkColor.h"
 
-namespace views {
-class WebView;
-}
-
 class ProfilePicker {
  public:
   // An entry point that triggers the profile picker window to open.
@@ -30,25 +26,18 @@
 
   // Starts the sign-in flow. The layout of the window gets updated for the
   // sign-in flow. At the same time, the new profile is created (with
-  // `profile_color`) and the sign-in page is rendered using the new profile.
-  // If the creation of the new profile fails, `switch_failure_callback` gets
+  // |profile_color|) and the sign-in page is rendered using the new profile.
+  // If the creation of the new profile fails, |switch_failure_callback| gets
   // called.
   static void SwitchToSignIn(SkColor profile_color,
                              base::OnceClosure switch_failure_callback);
 
-  // Finishes the sign-in flow by moving to the sync confirmation screen. It
-  // uses the same new profile created by `SwitchToSignIn()`.
-  static void SwitchToSyncConfirmation();
-
   // Hides the profile picker.
   static void Hide();
 
   // Returns whether the profile picker is currently open.
   static bool IsOpen();
 
-  // Returns the global profile picker instance for testing.
-  static views::WebView* GetWebViewForTesting();
-
  private:
   DISALLOW_COPY_AND_ASSIGN(ProfilePicker);
 };
diff --git a/chrome/browser/ui/views/chrome_typography.cc b/chrome/browser/ui/views/chrome_typography.cc
index 1b90797..62cedb0 100644
--- a/chrome/browser/ui/views/chrome_typography.cc
+++ b/chrome/browser/ui/views/chrome_typography.cc
@@ -100,8 +100,20 @@
       *size_delta = 15 - gfx::PlatformFont::kDefaultBaseFontSize;
       break;
 #endif
+    case CONTEXT_IPH_BUBBLE_TITLE: {
+      *size_delta = GetFontSizeDeltaIgnoringUserOrLocaleSettings(18);
+      break;
+    }
+    case CONTEXT_IPH_BUBBLE_BODY_WITH_TITLE: {
+      *size_delta = GetFontSizeDeltaIgnoringUserOrLocaleSettings(13);
+      break;
+    }
+    case CONTEXT_IPH_BUBBLE_BODY_WITHOUT_TITLE: {
+      *size_delta = GetFontSizeDeltaIgnoringUserOrLocaleSettings(14);
+      break;
+    }
     case CONTEXT_IPH_BUBBLE_BUTTON: {
-      *size_delta = GetFontSizeDeltaIgnoringUserOrLocaleSettings(12);
+      *size_delta = GetFontSizeDeltaIgnoringUserOrLocaleSettings(13);
       break;
     }
   }
diff --git a/chrome/browser/ui/views/chrome_typography.h b/chrome/browser/ui/views/chrome_typography.h
index b01bcb6f..cfe3b72 100644
--- a/chrome/browser/ui/views/chrome_typography.h
+++ b/chrome/browser/ui/views/chrome_typography.h
@@ -56,7 +56,16 @@
   // Status labels in the download shelf.  Usually 10pt.
   CONTEXT_DOWNLOAD_SHELF_STATUS,
 
-  // Button label in the IPH bubble.
+  // Title label in the IPH bubble. Usually 18pt.
+  CONTEXT_IPH_BUBBLE_TITLE,
+
+  // Body text label in the IPH bubble when a title exists. Usually 13pt.
+  CONTEXT_IPH_BUBBLE_BODY_WITH_TITLE,
+
+  // Body text label in the IPH bubble when no title exists. Usually 14pt.
+  CONTEXT_IPH_BUBBLE_BODY_WITHOUT_TITLE,
+
+  // Button label in the IPH bubble. Usually 13pt.
   CONTEXT_IPH_BUBBLE_BUTTON
 };
 
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
index 69858932..b8d244c1 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
@@ -243,6 +243,7 @@
   SetButtonLabel(ui::DIALOG_BUTTON_OK, prompt_->GetAcceptButtonLabel());
   SetButtonLabel(ui::DIALOG_BUTTON_CANCEL, prompt_->GetAbortButtonLabel());
   set_close_on_deactivate(false);
+  SetShowCloseButton(false);
   CreateContents();
 
   UMA_HISTOGRAM_ENUMERATION("Extensions.InstallPrompt.Type2", prompt_->type(),
@@ -397,10 +398,6 @@
   return true;
 }
 
-bool ExtensionInstallDialogView::ShouldShowCloseButton() const {
-  return true;
-}
-
 void ExtensionInstallDialogView::CloseDialog() {
   GetWidget()->Close();
 }
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.h b/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
index fe0cabd..89b1d23a 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
@@ -58,7 +58,6 @@
   void VisibilityChanged(views::View* starting_from, bool is_visible) override;
   void AddedToWidget() override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
-  bool ShouldShowCloseButton() const override;
 
  private:
   void CloseDialog();
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
index 4dfdefe..7afc88e 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
@@ -232,6 +232,9 @@
     // cancel.
     ExtensionInstallPromptTestHelper helper;
     ExtensionInstallDialogView* delegate_view = CreateAndShowPrompt(&helper);
+    // Note that the close button isn't present, but the dialog can still be
+    // closed this way via Esc.
+    EXPECT_FALSE(delegate_view->ShouldShowCloseButton());
     CloseAndWait(delegate_view->GetWidget());
     // TODO(devlin): Should this be ABORTED?
     EXPECT_EQ(ExtensionInstallPrompt::Result::USER_CANCELED, helper.result());
diff --git a/chrome/browser/ui/views/frame/desktop_linux_browser_frame_view_layout_unittest.cc b/chrome/browser/ui/views/frame/desktop_linux_browser_frame_view_layout_unittest.cc
index e2c2eae..c390151 100644
--- a/chrome/browser/ui/views/frame/desktop_linux_browser_frame_view_layout_unittest.cc
+++ b/chrome/browser/ui/views/frame/desktop_linux_browser_frame_view_layout_unittest.cc
@@ -52,6 +52,7 @@
   bool IsRegularOrGuestSession() const override { return true; }
   bool IsMaximized() const override { return false; }
   bool IsMinimized() const override { return false; }
+  bool IsFullscreen() const override { return false; }
   bool IsTabStripVisible() const override { return true; }
   int GetTabStripHeight() const override {
     return GetLayoutConstant(TAB_HEIGHT);
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
index b936578e8..f5cee2af 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -37,6 +37,7 @@
 #include "ui/display/win/screen_win.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/dip_util.h"
+#include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/icon_util.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/scoped_canvas.h"
@@ -310,8 +311,17 @@
                                         DWMWA_CAPTION_BUTTON_BOUNDS,
                                         &button_bounds,
                                         sizeof(button_bounds)))) {
-      gfx::Rect buttons = GetMirroredRect(gfx::ConvertRectToDIP(
-          display::win::GetDPIScale(), gfx::Rect(button_bounds)));
+      gfx::RectF button_bounds_in_dips = gfx::ConvertRectToDips(
+          gfx::Rect(button_bounds), display::win::GetDPIScale());
+      // TODO(crbug.com/1131681): GetMirroredRect() requires an integer rect,
+      // but the size in DIPs may not be an integer with a fractional device
+      // scale factor. If we want to keep using integers, the choice to use
+      // ToFlooredRectDeprecated() seems to be doing the wrong thing given the
+      // comment below about insetting 1 DIP instead of 1 physical pixel. We
+      // should probably use ToEnclosedRect() and then we could have inset 1
+      // physical pixel here.
+      gfx::Rect buttons =
+          GetMirroredRect(gfx::ToFlooredRectDeprecated(button_bounds_in_dips));
 
       // There is a small one-pixel strip right above the caption buttons in
       // which the resize border "peeks" through.
@@ -648,8 +658,14 @@
     window_icon_->SetVisible(show_icon);
   if (window_title_)
     window_title_->SetVisible(show_title);
-  if (!show_icon && !show_title && !web_app_frame_toolbar())
+  if (!show_icon && !show_title &&
+      (!web_app_frame_toolbar() || frame()->IsFullscreen())) {
+    // TODO(crbug.com/1132767): The "frame()->IsFullscreen()" term is required
+    // because we cannot currently lay out the toolbar in fullscreen mode
+    // without breaking a bunch of bubble anchoring. Please remove when the
+    // issue is resolved.
     return;
+  }
 
   const int icon_size =
       display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYSMICON);
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
index b80f5673..a63f046 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
@@ -433,6 +433,10 @@
   return frame()->IsMinimized();
 }
 
+bool OpaqueBrowserFrameView::IsFullscreen() const {
+  return frame()->IsFullscreen();
+}
+
 bool OpaqueBrowserFrameView::IsTabStripVisible() const {
   return browser_view()->IsTabStripVisible();
 }
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
index a26a2ea..c94eb1bf 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
@@ -95,6 +95,7 @@
   bool IsRegularOrGuestSession() const override;
   bool IsMaximized() const override;
   bool IsMinimized() const override;
+  bool IsFullscreen() const override;
   bool IsTabStripVisible() const override;
   int GetTabStripHeight() const override;
   bool IsToolbarVisible() const override;
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc
index d79e0a7..be5e07a 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc
@@ -278,9 +278,17 @@
   int size = delegate_->GetIconSize();
   bool should_show_icon = delegate_->ShouldShowWindowIcon() && window_icon_;
   bool should_show_title = delegate_->ShouldShowWindowTitle() && window_title_;
+  // TODO(crbug.com/1132767): fullscreen check is required only because we
+  // cannot allow toolbar to lay out in fullscreen mode without breaking some
+  // bubble anchoring because of how e.g. the zoom bubble anchors. If this
+  // issue is resolved, all of the references to |should_show_toolbar| can
+  // potentially be replaced with checks that |web_app_frame_toolbar_| is
+  // non-null.
+  bool should_show_toolbar =
+      !delegate_->IsFullscreen() && web_app_frame_toolbar_;
   base::Optional<int> icon_spacing;
 
-  if (should_show_icon || should_show_title || web_app_frame_toolbar_) {
+  if (should_show_icon || should_show_title || should_show_toolbar) {
     use_hidden_icon_location = false;
 
     // Our frame border has a different "3D look" than Windows'.  Theirs has
@@ -305,7 +313,7 @@
     // first element in the frame. We'll use this spacing again to ensure
     // appropriate spacing between icon and title.
     icon_spacing = y;
-    if (web_app_frame_toolbar_ && leading_buttons_.empty())
+    if (should_show_toolbar && leading_buttons_.empty())
       available_space_leading_x_ = FrameSideThickness(false) + *icon_spacing;
     else
       available_space_leading_x_ += kIconLeftSpacing;
@@ -314,7 +322,7 @@
     available_space_leading_x_ += size;
     minimum_size_for_buttons_ += size;
 
-    if (web_app_frame_toolbar_) {
+    if (should_show_toolbar) {
       std::pair<int, int> remaining_bounds =
           web_app_frame_toolbar_->LayoutInContainer(available_space_leading_x_,
                                                     available_space_trailing_x_,
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_delegate.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_delegate.h
index dc73c32e..6167f864 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_delegate.h
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_delegate.h
@@ -47,6 +47,7 @@
   // Controls window state.
   virtual bool IsMaximized() const = 0;
   virtual bool IsMinimized() const = 0;
+  virtual bool IsFullscreen() const = 0;
 
   virtual bool IsTabStripVisible() const = 0;
   virtual int GetTabStripHeight() const = 0;
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc
index 1949435..8fb8979 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc
@@ -62,6 +62,7 @@
   bool IsRegularOrGuestSession() const override { return true; }
   bool IsMaximized() const override { return maximized_; }
   bool IsMinimized() const override { return false; }
+  bool IsFullscreen() const override { return false; }
   bool IsTabStripVisible() const override { return window_title_.empty(); }
   int GetTabStripHeight() const override {
     return IsTabStripVisible() ? GetLayoutConstant(TAB_HEIGHT) : 0;
diff --git a/chrome/browser/ui/views/in_product_help/feature_promo_bubble_view.cc b/chrome/browser/ui/views/in_product_help/feature_promo_bubble_view.cc
index 0976163..6fffbeaeb 100644
--- a/chrome/browser/ui/views/in_product_help/feature_promo_bubble_view.cc
+++ b/chrome/browser/ui/views/in_product_help/feature_promo_bubble_view.cc
@@ -34,6 +34,7 @@
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/layout_provider.h"
 #include "ui/views/style/platform_style.h"
+#include "ui/views/style/typography.h"
 #include "ui/views/view_class_properties.h"
 
 namespace {
@@ -166,40 +167,45 @@
       ThemeProperties::COLOR_FEATURE_PROMO_BUBBLE_BACKGROUND);
   const SkColor text_color = theme_provider->GetColor(
       ThemeProperties::COLOR_FEATURE_PROMO_BUBBLE_TEXT);
-  const int vertical_spacing = layout_provider->GetDistanceMetric(
+  const int text_vertical_spacing = layout_provider->GetDistanceMetric(
+      views::DISTANCE_RELATED_CONTROL_VERTICAL);
+  const int button_vertical_spacing = layout_provider->GetDistanceMetric(
       views::DISTANCE_UNRELATED_CONTROL_VERTICAL);
 
   auto box_layout = std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical, kBubbleContentsInsets,
-      vertical_spacing);
+      text_vertical_spacing);
   box_layout->set_main_axis_alignment(
       views::BoxLayout::MainAxisAlignment::kCenter);
   box_layout->set_cross_axis_alignment(
       views::BoxLayout::CrossAxisAlignment::kStretch);
   SetLayoutManager(std::move(box_layout));
 
+  ChromeTextContext body_label_context;
   if (params.title_string_specifier.has_value()) {
     auto* title_label = AddChildView(std::make_unique<views::Label>(
-        l10n_util::GetStringUTF16(params.title_string_specifier.value())));
+        l10n_util::GetStringUTF16(params.title_string_specifier.value()),
+        ChromeTextContext::CONTEXT_IPH_BUBBLE_TITLE));
     title_label->SetBackgroundColor(background_color);
     title_label->SetEnabledColor(text_color);
-    title_label->SetFontList(views::style::GetFont(
-        views::style::CONTEXT_DIALOG_TITLE, views::style::STYLE_PRIMARY));
+    title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
 
-    if (params.preferred_width.has_value()) {
+    if (params.preferred_width.has_value())
       title_label->SetMultiLine(true);
-      title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-    }
+
+    body_label_context = CONTEXT_IPH_BUBBLE_BODY_WITH_TITLE;
+  } else {
+    body_label_context = CONTEXT_IPH_BUBBLE_BODY_WITHOUT_TITLE;
   }
 
-  auto* body_label = AddChildView(std::make_unique<views::Label>(body_text));
+  auto* body_label = AddChildView(
+      std::make_unique<views::Label>(body_text, body_label_context));
   body_label->SetBackgroundColor(background_color);
   body_label->SetEnabledColor(text_color);
+  body_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
 
-  if (params.preferred_width.has_value()) {
+  if (params.preferred_width.has_value())
     body_label->SetMultiLine(true);
-    body_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  }
 
   if (snoozable_) {
     auto* button_container = AddChildView(std::make_unique<views::View>());
@@ -209,6 +215,8 @@
 
     button_layout->set_main_axis_alignment(
         views::BoxLayout::MainAxisAlignment::kEnd);
+    button_container->SetProperty(
+        views::kMarginsKey, gfx::Insets(button_vertical_spacing, 0, 0, 0));
 
     const base::string16 snooze_text =
         l10n_util::GetStringUTF16(IDS_PROMO_SNOOZE_BUTTON);
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.cc b/chrome/browser/ui/views/profiles/profile_picker_view.cc
index b81043c..88ab9e13 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.cc
@@ -8,7 +8,6 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_functions.h"
-#include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
@@ -17,21 +16,13 @@
 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_window.h"
-#include "chrome/browser/profiles/profiles_state.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.h"
-#include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h"
 #include "chrome/browser/ui/webui/signin/profile_picker_ui.h"
-#include "chrome/browser/ui/webui/signin/signin_web_dialog_ui.h"
-#include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/google_chrome_strings.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
-#include "components/signin/public/identity_manager/account_info.h"
 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
 #include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/render_frame_host.h"
@@ -91,13 +82,6 @@
 }
 
 // static
-void ProfilePicker::SwitchToSyncConfirmation() {
-  if (g_profile_picker_view) {
-    g_profile_picker_view->SwitchToSyncConfirmation();
-  }
-}
-
-// static
 void ProfilePicker::Hide() {
   if (g_profile_picker_view)
     g_profile_picker_view->Clear();
@@ -108,11 +92,6 @@
   return g_profile_picker_view;
 }
 
-// static
-views::WebView* ProfilePicker::GetWebViewForTesting() {
-  return g_profile_picker_view->web_view_;
-}
-
 ProfilePickerView::ProfilePickerView()
     : keep_alive_(KeepAliveOrigin::USER_MANAGER_VIEW,
                   KeepAliveRestartOption::DISABLED) {
@@ -242,7 +221,7 @@
 
   DCHECK(profile);
 
-  ProfileAttributesEntry* entry = nullptr;
+  ProfileAttributesEntry* entry;
   if (!g_browser_process->profile_manager()
            ->GetProfileAttributesStorage()
            .GetProfileAttributesWithPath(profile->GetPath(), &entry)) {
@@ -258,25 +237,6 @@
   auto* theme_service = ThemeServiceFactory::GetForProfile(profile);
   theme_service->BuildAutogeneratedThemeFromColor(profile_color);
 
-  // TODO(crbug.com/1126913): Record also that we show the sign-in promo
-  // (it has to be plumbed from js to profile_picker_handler.cc):
-  //   signin_metrics::RecordSigninImpressionUserActionForAccessPoint(
-  //       signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER);
-
-  // Record that the sign in process starts (its end is recorded automatically
-  // by the instance of DiceTurnSyncOnHelper constructed later on).
-  signin_metrics::RecordSigninUserActionForAccessPoint(
-      signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER,
-      signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO);
-  signin_metrics::LogSigninAccessPointStarted(
-      signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER,
-      signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO);
-
-  // Listen for sign-in getting completed.
-  identity_manager_observer_.Add(
-      IdentityManagerFactory::GetForProfile(profile));
-  profile_being_created_ = profile;
-
   // Rebuild the view.
   // TODO(crbug.com/1126913): Add the simple toolbar with the back button.
   RemoveAllChildViews(true);
@@ -288,18 +248,6 @@
   web_view_->RequestFocus();
 }
 
-void ProfilePickerView::SwitchToSyncConfirmation() {
-  // TODO(crbug.com/1126913): Remove the simple toolbar with the back button
-  // (once it is added for the GAIA part).
-  web_view_->LoadInitialURL(GURL(chrome::kChromeUISyncConfirmationURL));
-  web_view_->RequestFocus();
-
-  SyncConfirmationUI* sync_confirmation_ui = static_cast<SyncConfirmationUI*>(
-      web_view_->GetWebContents()->GetWebUI()->GetController());
-  sync_confirmation_ui->InitializeMessageHandlerWithProfile(
-      profile_being_created_);
-}
-
 gfx::Size ProfilePickerView::CalculatePreferredSize() const {
   gfx::Size preferred_size = gfx::Size(kWindowWidth, kWindowHeight);
   gfx::Size work_area_size = GetWidget()->GetWorkAreaBoundsInScreen().size();
@@ -333,114 +281,3 @@
   // Ignores context menu.
   return true;
 }
-
-void ProfilePickerView::OnRefreshTokenUpdatedForAccount(
-    const CoreAccountInfo& account_info) {
-  DCHECK(!account_info.IsEmpty());
-
-  base::OnceClosure sync_consent_completed_closure =
-      base::BindOnce(&ProfilePickerView::FinishSignedInCreationFlow,
-                     weak_ptr_factory_.GetWeakPtr(), profile_being_created_,
-                     BrowserOpenedCallback());
-
-  // DiceTurnSyncOnHelper deletes itself once done.
-  new DiceTurnSyncOnHelper(
-      profile_being_created_,
-      signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER,
-      signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO,
-      signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT,
-      account_info.account_id,
-      DiceTurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT,
-      std::make_unique<ProfilePickerViewSyncDelegate>(
-          profile_being_created_,
-          base::BindOnce(&ProfilePickerView::FinishSignedInCreationFlow,
-                         weak_ptr_factory_.GetWeakPtr())),
-      std::move(sync_consent_completed_closure));
-}
-
-void ProfilePickerView::OnExtendedAccountInfoUpdated(
-    const AccountInfo& account_info) {
-  if (!account_info.IsValid())
-    return;
-  account_info_ = account_info;
-
-  // Stop listening to further changes.
-  identity_manager_observer_.Remove(
-      IdentityManagerFactory::GetForProfile(profile_being_created_));
-
-  if (on_account_info_available_)
-    std::move(on_account_info_available_).Run();
-}
-
-void ProfilePickerView::FinishSignedInCreationFlow(
-    Profile* profile,
-    BrowserOpenedCallback callback) {
-  // This can get called first time from a special case handling (such as the
-  // Settings link) and than second time when the consent flow finishes. We need
-  // to make sure only the first call gets handled.
-  if (!profile_being_created_)
-    return;
-  profile_being_created_ = nullptr;
-
-  if (!account_info_.IsValid()) {
-    // TODO(crbug.com/1126913): Add a timeout so that Chrome deals with cases
-    // when the extended info is never available (firewall, etc).
-    on_account_info_available_ = base::BindOnce(
-        &ProfilePickerView::FinishSignedInCreationFlowImpl,
-        weak_ptr_factory_.GetWeakPtr(), profile, std::move(callback));
-    return;
-  }
-
-  FinishSignedInCreationFlowImpl(profile, std::move(callback));
-}
-
-void ProfilePickerView::FinishSignedInCreationFlowImpl(
-    Profile* profile,
-    BrowserOpenedCallback callback) {
-  DCHECK(account_info_.IsValid());
-
-  ProfileAttributesEntry* entry = nullptr;
-  if (!g_browser_process->profile_manager()
-           ->GetProfileAttributesStorage()
-           .GetProfileAttributesWithPath(profile->GetPath(), &entry)) {
-    NOTREACHED();
-    return;
-  }
-
-  // Unmark this profile ephemeral so that it is not deleted upon next startup.
-  entry->SetIsEphemeral(false);
-
-  // Set the profile name
-  entry->SetLocalProfileName(
-      profiles::GetDefaultNameForNewSignedInProfile(account_info_));
-
-  // TODO(crbug.com/1126913): Change the callback of
-  // profiles::OpenBrowserWindowForProfile() to be a OnceCallback as it is only
-  // called once.
-  profiles::OpenBrowserWindowForProfile(
-      base::AdaptCallbackForRepeating(
-          base::BindOnce(&ProfilePickerView::OnBrowserOpened,
-                         weak_ptr_factory_.GetWeakPtr(), std::move(callback))),
-      /*always_create=*/false,   // Don't create a window if one already exists.
-      /*is_new_profile=*/false,  // Don't create a first run window.
-      /*unblock_extensions=*/false,  // There is no need to unblock all
-                                     // extensions because we only open browser
-                                     // window if the Profile is not locked.
-                                     // Hence there is no extension blocked.
-      profile, Profile::CREATE_STATUS_INITIALIZED);
-}
-
-void ProfilePickerView::OnBrowserOpened(
-    BrowserOpenedCallback finish_flow_callback,
-    Profile* profile,
-    Profile::CreateStatus profile_create_status) {
-  // Hide the picker view.
-  Clear();
-
-  if (!finish_flow_callback)
-    return;
-
-  Browser* browser = chrome::FindLastActiveWithProfile(profile);
-  DCHECK(browser);
-  std::move(finish_flow_callback).Run(browser);
-}
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.h b/chrome/browser/ui/views/profiles/profile_picker_view.h
index 9f5dbb68..a5d61d7 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.h
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.h
@@ -9,14 +9,10 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/profile_picker.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/window/dialog_delegate.h"
 
-struct AccountInfo;
-class Browser;
-
 namespace content {
 struct ContextMenuParams;
 class RenderFrameHost;
@@ -24,11 +20,7 @@
 
 // Dialog widget that contains the Desktop Profile picker webui.
 class ProfilePickerView : public views::DialogDelegateView,
-                          public content::WebContentsDelegate,
-                          public signin::IdentityManager::Observer {
- public:
-  using BrowserOpenedCallback = base::OnceCallback<void(Browser*)>;
-
+                          public content::WebContentsDelegate {
  private:
   friend class ProfilePicker;
 
@@ -62,8 +54,6 @@
   void OnProfileForSigninCreated(SkColor profile_color,
                                  Profile* new_profile,
                                  Profile::CreateStatus status);
-  // Switches the layout to the sync confirmation screen.
-  void SwitchToSyncConfirmation();
 
   // views::DialogDelegateView:
   gfx::Size CalculatePreferredSize() const override;
@@ -76,35 +66,12 @@
   bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
                          const content::ContextMenuParams& params) override;
 
-  // IdentityManager::Observer:
-  void OnRefreshTokenUpdatedForAccount(
-      const CoreAccountInfo& account_info) override;
-  void OnExtendedAccountInfoUpdated(const AccountInfo& account_info) override;
-
-  // Finishes the creation flow by marking `profile` as fully created, opening a
-  // browser window for `profile` and calling `callback`.
-  void FinishSignedInCreationFlow(Profile* profile,
-                                  BrowserOpenedCallback callback);
-  void FinishSignedInCreationFlowImpl(Profile* profile,
-                                      BrowserOpenedCallback callback);
-
-  // Internal callback to finish the last steps of the signed-in creation flow.
-  void OnBrowserOpened(BrowserOpenedCallback finish_flow_callback,
-                       Profile* profile,
-                       Profile::CreateStatus profile_create_status);
-
   ScopedKeepAlive keep_alive_;
   views::WebView* web_view_ = nullptr;
   InitState initialized_ = InitState::kNotInitialized;
-  Profile* profile_being_created_ = nullptr;
-
-  AccountInfo account_info_;
-  base::OnceClosure on_account_info_available_;
 
   // Not null iff switching to sign-in is in progress.
   base::OnceClosure switch_failure_callback_;
-  ScopedObserver<signin::IdentityManager, signin::IdentityManager::Observer>
-      identity_manager_observer_{this};
 
   // Creation time of the picker, to measure performance on startup. Only set
   // when the picker is shown on startup.
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
deleted file mode 100644
index 5abbb64b..0000000
--- a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright 2020 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/views/profiles/profile_picker_view.h"
-
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/mock_callback.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/policy/cloud/user_policy_signin_service.h"
-#include "chrome/browser/policy/cloud/user_policy_signin_service_factory.h"
-#include "chrome/browser/profiles/profile_attributes_entry.h"
-#include "chrome/browser/profiles/profile_attributes_storage.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/profiles/profile_manager_observer.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/themes/theme_service.h"
-#include "chrome/browser/themes/theme_service_factory.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/ui_features.h"
-#include "chrome/browser/ui/webui/signin/login_ui_service.h"
-#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/signin/public/identity_manager/account_info.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
-#include "components/signin/public/identity_manager/identity_test_utils.h"
-#include "components/sync/driver/sync_driver_switches.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "google_apis/gaia/gaia_urls.h"
-#include "third_party/skia/include/core/SkColor.h"
-
-namespace {
-
-AccountInfo FillAccountInfo(const CoreAccountInfo& core_info,
-                            const std::string& given_name) {
-  AccountInfo account_info;
-  account_info.email = core_info.email;
-  account_info.gaia = core_info.gaia;
-  account_info.account_id = core_info.account_id;
-  account_info.is_under_advanced_protection =
-      core_info.is_under_advanced_protection;
-  account_info.full_name = "Test Full Name";
-  account_info.given_name = given_name;
-  account_info.hosted_domain = kNoHostedDomainFound;
-  account_info.locale = "en";
-  account_info.picture_url = "https://get-avatar.com/foo";
-  account_info.is_child_account = false;
-  return account_info;
-}
-
-// Waits until a new profile is created.
-class ProfileWaiter : public ProfileManagerObserver {
- public:
-  ProfileWaiter() {
-    profile_manager_observer_.Add(g_browser_process->profile_manager());
-  }
-
-  ~ProfileWaiter() override = default;
-
-  Profile* WaitForProfileAdded() {
-    run_loop_.Run();
-    return profile_;
-  }
-
- private:
-  // ProfileManagerObserver:
-  void OnProfileAdded(Profile* profile) override {
-    profile_manager_observer_.RemoveAll();
-    profile_ = profile;
-    run_loop_.Quit();
-  }
-
-  Profile* profile_ = nullptr;
-  ScopedObserver<ProfileManager, ProfileManagerObserver>
-      profile_manager_observer_{this};
-  base::RunLoop run_loop_;
-};
-
-// Waits until a first non empty paint for given `url`.
-class FirstVisuallyNonEmptyPaintObserver : public content::WebContentsObserver {
- public:
-  explicit FirstVisuallyNonEmptyPaintObserver(content::WebContents* contents,
-                                              const GURL& url)
-      : content::WebContentsObserver(contents), url_(url) {}
-
-  void DidFirstVisuallyNonEmptyPaint() override {
-    if (web_contents()->GetVisibleURL() == url_)
-      run_loop_.Quit();
-  }
-
-  void Wait() { run_loop_.Run(); }
-
- private:
-  base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
-  GURL url_;
-};
-
-// Fake user policy signin service immediately invoking the callbacks.
-class FakeUserPolicySigninService : public policy::UserPolicySigninService {
- public:
-  static std::unique_ptr<KeyedService> Build(content::BrowserContext* context) {
-    Profile* profile = Profile::FromBrowserContext(context);
-    return std::make_unique<FakeUserPolicySigninService>(
-        profile, IdentityManagerFactory::GetForProfile(profile));
-  }
-
-  FakeUserPolicySigninService(Profile* profile,
-                              signin::IdentityManager* identity_manager)
-      : UserPolicySigninService(profile,
-                                nullptr,
-                                nullptr,
-                                nullptr,
-                                identity_manager,
-                                nullptr) {}
-
-  // policy::UserPolicySigninService:
-  void RegisterForPolicyWithAccountId(
-      const std::string& username,
-      const CoreAccountId& account_id,
-      PolicyRegistrationCallback callback) override {
-    std::move(callback).Run(dm_token_, client_id_);
-  }
-
-  // policy::UserPolicySigninServiceBase:
-  void FetchPolicyForSignedInUser(
-      const AccountId& account_id,
-      const std::string& dm_token,
-      const std::string& client_id,
-      scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory,
-      PolicyFetchCallback callback) override {
-    std::move(callback).Run(true);
-  }
-
- private:
-  std::string dm_token_;
-  std::string client_id_;
-};
-
-class ProfilePickerCreationFlowBrowserTest : public InProcessBrowserTest {
- public:
-  ProfilePickerCreationFlowBrowserTest() {
-    feature_list_.InitWithFeatures(
-        {features::kProfilesUIRevamp, features::kNewProfilePicker}, {});
-  }
-
-  void SetUpInProcessBrowserTestFixture() override {
-    InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
-    create_services_subscription_ =
-        BrowserContextDependencyManager::GetInstance()
-            ->RegisterCreateServicesCallbackForTesting(
-                base::Bind(&ProfilePickerCreationFlowBrowserTest::
-                               OnWillCreateBrowserContextServices,
-                           base::Unretained(this)));
-
-    // A hack for DiceTurnSyncOnHelper to actually skip talking to SyncService.
-    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-    command_line->AppendSwitch(switches::kDisableSync);
-  }
-
-  void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
-    policy::UserPolicySigninServiceFactory::GetInstance()->SetTestingFactory(
-        context, base::BindRepeating(&FakeUserPolicySigninService::Build));
-  }
-
-  content::WebContents* web_contents() {
-    return ProfilePicker::GetWebViewForTesting()->GetWebContents();
-  }
-
- private:
-  std::unique_ptr<
-      BrowserContextDependencyManager::CreateServicesCallbackList::Subscription>
-      create_services_subscription_;
-  base::test::ScopedFeatureList feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest, ShowChoice) {
-  ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
-  // Wait for web_contents() to get created.
-  base::RunLoop().RunUntilIdle();
-  FirstVisuallyNonEmptyPaintObserver(
-      web_contents(), GURL("chrome://profile-picker/new-profile"))
-      .Wait();
-}
-
-IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest,
-                       CreateSignedInProfile) {
-  const SkColor kProfileColor = SK_ColorRED;
-  ProfileManager* profile_manager = g_browser_process->profile_manager();
-  BrowserList* active_browser_list = BrowserList::GetInstance();
-
-  ASSERT_EQ(1u, profile_manager->GetNumberOfProfiles());
-  EXPECT_EQ(1u, active_browser_list->size());
-
-  ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(ProfilePicker::IsOpen());
-
-  // Simulate a click on the signin button.
-  base::MockCallback<base::OnceClosure> switch_failure_callback;
-  EXPECT_CALL(switch_failure_callback, Run()).Times(0);
-  ProfilePicker::SwitchToSignIn(kProfileColor, switch_failure_callback.Get());
-
-  // Need to wait for the new profile being created before waiting on
-  // web_contents() because web_contents is reconstructed with the new profile.
-  Profile* profile_being_created = ProfileWaiter().WaitForProfileAdded();
-  base::RunLoop().RunUntilIdle();
-  FirstVisuallyNonEmptyPaintObserver(
-      web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice())
-      .Wait();
-
-  // Add an account - simulate a successful Gaia sign-in.
-  signin::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForProfile(profile_being_created);
-  CoreAccountInfo core_account_info =
-      signin::MakeAccountAvailable(identity_manager, "joe.consumer@gmail.com");
-  ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(
-      core_account_info.account_id));
-
-  AccountInfo account_info = FillAccountInfo(core_account_info, "Joe");
-  signin::UpdateAccountInfoForAccount(identity_manager, account_info);
-
-  FirstVisuallyNonEmptyPaintObserver(web_contents(),
-                                     GURL("chrome://sync-confirmation/"))
-      .Wait();
-
-  // Simulate closing the UI with "Yes, I'm in".
-  LoginUIServiceFactory::GetForProfile(profile_being_created)
-      ->SyncConfirmationUIClosed(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS);
-  base::RunLoop().RunUntilIdle();
-
-  // Check expectations when the profile creation flow is done.
-  EXPECT_EQ(2u, active_browser_list->size());
-  EXPECT_FALSE(ProfilePicker::IsOpen());
-
-  ProfileAttributesEntry* entry = nullptr;
-  ASSERT_TRUE(g_browser_process->profile_manager()
-                  ->GetProfileAttributesStorage()
-                  .GetProfileAttributesWithPath(
-                      profile_being_created->GetPath(), &entry));
-  EXPECT_FALSE(entry->IsEphemeral());
-  EXPECT_EQ(entry->GetLocalProfileName(), base::UTF8ToUTF16("Joe"));
-  EXPECT_EQ(ThemeServiceFactory::GetForProfile(profile_being_created)
-                ->GetAutogeneratedThemeColor(),
-            kProfileColor);
-}
-
-}  // namespace
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.cc b/chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.cc
deleted file mode 100644
index d9de5580..0000000
--- a/chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2020 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/views/profiles/profile_picker_view_sync_delegate.h"
-
-#include "base/logging.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/profile_picker.h"
-#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
-#include "chrome/common/webui_url_constants.h"
-
-namespace {
-
-void OpenSettingsInBrowser(Browser* browser) {
-  chrome::ShowSettingsSubPage(browser, chrome::kSyncSetupSubPage);
-}
-
-}  // namespace
-
-ProfilePickerViewSyncDelegate::ProfilePickerViewSyncDelegate(
-    Profile* profile,
-    OpenBrowserCallback open_browser_callback)
-    : profile_(profile),
-      open_browser_callback_(std::move(open_browser_callback)) {}
-
-ProfilePickerViewSyncDelegate::~ProfilePickerViewSyncDelegate() = default;
-
-void ProfilePickerViewSyncDelegate::ShowLoginError(
-    const std::string& email,
-    const std::string& error_message) {
-  // TODO(crbug.com/1126913): Handle the error cases.
-  NOTIMPLEMENTED();
-}
-
-void ProfilePickerViewSyncDelegate::ShowMergeSyncDataConfirmation(
-    const std::string& previous_email,
-    const std::string& new_email,
-    DiceTurnSyncOnHelper::SigninChoiceCallback callback) {
-  // A brand new profile cannot have a conflict in sync accounts.
-  NOTREACHED();
-}
-
-void ProfilePickerViewSyncDelegate::ShowEnterpriseAccountConfirmation(
-    const std::string& email,
-    DiceTurnSyncOnHelper::SigninChoiceCallback callback) {
-  // TODO(crbug.com/1126913): Handle the error cases.
-  NOTIMPLEMENTED();
-}
-
-void ProfilePickerViewSyncDelegate::ShowSyncConfirmation(
-    base::OnceCallback<void(LoginUIService::SyncConfirmationUIClosedResult)>
-        callback) {
-  DCHECK(callback);
-  sync_confirmation_callback_ = std::move(callback);
-  scoped_login_ui_service_observer_.Add(
-      LoginUIServiceFactory::GetForProfile(profile_));
-  ProfilePicker::SwitchToSyncConfirmation();
-}
-
-void ProfilePickerViewSyncDelegate::ShowSyncSettings() {
-  // Open the browser and when it's done, open settings in the browser.
-  std::move(open_browser_callback_)
-      .Run(profile_, base::BindOnce(&OpenSettingsInBrowser));
-}
-
-void ProfilePickerViewSyncDelegate::SwitchToProfile(Profile* new_profile) {
-  // TODO(crbug.com/1126913): Handle this flow.
-  NOTIMPLEMENTED();
-}
-
-void ProfilePickerViewSyncDelegate::OnSyncConfirmationUIClosed(
-    LoginUIService::SyncConfirmationUIClosedResult result) {
-  // No need to listen to further confirmations any more.
-  scoped_login_ui_service_observer_.Remove(
-      LoginUIServiceFactory::GetForProfile(profile_));
-
-  DCHECK(sync_confirmation_callback_);
-  std::move(sync_confirmation_callback_).Run(result);
-}
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.h b/chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.h
deleted file mode 100644
index d5a4b51..0000000
--- a/chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_PROFILES_PROFILE_PICKER_VIEW_SYNC_DELEGATE_H_
-#define CHROME_BROWSER_UI_VIEWS_PROFILES_PROFILE_PICKER_VIEW_SYNC_DELEGATE_H_
-
-#include "base/scoped_observer.h"
-#include "chrome/browser/ui/views/profiles/profile_picker_view.h"
-#include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h"
-#include "chrome/browser/ui/webui/signin/login_ui_service.h"
-
-class Profile;
-
-// Handles the sync consent screen for creating a signed-in profile from the
-// profile picker.
-class ProfilePickerViewSyncDelegate : public DiceTurnSyncOnHelper::Delegate,
-                                      public LoginUIService::Observer {
- public:
-  using OpenBrowserCallback =
-      base::OnceCallback<void(Profile*,
-                              ProfilePickerView::BrowserOpenedCallback)>;
-
-  ProfilePickerViewSyncDelegate(Profile* profile,
-                                OpenBrowserCallback open_browser_callback);
-  ~ProfilePickerViewSyncDelegate() override;
-
- private:
-  // DiceTurnSyncOnHelper::Delegate:
-  void ShowLoginError(const std::string& email,
-                      const std::string& error_message) override;
-  void ShowMergeSyncDataConfirmation(
-      const std::string& previous_email,
-      const std::string& new_email,
-      DiceTurnSyncOnHelper::SigninChoiceCallback callback) override;
-  void ShowEnterpriseAccountConfirmation(
-      const std::string& email,
-      DiceTurnSyncOnHelper::SigninChoiceCallback callback) override;
-  void ShowSyncConfirmation(
-      base::OnceCallback<void(LoginUIService::SyncConfirmationUIClosedResult)>
-          callback) override;
-  void ShowSyncSettings() override;
-  void SwitchToProfile(Profile* new_profile) override;
-
-  // LoginUIService::Observer:
-  void OnSyncConfirmationUIClosed(
-      LoginUIService::SyncConfirmationUIClosedResult result) override;
-
-  Profile* profile_;
-  OpenBrowserCallback open_browser_callback_;
-  base::OnceCallback<void(LoginUIService::SyncConfirmationUIClosedResult)>
-      sync_confirmation_callback_;
-  ScopedObserver<LoginUIService, LoginUIService::Observer>
-      scoped_login_ui_service_observer_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(ProfilePickerViewSyncDelegate);
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_PROFILES_PROFILE_PICKER_VIEW_SYNC_DELEGATE_H_
diff --git a/chrome/browser/ui/views/webauthn/authenticator_qr_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_qr_sheet_view.cc
index 8053fd1..65e5f0c1 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_qr_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_qr_sheet_view.cc
@@ -231,7 +231,7 @@
 AuthenticatorQRSheetView::AuthenticatorQRSheetView(
     std::unique_ptr<AuthenticatorQRSheetModel> sheet_model)
     : AuthenticatorRequestSheetView(std::move(sheet_model)),
-      qr_generator_key_(reinterpret_cast<AuthenticatorQRSheetModel*>(model())
+      qr_generator_key_(static_cast<AuthenticatorQRSheetModel*>(model())
                             ->dialog_model()
                             ->qr_generator_key()) {}
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/internet_section.cc b/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
index 374f992..ce765b5c 100644
--- a/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
@@ -88,6 +88,18 @@
   return *tags;
 }
 
+const std::vector<SearchConcept>& GetEthernetNotConnectedSearchConcepts() {
+  static const base::NoDestructor<std::vector<SearchConcept>> tags({
+      {IDS_OS_SETTINGS_TAG_ETHERNET,
+       mojom::kNetworkSectionPath,
+       mojom::SearchResultIcon::kEthernet,
+       mojom::SearchResultDefaultRank::kMedium,
+       mojom::SearchResultType::kSection,
+       {.section = mojom::Section::kNetwork}},
+  });
+  return *tags;
+}
+
 const std::vector<SearchConcept>& GetWifiSearchConcepts() {
   static const base::NoDestructor<std::vector<SearchConcept>> tags({
       {IDS_OS_SETTINGS_TAG_WIFI,
@@ -871,6 +883,10 @@
   updater.RemoveSearchTags(GetInstantTetheringOnSearchConcepts());
   updater.RemoveSearchTags(GetInstantTetheringOffSearchConcepts());
 
+  // Keep track of ethernet devices to handle an edge case where Ethernet device
+  // is present but no network is connected.
+  does_ethernet_device_exist_ = false;
+
   for (const auto& device : devices) {
     switch (device->type) {
       case NetworkType::kWiFi:
@@ -899,6 +915,10 @@
           updater.AddSearchTags(GetInstantTetheringOffSearchConcepts());
         break;
 
+      case NetworkType::kEthernet:
+        does_ethernet_device_exist_ = true;
+        break;
+
       default:
         // Note: Ethernet and VPN only show search tags when connected, and
         // categories such as Mobile/Wireless do not have search tags.
@@ -923,6 +943,7 @@
   SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();
 
   updater.RemoveSearchTags(GetEthernetConnectedSearchConcepts());
+  updater.RemoveSearchTags(GetEthernetNotConnectedSearchConcepts());
   updater.RemoveSearchTags(GetWifiConnectedSearchConcepts());
   updater.RemoveSearchTags(GetWifiMeteredSearchConcepts());
   updater.RemoveSearchTags(GetCellularSearchConcepts());
@@ -990,6 +1011,12 @@
         break;
     }
   }
+
+  // Edge case where Ethernet device is present but no network is connected,
+  // i.e. on Chromeboxes. http://crbug.com/1096768
+  if (does_ethernet_device_exist_ && !connected_ethernet_guid_.has_value()) {
+    updater.AddSearchTags(GetEthernetNotConnectedSearchConcepts());
+  }
 }
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/chromeos/internet_section.h b/chrome/browser/ui/webui/settings/chromeos/internet_section.h
index cbe3547..6e803c4 100644
--- a/chrome/browser/ui/webui/settings/chromeos/internet_section.h
+++ b/chrome/browser/ui/webui/settings/chromeos/internet_section.h
@@ -75,6 +75,8 @@
   base::Optional<std::string> connected_tether_guid_;
   base::Optional<std::string> connected_vpn_guid_;
 
+  bool does_ethernet_device_exist_ = false;
+
   mojo::Receiver<network_config::mojom::CrosNetworkConfigObserver> receiver_{
       this};
   mojo::Remote<network_config::mojom::CrosNetworkConfig> cros_network_config_;
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 1eb508e..1a24e4ed 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -924,6 +924,7 @@
       {"editPasswordTitle", IDS_SETTINGS_PASSWORD_EDIT_TITLE},
       {"editPassword", IDS_SETTINGS_PASSWORD_EDIT},
       {"editPasswordFootnote", IDS_SETTINGS_PASSWORD_EDIT_FOOTNOTE},
+      {"usernameAlreadyUsed", IDS_SETTINGS_PASSWORD_USERNAME_ALREADY_USED},
       {"copyPassword", IDS_SETTINGS_PASSWORD_COPY},
       {"passwordStoredOnDevice", IDS_SETTINGS_PASSWORD_STORED_ON_DEVICE},
       {"passwordStoredInAccount", IDS_SETTINGS_PASSWORD_STORED_IN_ACCOUNT},
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc b/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
index 92894c6..1924e39c 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
@@ -37,14 +37,14 @@
 }  // namespace
 
 SyncConfirmationHandler::SyncConfirmationHandler(
-    Profile* profile,
-    const std::unordered_map<std::string, int>& string_to_grd_id_map,
-    Browser* browser)
-    : profile_(profile),
-      string_to_grd_id_map_(string_to_grd_id_map),
+    Browser* browser,
+    const std::unordered_map<std::string, int>& string_to_grd_id_map)
+    : profile_(browser->profile()),
       browser_(browser),
+      string_to_grd_id_map_(string_to_grd_id_map),
       identity_manager_(IdentityManagerFactory::GetForProfile(profile_)) {
   DCHECK(profile_);
+  DCHECK(browser_);
   BrowserList::AddObserver(this);
 }
 
@@ -219,6 +219,9 @@
     const base::ListValue* args) {
   AllowJavascript();
 
+  if (!browser_)
+    return;
+
   base::Optional<AccountInfo> primary_account_info =
       identity_manager_->FindExtendedAccountInfoForAccountWithRefreshToken(
           identity_manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired));
@@ -235,6 +238,5 @@
     SetUserImageURL(primary_account_info->picture_url);
   }
 
-  if (browser_)
-    signin::SetInitializedModalHeight(browser_, web_ui(), args);
+  signin::SetInitializedModalHeight(browser_, web_ui(), args);
 }
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_handler.h b/chrome/browser/ui/webui/signin/sync_confirmation_handler.h
index becbc76b..f80bf2e 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_handler.h
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_handler.h
@@ -29,14 +29,12 @@
                                 public signin::IdentityManager::Observer,
                                 public BrowserListObserver {
  public:
-  // Creates a SyncConfirmationHandler for the |profile|. All strings in the
+  // Creates a SyncConfirmationHandler for the |browser|. All strings in the
   // corresponding Web UI should be represented in |string_to_grd_id_map| and
-  // mapped to their GRD IDs. If |browser| is provided, its signin view
-  // controller will be notified of the rendered size of the web page.
+  // mapped to their GRD IDs.
   SyncConfirmationHandler(
-      Profile* profile,
-      const std::unordered_map<std::string, int>& string_to_grd_id_map,
-      Browser* browser = nullptr);
+      Browser* browser,
+      const std::unordered_map<std::string, int>& string_to_grd_id_map);
   ~SyncConfirmationHandler() override;
 
   // content::WebUIMessageHandler:
@@ -97,6 +95,9 @@
  private:
   Profile* profile_;
 
+  // Weak reference to the browser that showed the sync confirmation dialog.
+  Browser* browser_;
+
   // Records whether the user clicked on Undo, Ok, or Settings.
   bool did_user_explicitly_interact_ = false;
 
@@ -104,10 +105,6 @@
   // and their respective GRD IDs.
   std::unordered_map<std::string, int> string_to_grd_id_map_;
 
-  // Weak reference to the browser that showed the sync confirmation dialog (if
-  // such a dialog exists).
-  Browser* browser_;
-
   signin::IdentityManager* identity_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(SyncConfirmationHandler);
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc b/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
index 1f81eeed..ab71645 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
@@ -45,9 +45,7 @@
       Browser* browser,
       content::WebUI* web_ui,
       std::unordered_map<std::string, int> string_to_grd_id_map)
-      : SyncConfirmationHandler(browser->profile(),
-                                string_to_grd_id_map,
-                                browser) {
+      : SyncConfirmationHandler(browser, string_to_grd_id_map) {
     set_web_ui(web_ui);
   }
 
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc b/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
index 667dbd5..6e854c5ef 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/signin/sync_confirmation_handler.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
@@ -121,12 +120,7 @@
 
 void SyncConfirmationUI::InitializeMessageHandlerWithBrowser(Browser* browser) {
   web_ui()->AddMessageHandler(std::make_unique<SyncConfirmationHandler>(
-      browser->profile(), js_localized_string_to_ids_map_, browser));
-}
-
-void SyncConfirmationUI::InitializeMessageHandlerWithProfile(Profile* profile) {
-  web_ui()->AddMessageHandler(std::make_unique<SyncConfirmationHandler>(
-      profile, js_localized_string_to_ids_map_));
+      browser, js_localized_string_to_ids_map_));
 }
 
 void SyncConfirmationUI::AddStringResource(content::WebUIDataSource* source,
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_ui.h b/chrome/browser/ui/webui/signin/sync_confirmation_ui.h
index 5d1cdba..a560fb4 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_ui.h
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_ui.h
@@ -12,9 +12,6 @@
 #include "base/macros.h"
 #include "chrome/browser/ui/webui/signin/signin_web_dialog_ui.h"
 
-class Browser;
-class Profile;
-
 namespace content {
 class WebUIDataSource;
 }
@@ -35,10 +32,6 @@
   // SigninWebDialogUI:
   void InitializeMessageHandlerWithBrowser(Browser* browser) override;
 
-  // Initializes the message handler when there's no browser for `profile`
-  // available (such as in the profile creation flow).
-  void InitializeMessageHandlerWithProfile(Profile* profile);
-
  private:
   // Adds a string resource with the given GRD |ids| to the WebUI data |source|
   // named as |name|. Also stores a reverse mapping from the localized version
diff --git a/chrome/browser/webshare/win/scoped_fake_data_transfer_manager_interop.cc b/chrome/browser/webshare/win/scoped_fake_data_transfer_manager_interop.cc
new file mode 100644
index 0000000..7dadf63
--- /dev/null
+++ b/chrome/browser/webshare/win/scoped_fake_data_transfer_manager_interop.cc
@@ -0,0 +1,73 @@
+// Copyright 2020 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/webshare/win/scoped_fake_data_transfer_manager_interop.h"
+
+#include <windows.applicationmodel.datatransfer.h>
+#include <wrl/implements.h>
+
+#include "base/strings/string_piece.h"
+#include "base/win/com_init_util.h"
+#include "base/win/core_winrt_util.h"
+#include "base/win/win_util.h"
+#include "chrome/browser/webshare/win/fake_data_transfer_manager_interop.h"
+#include "chrome/browser/webshare/win/show_share_ui_for_window_operation.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace webshare {
+namespace {
+
+static FakeDataTransferManagerInterop* g_current_fake_interop = nullptr;
+
+static HRESULT FakeRoGetActivationFactory(HSTRING class_id,
+                                          const IID& iid,
+                                          void** out_factory) {
+  base::win::ScopedHString class_id_hstring(class_id);
+  EXPECT_STREQ(
+      class_id_hstring.Get().data(),
+      RuntimeClass_Windows_ApplicationModel_DataTransfer_DataTransferManager);
+  if (g_current_fake_interop == nullptr) {
+    ADD_FAILURE();
+    return E_UNEXPECTED;
+  }
+  *out_factory = g_current_fake_interop;
+  g_current_fake_interop->AddRef();
+  return S_OK;
+}
+
+}  // namespace
+
+ScopedFakeDataTransferManagerInterop::ScopedFakeDataTransferManagerInterop() {
+  // Initialization work is done in an independent function so that the
+  // various test macros can be used.
+  Initialize();
+}
+
+ScopedFakeDataTransferManagerInterop::~ScopedFakeDataTransferManagerInterop() {
+  g_current_fake_interop = nullptr;
+  ShowShareUIForWindowOperation::SetRoGetActivationFactoryFunctionForTesting(
+      &base::win::RoGetActivationFactory);
+}
+
+FakeDataTransferManagerInterop&
+ScopedFakeDataTransferManagerInterop::instance() {
+  return *(instance_.Get());
+}
+
+void ScopedFakeDataTransferManagerInterop::Initialize() {
+  ASSERT_TRUE(base::win::ResolveCoreWinRTDelayload());
+  ASSERT_TRUE(base::win::ScopedHString::ResolveCoreWinRTStringDelayload());
+  base::win::AssertComInitialized();
+
+  instance_ = Microsoft::WRL::Make<FakeDataTransferManagerInterop>();
+
+  // Confirm there is no competing instance and set this instance
+  // as the factory for the data_transfer_manager_util
+  ASSERT_EQ(g_current_fake_interop, nullptr);
+  g_current_fake_interop = instance_.Get();
+  ShowShareUIForWindowOperation::SetRoGetActivationFactoryFunctionForTesting(
+      &FakeRoGetActivationFactory);
+}
+
+}  // namespace webshare
diff --git a/chrome/browser/webshare/win/scoped_fake_data_transfer_manager_interop.h b/chrome/browser/webshare/win/scoped_fake_data_transfer_manager_interop.h
new file mode 100644
index 0000000..b5468b64
--- /dev/null
+++ b/chrome/browser/webshare/win/scoped_fake_data_transfer_manager_interop.h
@@ -0,0 +1,36 @@
+// Copyright 2020 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_WEBSHARE_WIN_SCOPED_FAKE_DATA_TRANSFER_MANAGER_INTEROP_H_
+#define CHROME_BROWSER_WEBSHARE_WIN_SCOPED_FAKE_DATA_TRANSFER_MANAGER_INTEROP_H_
+
+#include <wrl/client.h>
+
+namespace webshare {
+
+class FakeDataTransferManagerInterop;
+
+// Creates and registers a FakeDataTransferManagerInterop on creation and cleans
+// it up on tear down, allowing GTests to easily simulate the Windows APIs used
+// for the Share contract.
+class ScopedFakeDataTransferManagerInterop {
+ public:
+  ScopedFakeDataTransferManagerInterop();
+  ScopedFakeDataTransferManagerInterop(
+      const ScopedFakeDataTransferManagerInterop&) = delete;
+  ScopedFakeDataTransferManagerInterop& operator=(
+      const ScopedFakeDataTransferManagerInterop&) = delete;
+  ~ScopedFakeDataTransferManagerInterop();
+
+  FakeDataTransferManagerInterop& instance();
+
+ private:
+  void Initialize();
+
+  Microsoft::WRL::ComPtr<FakeDataTransferManagerInterop> instance_;
+};
+
+}  // namespace webshare
+
+#endif  // CHROME_BROWSER_WEBSHARE_WIN_SCOPED_FAKE_DATA_TRANSFER_MANAGER_INTEROP_H_
diff --git a/chrome/browser/webshare/win/show_share_ui_for_window_operation.cc b/chrome/browser/webshare/win/show_share_ui_for_window_operation.cc
new file mode 100644
index 0000000..8fcccef
--- /dev/null
+++ b/chrome/browser/webshare/win/show_share_ui_for_window_operation.cc
@@ -0,0 +1,171 @@
+// Copyright 2020 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/webshare/win/show_share_ui_for_window_operation.h"
+
+#include <shlobj.h>
+#include <windows.applicationmodel.datatransfer.h>
+#include <wrl/event.h>
+
+#include "base/callback.h"
+#include "base/task/post_task.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/win/core_winrt_util.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
+using ABI::Windows::ApplicationModel::DataTransfer::DataRequestedEventArgs;
+using ABI::Windows::ApplicationModel::DataTransfer::DataTransferManager;
+using ABI::Windows::ApplicationModel::DataTransfer::IDataRequestedEventArgs;
+using ABI::Windows::ApplicationModel::DataTransfer::IDataTransferManager;
+using ABI::Windows::Foundation::ITypedEventHandler;
+using Microsoft::WRL::Callback;
+using Microsoft::WRL::ComPtr;
+
+namespace webshare {
+namespace {
+
+decltype(
+    &base::win::RoGetActivationFactory) ro_get_activation_factory_function_ =
+    &base::win::RoGetActivationFactory;
+
+// Fetches handles to the IDataTransferManager[Interop] instances for the
+// given |hwnd|
+HRESULT GetDataTransferManagerHandles(
+    HWND hwnd,
+    IDataTransferManagerInterop** data_transfer_manager_interop,
+    IDataTransferManager** data_transfer_manager) {
+  // If the required WinRT functionality is not available, fail the operation
+  if (!base::win::ResolveCoreWinRTDelayload() ||
+      !base::win::ScopedHString::ResolveCoreWinRTStringDelayload()) {
+    return E_FAIL;
+  }
+
+  // IDataTransferManagerInterop is semi-hidden behind a CloakedIid
+  // structure on the DataTransferManager, excluding it from things
+  // used by RoGetActivationFactory like GetIids(). Because of this,
+  // the safe way to fetch a pointer to it is through a publicly
+  // supported IID (e.g. IUnknown), followed by a QueryInterface call
+  // (or something that simply wraps it like As()) to convert it.
+  auto class_id_hstring = base::win::ScopedHString::Create(
+      RuntimeClass_Windows_ApplicationModel_DataTransfer_DataTransferManager);
+  if (!class_id_hstring.is_valid())
+    return E_FAIL;
+
+  ComPtr<IUnknown> data_transfer_manager_factory;
+  HRESULT hr = ro_get_activation_factory_function_(
+      class_id_hstring.get(), IID_PPV_ARGS(&data_transfer_manager_factory));
+  if (FAILED(hr))
+    return hr;
+
+  hr = data_transfer_manager_factory->QueryInterface(
+      data_transfer_manager_interop);
+  if (FAILED(hr))
+    return hr;
+
+  hr = (*data_transfer_manager_interop)
+           ->GetForWindow(hwnd, IID_PPV_ARGS(data_transfer_manager));
+  return hr;
+}
+}  // namespace
+
+ShowShareUIForWindowOperation::ShowShareUIForWindowOperation(HWND hwnd)
+    : hwnd_(hwnd) {
+  data_requested_token_.value = 0;
+}
+
+ShowShareUIForWindowOperation::~ShowShareUIForWindowOperation() {
+  Cancel();
+}
+
+// static
+void ShowShareUIForWindowOperation::SetRoGetActivationFactoryFunctionForTesting(
+    decltype(&base::win::RoGetActivationFactory) value) {
+  ro_get_activation_factory_function_ = value;
+}
+
+void ShowShareUIForWindowOperation::Run(
+    DataRequestedCallback data_requested_callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  data_requested_callback_ = std::move(data_requested_callback);
+
+  // Fetch the OS handles needed
+  ComPtr<IDataTransferManagerInterop> data_transfer_manager_interop;
+  HRESULT hr = GetDataTransferManagerHandles(
+      hwnd_, &data_transfer_manager_interop, &data_transfer_manager_);
+  if (FAILED(hr))
+    return Cancel();
+
+  // Create and register a data request handler
+  auto weak_ptr = weak_factory_.GetWeakPtr();
+  auto raw_data_requested_callback = Callback<
+      ITypedEventHandler<DataTransferManager*, DataRequestedEventArgs*>>(
+      [weak_ptr](IDataTransferManager* data_transfer_manager,
+                 IDataRequestedEventArgs* event_args) -> HRESULT {
+        DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+        if (weak_ptr)
+          weak_ptr.get()->OnDataRequested(data_transfer_manager, event_args);
+
+        // Always return S_OK, as returning a FAILED value results in the OS
+        // killing this process. If the data population failed the OS Share
+        // operation will fail gracefully with messaging to the user.
+        return S_OK;
+      });
+  hr = data_transfer_manager_->add_DataRequested(
+      raw_data_requested_callback.Get(), &data_requested_token_);
+  if (FAILED(hr))
+    return Cancel();
+
+  // Request showing the Share UI
+  show_share_ui_for_window_call_in_progress_ = true;
+  hr = data_transfer_manager_interop->ShowShareUIForWindow(hwnd_);
+  show_share_ui_for_window_call_in_progress_ = false;
+
+  // If the call is expected to complete later, schedule a timeout to cover
+  // any cases where it fails (and therefore never comes)
+  if (SUCCEEDED(hr) && data_requested_callback_) {
+    if (!base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+            FROM_HERE,
+            base::BindOnce(&ShowShareUIForWindowOperation::Cancel,
+                           weak_factory_.GetWeakPtr()),
+            kMaxExecutionTime)) {
+      return Cancel();
+    }
+  } else {
+    RemoveDataRequestedListener();
+  }
+}
+
+void ShowShareUIForWindowOperation::Cancel() {
+  RemoveDataRequestedListener();
+  if (data_requested_callback_) {
+    std::move(data_requested_callback_).Run(nullptr);
+  }
+}
+
+void ShowShareUIForWindowOperation::OnDataRequested(
+    IDataTransferManager* data_transfer_manager,
+    IDataRequestedEventArgs* event_args) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK_EQ(data_transfer_manager, data_transfer_manager_.Get());
+
+  // Remove the DataRequested handler if this is being invoked asynchronously.
+  // If this is an in-progress invocation the system APIs don't handle the
+  // event being unregistered while it is being executed, but we will unregister
+  // it after the ShowShareUIForWindow call completes.
+  if (!show_share_ui_for_window_call_in_progress_)
+    RemoveDataRequestedListener();
+
+  std::move(data_requested_callback_).Run(event_args);
+}
+
+void ShowShareUIForWindowOperation::RemoveDataRequestedListener() {
+  if (data_transfer_manager_ && data_requested_token_.value) {
+    data_transfer_manager_->remove_DataRequested(data_requested_token_);
+    data_requested_token_.value = 0;
+  }
+}
+
+}  // namespace webshare
diff --git a/chrome/browser/webshare/win/show_share_ui_for_window_operation.h b/chrome/browser/webshare/win/show_share_ui_for_window_operation.h
new file mode 100644
index 0000000..220c0dd3
--- /dev/null
+++ b/chrome/browser/webshare/win/show_share_ui_for_window_operation.h
@@ -0,0 +1,88 @@
+// Copyright 2020 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_WEBSHARE_WIN_SHOW_SHARE_UI_FOR_WINDOW_OPERATION_H_
+#define CHROME_BROWSER_WEBSHARE_WIN_SHOW_SHARE_UI_FOR_WINDOW_OPERATION_H_
+
+#include <EventToken.h>
+#include <wrl/client.h>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/win/core_winrt_util.h"
+
+namespace ABI {
+namespace Windows {
+namespace ApplicationModel {
+namespace DataTransfer {
+class IDataRequestedEventArgs;
+class IDataTransferManager;
+}  // namespace DataTransfer
+}  // namespace ApplicationModel
+}  // namespace Windows
+}  // namespace ABI
+
+namespace webshare {
+
+// Represents a call to ShowShareUIForWindow in an async fashion.
+class ShowShareUIForWindowOperation {
+ public:
+  using DataRequestedCallback = base::OnceCallback<void(
+      ABI::Windows::ApplicationModel::DataTransfer::IDataRequestedEventArgs*)>;
+
+  explicit ShowShareUIForWindowOperation(const HWND hwnd);
+  ShowShareUIForWindowOperation(const ShowShareUIForWindowOperation&) = delete;
+  ShowShareUIForWindowOperation& operator=(
+      const ShowShareUIForWindowOperation&) = delete;
+  ~ShowShareUIForWindowOperation();
+
+  // Test hook for overriding the base RoGetActivationFactory function
+  static void SetRoGetActivationFactoryFunctionForTesting(
+      decltype(&base::win::RoGetActivationFactory) value);
+
+  static constexpr base::TimeDelta max_execution_time_for_testing() {
+    return kMaxExecutionTime;
+  }
+
+  // Requests the Window's Share operation for the previously supplied |hwnd|
+  // and uses the |data_requested_callback| to supply the operation with the
+  // data to share. This call does not impact the lifetime of this class, so the
+  // caller must keep this instance alive until it has completed or the caller
+  // no longer desires the operation to continue.
+  //
+  // The provided |data_requested_callback| will be invoked either
+  // synchronously as part of this call, or asynchronously at a
+  // later point when the OS Share operation requests it. In both cases, when
+  // the |data_requested_callback| is invoked it will be on the UI thread with
+  // |IDataRequestedEventArgs| from the OS. If an error is encountered the
+  // |data_requested_callback| will be invoked without any arguments.
+  //
+  // This should only be called from the UI thread.
+  void Run(DataRequestedCallback data_requested_callback);
+
+ private:
+  static constexpr base::TimeDelta kMaxExecutionTime =
+      base::TimeDelta::FromSeconds(30);
+
+  void Cancel();
+  void OnDataRequested(
+      ABI::Windows::ApplicationModel::DataTransfer::IDataTransferManager*
+          data_transfer_manager,
+      ABI::Windows::ApplicationModel::DataTransfer::IDataRequestedEventArgs*
+          event_args);
+  void RemoveDataRequestedListener();
+
+  DataRequestedCallback data_requested_callback_;
+  EventRegistrationToken data_requested_token_;
+  Microsoft::WRL::ComPtr<
+      ABI::Windows::ApplicationModel::DataTransfer::IDataTransferManager>
+      data_transfer_manager_;
+  const HWND hwnd_;
+  bool show_share_ui_for_window_call_in_progress_;
+  base::WeakPtrFactory<ShowShareUIForWindowOperation> weak_factory_{this};
+};
+
+}  // namespace webshare
+
+#endif  // CHROME_BROWSER_WEBSHARE_WIN_SHOW_SHARE_UI_FOR_WINDOW_OPERATION_H_
diff --git a/chrome/browser/webshare/win/show_share_ui_for_window_operation_unittest.cc b/chrome/browser/webshare/win/show_share_ui_for_window_operation_unittest.cc
new file mode 100644
index 0000000..cd79303b
--- /dev/null
+++ b/chrome/browser/webshare/win/show_share_ui_for_window_operation_unittest.cc
@@ -0,0 +1,182 @@
+// Copyright 2020 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/webshare/win/show_share_ui_for_window_operation.h"
+
+#include <shlobj.h>
+#include <windows.applicationmodel.datatransfer.h>
+#include <wrl/implements.h>
+#include <wrl/module.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/win/com_init_util.h"
+#include "base/win/core_winrt_util.h"
+#include "chrome/browser/webshare/win/fake_data_transfer_manager_interop.h"
+#include "chrome/browser/webshare/win/scoped_fake_data_transfer_manager_interop.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ABI::Windows::ApplicationModel::DataTransfer::IDataRequest;
+using ABI::Windows::ApplicationModel::DataTransfer::IDataRequestedEventArgs;
+using ABI::Windows::ApplicationModel::DataTransfer::IDataTransferManager;
+using Microsoft::WRL::ActivationFactory;
+using Microsoft::WRL::ComPtr;
+using Microsoft::WRL::RuntimeClass;
+using Microsoft::WRL::RuntimeClassFlags;
+
+namespace webshare {
+
+using DataRequestedCallback =
+    ShowShareUIForWindowOperation::DataRequestedCallback;
+using ShowShareUIForWindowBehavior =
+    FakeDataTransferManagerInterop::ShowShareUIForWindowBehavior;
+
+class ShowShareUIForWindowOperationTest : public ::testing::Test {
+ protected:
+  enum TestCallbackState { NotRun = 0, RunWithoutValue, RunWithValue };
+
+  bool IsSupportedEnvironment() {
+    return base::win::ResolveCoreWinRTDelayload() &&
+           base::win::ScopedHString::ResolveCoreWinRTStringDelayload();
+  }
+
+  void SetUp() override {
+    if (!IsSupportedEnvironment())
+      return;
+    scoped_interop_ = std::make_unique<ScopedFakeDataTransferManagerInterop>();
+    auto weak_ptr = weak_factory_.GetWeakPtr();
+    test_callback_ = base::BindOnce(
+        [](base::WeakPtr<ShowShareUIForWindowOperationTest> weak_ptr,
+           IDataRequestedEventArgs* event_args) {
+          if (weak_ptr) {
+            EXPECT_EQ(weak_ptr->test_callback_state_,
+                      TestCallbackState::NotRun);
+            weak_ptr->test_callback_state_ =
+                event_args ? TestCallbackState::RunWithValue
+                           : TestCallbackState::RunWithoutValue;
+          }
+        },
+        weak_ptr);
+  }
+
+  void TearDown() override {
+    if (!IsSupportedEnvironment())
+      return;
+    base::win::RoUninitialize();
+    ASSERT_FALSE(fake_interop().HasDataRequestedListener(hwnd_));
+  }
+
+  FakeDataTransferManagerInterop& fake_interop() {
+    return scoped_interop_->instance();
+  }
+
+  const HWND hwnd_ = reinterpret_cast<HWND>(1);
+  std::unique_ptr<ScopedFakeDataTransferManagerInterop> scoped_interop_;
+  content::BrowserTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  DataRequestedCallback test_callback_;
+  TestCallbackState test_callback_state_ = TestCallbackState::NotRun;
+  base::WeakPtrFactory<ShowShareUIForWindowOperationTest> weak_factory_{this};
+};
+
+TEST_F(ShowShareUIForWindowOperationTest, AsyncSuccess) {
+  if (!IsSupportedEnvironment())
+    return;
+
+  fake_interop().SetShowShareUIForWindowBehavior(
+      ShowShareUIForWindowBehavior::SucceedWithoutAction);
+
+  ShowShareUIForWindowOperation operation{hwnd_};
+  operation.Run(std::move(test_callback_));
+  ASSERT_EQ(test_callback_state_, TestCallbackState::NotRun);
+  auto data_requested_invoker = fake_interop().GetDataRequestedInvoker(hwnd_);
+
+  std::move(data_requested_invoker).Run();
+  ASSERT_EQ(test_callback_state_, TestCallbackState::RunWithValue);
+}
+
+TEST_F(ShowShareUIForWindowOperationTest, AsyncFailure) {
+  if (!IsSupportedEnvironment())
+    return;
+
+  fake_interop().SetShowShareUIForWindowBehavior(
+      ShowShareUIForWindowBehavior::SucceedWithoutAction);
+
+  ShowShareUIForWindowOperation operation{hwnd_};
+  operation.Run(std::move(test_callback_));
+  ASSERT_EQ(test_callback_state_, TestCallbackState::NotRun);
+
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  ASSERT_EQ(test_callback_state_, TestCallbackState::NotRun);
+
+  task_environment_.FastForwardBy(
+      ShowShareUIForWindowOperation::max_execution_time_for_testing());
+  ASSERT_EQ(test_callback_state_, TestCallbackState::RunWithoutValue);
+}
+
+TEST_F(ShowShareUIForWindowOperationTest, AsyncEarlyDestruction) {
+  if (!IsSupportedEnvironment())
+    return;
+
+  fake_interop().SetShowShareUIForWindowBehavior(
+      ShowShareUIForWindowBehavior::SucceedWithoutAction);
+
+  auto operation = std::make_unique<ShowShareUIForWindowOperation>(hwnd_);
+  operation->Run(std::move(test_callback_));
+  ASSERT_EQ(test_callback_state_, TestCallbackState::NotRun);
+
+  auto data_requested_invoker = fake_interop().GetDataRequestedInvoker(hwnd_);
+  ASSERT_NO_FATAL_FAILURE(operation.reset());
+  ASSERT_EQ(test_callback_state_, TestCallbackState::RunWithoutValue);
+  ASSERT_NO_FATAL_FAILURE(std::move(data_requested_invoker).Run());
+}
+
+TEST_F(ShowShareUIForWindowOperationTest, SyncSuccess) {
+  if (!IsSupportedEnvironment())
+    return;
+
+  fake_interop().SetShowShareUIForWindowBehavior(
+      ShowShareUIForWindowBehavior::InvokeEventSynchronously);
+
+  ShowShareUIForWindowOperation operation{hwnd_};
+  operation.Run(std::move(test_callback_));
+  ASSERT_EQ(test_callback_state_, TestCallbackState::RunWithValue);
+}
+
+TEST_F(ShowShareUIForWindowOperationTest, SyncEarlyFailure) {
+  if (!IsSupportedEnvironment())
+    return;
+
+  fake_interop().SetShowShareUIForWindowBehavior(
+      ShowShareUIForWindowBehavior::FailImmediately);
+
+  ShowShareUIForWindowOperation operation{hwnd_};
+  operation.Run(std::move(test_callback_));
+  ASSERT_EQ(test_callback_state_, TestCallbackState::NotRun);
+}
+
+TEST_F(ShowShareUIForWindowOperationTest, SyncLateFailure) {
+  if (!IsSupportedEnvironment())
+    return;
+
+  fake_interop().SetShowShareUIForWindowBehavior(
+      ShowShareUIForWindowBehavior::InvokeEventSynchronouslyAndReturnFailure);
+
+  ShowShareUIForWindowOperation operation{hwnd_};
+  operation.Run(std::move(test_callback_));
+  ASSERT_EQ(test_callback_state_, TestCallbackState::RunWithValue);
+}
+
+TEST_F(ShowShareUIForWindowOperationTest, DestructionWithoutRun) {
+  if (!IsSupportedEnvironment())
+    return;
+
+  auto operation = std::make_unique<ShowShareUIForWindowOperation>(hwnd_);
+  ASSERT_NO_FATAL_FAILURE(operation.reset());
+}
+
+}  // namespace webshare
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index e2c2c3c..34139259 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1601272286-5f5720137a0694fa0bfa67350ff5844644ba53c0.profdata
+chrome-linux-master-1601294375-f18d3a3ac9a6313011d7149de4bf7e7984859ae2.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 558b8af..f4ad854 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1601272286-d5061451d97ba33a632a304f531ce91f70d7428a.profdata
+chrome-mac-master-1601294375-805284c073a4c807365387c8b737f70c270f4966.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index dfc425d..5e687d70 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1601098956-2811654f54d3946c8e0477e7d77d36d5dbdfab8f.profdata
+chrome-win64-master-1601304808-a0959ded2b2639d64197cdec426ae296818dc3ae.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index bfb9b5b5..6dedaf5 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -726,10 +726,6 @@
                                          base::FEATURE_ENABLED_BY_DEFAULT};
 
 #if defined(OS_CHROMEOS)
-// Enables or disables automatic setup of USB printers.
-const base::Feature kStreamlinedUsbPrinterSetup{
-    "StreamlinedUsbPrinterSetup", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables or disables chrome://sys-internals.
 const base::Feature kSysInternals{"SysInternals",
                                   base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index d5a2e978..6948997 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -473,9 +473,6 @@
 
 #if defined(OS_CHROMEOS)
 COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kStreamlinedUsbPrinterSetup;
-
-COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kSysInternals;
 #endif
 
diff --git a/chrome/common/profiler/thread_profiler.cc b/chrome/common/profiler/thread_profiler.cc
index 7b9d7c6..f298d7c 100644
--- a/chrome/common/profiler/thread_profiler.cc
+++ b/chrome/common/profiler/thread_profiler.cc
@@ -14,6 +14,7 @@
 #include "base/message_loop/work_id_provider.h"
 #include "base/no_destructor.h"
 #include "base/process/process.h"
+#include "base/profiler/profiler_buildflags.h"
 #include "base/profiler/sample_metadata.h"
 #include "base/profiler/sampling_profiler_thread_token.h"
 #include "base/rand_util.h"
diff --git a/chrome/credential_provider/extension/BUILD.gn b/chrome/credential_provider/extension/BUILD.gn
index 76f20cb..2f87d4c 100644
--- a/chrome/credential_provider/extension/BUILD.gn
+++ b/chrome/credential_provider/extension/BUILD.gn
@@ -56,7 +56,7 @@
   ]
   deps = [
     ":common",
-    "../gaiacp:common",
+    "../gaiacp:util",
     "//base:base",
   ]
   configs += [ "//build/config/win:windowed" ]
@@ -71,6 +71,7 @@
     ":version",
     "../eventlog:gcp_eventlog_messages",
     "../gaiacp:common",
+    "../gaiacp:util",
     "//base",
     "//components/crash/core/app:crash_export_thunks",
     "//components/crash/core/app:run_as_crashpad_handler",
diff --git a/chrome/credential_provider/extension/extension_main.cc b/chrome/credential_provider/extension/extension_main.cc
index 974ff08f..8045a35 100644
--- a/chrome/credential_provider/extension/extension_main.cc
+++ b/chrome/credential_provider/extension/extension_main.cc
@@ -14,13 +14,23 @@
 #include "chrome/credential_provider/eventlog/gcp_eventlog_messages.h"
 #include "chrome/credential_provider/extension/os_service_manager.h"
 #include "chrome/credential_provider/extension/service.h"
+#include "chrome/credential_provider/extension/task_manager.h"
 #include "chrome/credential_provider/gaiacp/logging.h"
 #include "chrome/credential_provider/gaiacp/mdm_utils.h"
 #include "chrome/credential_provider/gaiacp/reg_utils.h"
+#include "chrome/credential_provider/gaiacp/user_policies_manager.h"
 
 using credential_provider::GetGlobalFlagOrDefault;
 using credential_provider::kRegEnableVerboseLogging;
 
+// Register all tasks for ESA with the TaskManager.
+void RegisterAllTasks() {
+  // Task to fetch Cloud policies for all GCPW users.
+  credential_provider::extension::TaskManager::Get()->RegisterTask(
+      "FetchCloudPolicies",
+      credential_provider::UserPoliciesManager::GetFetchPoliciesTaskCreator());
+}
+
 int APIENTRY wWinMain(HINSTANCE hInstance,
                       HINSTANCE /*hPrevInstance*/,
                       wchar_t* lpCmdLine,
@@ -59,7 +69,7 @@
   base::win::RegisterInvalidParamHandler();
   base::win::SetupCRT(*base::CommandLine::ForCurrentProcess());
 
-  credential_provider::extension::Service::Get()->Run();
+  RegisterAllTasks();
 
-  return 0;
+  return credential_provider::extension::Service::Get()->Run();
 }
diff --git a/chrome/credential_provider/extension/task_manager_unittests.cc b/chrome/credential_provider/extension/task_manager_unittests.cc
index e35a2fc..b40e384 100644
--- a/chrome/credential_provider/extension/task_manager_unittests.cc
+++ b/chrome/credential_provider/extension/task_manager_unittests.cc
@@ -120,9 +120,11 @@
   base::string16 serial_number = L"1234";
   GoogleRegistrationDataForTesting g_registration_data(serial_number);
   base::string16 machine_guid = L"machine_guid";
-  std::string dm_token = "dm_token";
   SetMachineGuidForTesting(machine_guid);
-  SetDmTokenForTesting(dm_token);
+  ASSERT_EQ(S_OK, SetDmTokenForTesting("dmtoken"));
+  std::string dm_token;
+  // DM token gets Base64 encoded so get the encoded value here.
+  ASSERT_EQ(S_OK, GetDmToken(&dm_token));
 
   // Create a fake user associated to a gaia id.
   CComBSTR sid1;
diff --git a/chrome/credential_provider/extension/user_context_enumerator.cc b/chrome/credential_provider/extension/user_context_enumerator.cc
index 6c0260ca..dd4d1f78 100644
--- a/chrome/credential_provider/extension/user_context_enumerator.cc
+++ b/chrome/credential_provider/extension/user_context_enumerator.cc
@@ -12,7 +12,6 @@
 #include "chrome/credential_provider/extension/user_device_context.h"
 #include "chrome/credential_provider/gaiacp/gcp_utils.h"
 #include "chrome/credential_provider/gaiacp/logging.h"
-#include "chrome/credential_provider/gaiacp/mdm_utils.h"
 #include "chrome/credential_provider/gaiacp/reg_utils.h"
 
 namespace credential_provider {
diff --git a/chrome/credential_provider/gaiacp/BUILD.gn b/chrome/credential_provider/gaiacp/BUILD.gn
index b182615d..49dca3a 100644
--- a/chrome/credential_provider/gaiacp/BUILD.gn
+++ b/chrome/credential_provider/gaiacp/BUILD.gn
@@ -24,23 +24,15 @@
     "gcp_crash_reporter_client.h",
     "gcp_crash_reporting_utils.cc",
     "gcp_crash_reporting_utils.h",
-    "gcp_utils.cc",
-    "gcp_utils.h",
     "gcpw_strings.cc",
     "gcpw_strings.h",
     "gcpw_version.cc",
     "gcpw_version.h",
-    "logging.cc",
-    "logging.h",
     "mdm_utils.cc",
     "mdm_utils.h",
     "os_user_manager.cc",
     "os_user_manager.h",
-    "reg_utils.cc",
-    "reg_utils.h",
     "scoped_handle.h",
-    "scoped_lsa_policy.cc",
-    "scoped_lsa_policy.h",
     "user_policies.cc",
     "user_policies.h",
     "user_policies_manager.cc",
@@ -53,6 +45,8 @@
   deps = [
     ":gaia_credential_provider_idl",
     ":string_resources",
+    ":util",
+    "../extension:extension_lib",
     "//base",
     "//build:branding_buildflags",
     "//chrome/common:version_header",
@@ -80,6 +74,39 @@
   ]
 }
 
+# This utility library is shared with the GCPW extension.
+source_set("util") {
+  sources = [
+    "gcp_utils.cc",
+    "gcp_utils.h",
+    "logging.cc",
+    "logging.h",
+    "reg_utils.cc",
+    "reg_utils.h",
+    "scoped_lsa_policy.cc",
+    "scoped_lsa_policy.h",
+  ]
+  public_configs = [ ":util_config" ]
+  public_deps = [ "//chrome/credential_provider/common:common_constants" ]
+  deps = [
+    ":gaia_credential_provider_idl",
+    ":string_resources",
+    "//base",
+    "//build:branding_buildflags",
+    "//chrome/common:version_header",
+    "//chrome/installer/launcher_support",
+    "//google_apis:google_apis",
+    "//url:url",
+  ]
+}
+
+config("util_config") {
+  defines = [
+    # Needed in order to include the win32 header security.h.
+    "SECURITY_WIN32",
+  ]
+}
+
 # This static library is shared with the test code.
 
 source_set("gaiacp_lib") {
@@ -131,6 +158,7 @@
     ":gaia_credential_provider_idl",
     ":static_resources",
     ":string_resources",
+    ":util",
     "../eventlog:gcp_eventlog_messages",
     "//build:branding_buildflags",
     "//chrome/common:non_code_constants",
@@ -241,6 +269,7 @@
   deps = [
     ":common",
     ":gaiacp_lib",
+    ":util",
     ":version",
     "//base",
     "//build:branding_buildflags",
diff --git a/chrome/credential_provider/gaiacp/device_policies_manager.cc b/chrome/credential_provider/gaiacp/device_policies_manager.cc
index e089b66..43c63f50 100644
--- a/chrome/credential_provider/gaiacp/device_policies_manager.cc
+++ b/chrome/credential_provider/gaiacp/device_policies_manager.cc
@@ -106,10 +106,10 @@
 
   base::string16 update_channel;  // Empty value indicates the stable channel.
   std::wstring ap_value;
-  status = key.ReadValue(kRegUpdateTracksName, &ap_value);
+  LONG ap_status = key.ReadValue(kRegUpdateTracksName, &ap_value);
   GcpwVersion current_pinned_version(base::UTF16ToUTF8(ap_value));
 
-  if (status == ERROR_SUCCESS && !current_pinned_version.IsValid()) {
+  if (ap_status == ERROR_SUCCESS && !current_pinned_version.IsValid()) {
     std::vector<base::string16> ap_components =
         base::SplitString(ap_value, kChannelAndVersionSeparator,
                           base::WhitespaceHandling::TRIM_WHITESPACE,
@@ -124,7 +124,7 @@
     // pinned to a version, remove the registry entry if device was on the
     // stable channel or restore the previous channel.
 
-    if (update_channel.empty()) {
+    if (ap_status == ERROR_SUCCESS && update_channel.empty()) {
       status = key.DeleteValue(kRegUpdateTracksName);
       if (status != ERROR_SUCCESS) {
         LOGFN(ERROR) << "Unable to delete " << kRegUpdateTracksName
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.cc b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
index 074dc03..c4a4e40 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
@@ -2477,9 +2477,17 @@
   if (UserPoliciesManager::Get()->CloudPoliciesEnabled() &&
       UserPoliciesManager::Get()->GetTimeDeltaSinceLastPolicyFetch(sid) >
           kMaxTimeDeltaSinceLastUserPolicyRefresh) {
+    // Save gaia id since it is needed for the cloud policies server request.
+    base::string16 gaia_id = GetDictString(*authentication_results_, kKeyId);
+    HRESULT hr = SetUserProperty(sid, kUserId, gaia_id);
+    if (FAILED(hr)) {
+      LOGFN(ERROR) << "SetUserProperty(id) hr=" << putHR(hr);
+    }
+
     // TODO(crbug.com/976744) Use downscoped token here.
-    base::string16 access_token = GetDictString(*properties, kKeyAccessToken);
-    HRESULT hr = UserPoliciesManager::Get()->FetchAndStoreCloudUserPolicies(
+    base::string16 access_token =
+        GetDictString(*authentication_results_, kKeyAccessToken);
+    hr = UserPoliciesManager::Get()->FetchAndStoreCloudUserPolicies(
         sid, base::UTF16ToUTF8(access_token));
     SecurelyClearString(access_token);
     if (FAILED(hr)) {
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
index f60f559..3145a51 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
@@ -3305,7 +3305,9 @@
     } else {
       UserPolicies policies;
       base::Value policies_value = policies.ToValue();
-      base::JSONWriter::Write(policies_value, &expected_response);
+      base::Value expected_response_value(base::Value::Type::DICTIONARY);
+      expected_response_value.SetKey("policies", std::move(policies_value));
+      base::JSONWriter::Write(expected_response_value, &expected_response);
     }
 
     fake_http_url_fetcher_factory()->SetFakeResponse(
diff --git a/chrome/credential_provider/gaiacp/gcp_utils.cc b/chrome/credential_provider/gaiacp/gcp_utils.cc
index c73b2ee1..695255c3 100644
--- a/chrome/credential_provider/gaiacp/gcp_utils.cc
+++ b/chrome/credential_provider/gaiacp/gcp_utils.cc
@@ -51,7 +51,6 @@
 #include "chrome/credential_provider/common/gcp_strings.h"
 #include "chrome/credential_provider/gaiacp/gaia_resources.h"
 #include "chrome/credential_provider/gaiacp/logging.h"
-#include "chrome/credential_provider/gaiacp/mdm_utils.h"
 #include "chrome/credential_provider/gaiacp/reg_utils.h"
 #include "chrome/installer/launcher_support/chrome_launcher_support.h"
 #include "google_apis/gaia/gaia_auth_util.h"
@@ -62,6 +61,9 @@
 
 const wchar_t kDefaultProfilePictureFileExtension[] = L".jpg";
 
+const base::FilePath::CharType kCredentialProviderFolder[] =
+    L"Credential Provider";
+
 // Overridden in tests to fake serial number extraction.
 bool g_use_test_serial_number = false;
 base::string16 g_test_serial_number = L"";
@@ -87,8 +89,6 @@
 constexpr char kMinimumSupportedChromeVersionStr[] = "77.0.3865.65";
 
 constexpr char kSentinelFilename[] = "gcpw_startup.sentinel";
-constexpr base::FilePath::CharType kCredentialProviderFolder[] =
-    L"Credential Provider";
 constexpr int64_t kMaxConsecutiveCrashCount = 5;
 
 constexpr base::win::i18n::LanguageSelector::LangToOffset
diff --git a/chrome/credential_provider/gaiacp/gcp_utils.h b/chrome/credential_provider/gaiacp/gcp_utils.h
index 001f5b2..dd061c76 100644
--- a/chrome/credential_provider/gaiacp/gcp_utils.h
+++ b/chrome/credential_provider/gaiacp/gcp_utils.h
@@ -60,6 +60,9 @@
 // does not have a file extension.
 extern const wchar_t kDefaultProfilePictureFileExtension[];
 
+// Name of the sub-folder under which all files for GCPW are stored.
+extern const base::FilePath::CharType kCredentialProviderFolder[];
+
 // Because of some strange dependency problems with windows header files,
 // define STATUS_SUCCESS here instead of including ntstatus.h or SubAuth.h
 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
diff --git a/chrome/credential_provider/gaiacp/gem_device_details_manager.cc b/chrome/credential_provider/gaiacp/gem_device_details_manager.cc
index 334e2cd..e38819eeb 100644
--- a/chrome/credential_provider/gaiacp/gem_device_details_manager.cc
+++ b/chrome/credential_provider/gaiacp/gem_device_details_manager.cc
@@ -13,7 +13,6 @@
 #define _NTDEF_  // Prevent redefition errors, must come after <winternl.h>
 #include <ntsecapi.h>  // For POLICY_ALL_ACCESS types
 
-#include "base/base64.h"
 #include "base/containers/span.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -129,15 +128,12 @@
   for (const std::string& mac_address : mac_addresses)
     mac_address_value_list.Append(base::Value(mac_address));
 
-  std::string dm_token = "";
-  std::string encoded_dm_token = "";
+  std::string dm_token;
   hr = GetDmToken(&dm_token);
   if (FAILED(hr)) {
     LOGFN(WARNING) << "DM token is required to execute periodic tasks hr="
                  << putHR(hr);
     hr = S_OK;
-  } else {
-    base::Base64Encode(dm_token, &encoded_dm_token);
   }
 
   request_dict_.reset(new base::Value(base::Value::Type::DICTIONARY));
@@ -161,7 +157,7 @@
   request_dict_->SetStringKey(kBuiltInAdminNameParameterName,
                               built_in_admin_name);
   request_dict_->SetStringKey(kAdminGroupNameParameterName, admin_group_name);
-  request_dict_->SetStringKey(kDmToken, encoded_dm_token);
+  request_dict_->SetStringKey(kDmToken, dm_token);
 
   base::string16 known_resource_id = GetUserDeviceResourceId(sid);
   if (!known_resource_id.empty()) {
diff --git a/chrome/credential_provider/gaiacp/mdm_utils.cc b/chrome/credential_provider/gaiacp/mdm_utils.cc
index a495713..c22e4655 100644
--- a/chrome/credential_provider/gaiacp/mdm_utils.cc
+++ b/chrome/credential_provider/gaiacp/mdm_utils.cc
@@ -50,10 +50,8 @@
     L"device_details_upload_status";
 constexpr wchar_t kRegDeviceDetailsUploadFailures[] =
     L"device_details_upload_failures";
-constexpr wchar_t kRegGlsPath[] = L"gls_path";
 constexpr wchar_t kRegUpdateCredentialsOnChange[] =
     L"update_credentials_on_change";
-constexpr wchar_t kRegUserDeviceResourceId[] = L"device_resource_id";
 constexpr wchar_t kRegUseShorterAccountName[] = L"use_shorter_account_name";
 constexpr wchar_t kUserPasswordLsaStoreKeyPrefix[] =
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
@@ -511,18 +509,6 @@
   return kUserPasswordLsaStoreKeyPrefix + sid;
 }
 
-base::string16 GetUserDeviceResourceId(const base::string16& sid) {
-  wchar_t known_resource_id[512];
-  ULONG known_resource_id_size = base::size(known_resource_id);
-  HRESULT hr = GetUserProperty(sid, kRegUserDeviceResourceId, known_resource_id,
-                               &known_resource_id_size);
-
-  if (SUCCEEDED(hr) && known_resource_id_size > 0)
-    return base::string16(known_resource_id, known_resource_id_size - 1);
-
-  return base::string16();
-}
-
 base::string16 GetDevelopmentUrl(const base::string16& url,
                                  const base::string16& dev) {
   std::string project;
diff --git a/chrome/credential_provider/gaiacp/mdm_utils.h b/chrome/credential_provider/gaiacp/mdm_utils.h
index afb7d64a..4dc3201 100644
--- a/chrome/credential_provider/gaiacp/mdm_utils.h
+++ b/chrome/credential_provider/gaiacp/mdm_utils.h
@@ -56,12 +56,6 @@
 // Number of consecutive failures encountered when uploading device details.
 extern const wchar_t kRegDeviceDetailsUploadFailures[];
 
-// Specifies custom Chrome path to use for GLS.
-extern const wchar_t kRegGlsPath[];
-
-// Registry key where user device resource ID is stored.
-extern const wchar_t kRegUserDeviceResourceId[];
-
 // Maximum number of consecutive Upload device details failures for which we do
 // enforce auth.
 extern const int kMaxNumConsecutiveUploadDeviceFailures;
@@ -144,10 +138,6 @@
 // Constructs the password lsa store key for the given |sid|.
 base::string16 GetUserPasswordLsaStoreKey(const base::string16& sid);
 
-// Get device resource ID for the user with given |sid|. Returns an empty string
-// if one has not been set for the user.
-base::string16 GetUserDeviceResourceId(const base::string16& sid);
-
 // Converts the |url| in the form of http://xxxxx.googleapis.com/...
 // to a form that points to a development URL as specified with |dev|
 // environment. Final url will be in the form
diff --git a/chrome/credential_provider/gaiacp/reg_utils.cc b/chrome/credential_provider/gaiacp/reg_utils.cc
index 9401a18d..8b4fb4fd1 100644
--- a/chrome/credential_provider/gaiacp/reg_utils.cc
+++ b/chrome/credential_provider/gaiacp/reg_utils.cc
@@ -8,6 +8,7 @@
 
 #include <atlbase.h>
 
+#include "base/base64.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -44,6 +45,9 @@
     L"SOFTWARE\\Microsoft\\Cryptography";
 const wchar_t kMicrosoftCryptographyMachineGuidRegKey[] = L"MachineGuid";
 
+constexpr wchar_t kRegUserDeviceResourceId[] = L"device_resource_id";
+constexpr wchar_t kRegGlsPath[] = L"gls_path";
+
 namespace {
 
 constexpr wchar_t kAccountPicturesRootRegKey[] =
@@ -491,11 +495,29 @@
                              machine_guid);
 }
 
+base::string16 GetUserDeviceResourceId(const base::string16& sid) {
+  wchar_t known_resource_id[512];
+  ULONG known_resource_id_size = base::size(known_resource_id);
+  HRESULT hr = GetUserProperty(sid, kRegUserDeviceResourceId, known_resource_id,
+                               &known_resource_id_size);
+
+  if (SUCCEEDED(hr) && known_resource_id_size > 0)
+    return base::string16(known_resource_id, known_resource_id_size - 1);
+
+  return base::string16();
+}
+
 HRESULT GetDmToken(std::string* dm_token) {
   DCHECK(dm_token);
 
-  return GetMachineRegBinaryInternal(kEnrollmentRegKey, kDmTokenRegKey,
-                                     dm_token, KEY_READ | KEY_WOW64_32KEY);
+  std::string binary_dm_token;
+  HRESULT hr =
+      GetMachineRegBinaryInternal(kEnrollmentRegKey, kDmTokenRegKey,
+                                  &binary_dm_token, KEY_READ | KEY_WOW64_32KEY);
+  if (SUCCEEDED(hr)) {
+    base::Base64Encode(binary_dm_token, dm_token);
+  }
+  return hr;
 }
 
 HRESULT SetDmTokenForTesting(const std::string& dm_token) {
diff --git a/chrome/credential_provider/gaiacp/reg_utils.h b/chrome/credential_provider/gaiacp/reg_utils.h
index b1c20559..ac34ddbd 100644
--- a/chrome/credential_provider/gaiacp/reg_utils.h
+++ b/chrome/credential_provider/gaiacp/reg_utils.h
@@ -26,6 +26,12 @@
 // Registry key used to determine a user's default credential provider tile.
 extern const wchar_t kLogonUiUserTileRegKey[];
 
+// Registry key where user device resource ID is stored.
+extern const wchar_t kRegUserDeviceResourceId[];
+
+// Specifies custom Chrome path to use for GLS.
+extern const wchar_t kRegGlsPath[];
+
 // Gets any HKLM registry key on the system.
 HRESULT GetMachineRegDWORD(const base::string16& key_name,
                            const base::string16& name,
@@ -166,11 +172,19 @@
 // credential provider.
 HRESULT MakeGcpwDefaultCP();
 
+// Get device resource ID for the user with given |sid|. Returns an empty string
+// if one has not been set for the user.
+base::string16 GetUserDeviceResourceId(const base::string16& sid);
+
 // The token which is written to Windows registry as a result of exchanging
-// enrollment token.
+// enrollment token. The value returned here is the Base64 encoded version of
+// the binary value present in the registry.
 HRESULT GetDmToken(std::string* dm_token);
 
-// Sets  HKLM\SOFTWARE\Google\Enrollment\dm_token registry for testing.
+// Sets HKLM\SOFTWARE\Google\Enrollment\dm_token registry for testing. Note
+// here that the value specified in |dm_token| will be the binary value stored
+// in registry. The value read in GetDmToken() is the Base64 encoded version of
+// this.
 HRESULT SetDmTokenForTesting(const std::string& dm_token);
 
 }  // namespace credential_provider
diff --git a/chrome/credential_provider/gaiacp/user_policies.cc b/chrome/credential_provider/gaiacp/user_policies.cc
index ee6c918..97631a0 100644
--- a/chrome/credential_provider/gaiacp/user_policies.cc
+++ b/chrome/credential_provider/gaiacp/user_policies.cc
@@ -18,11 +18,11 @@
 namespace {
 
 // Parameter names that are used in the JSON payload of the response.
-const char kGcpwPolicyDmEnrollmentParameterName[] = "enable_dm_enrollment";
-const char kGcpwPolicyAutoUpdateParameterName[] = "enable_gcpw_auto_update";
-const char kGcpwPolicyPinnerVersionParameterName[] = "gcpw_pinned_version";
-const char kGcpwPolicMultiUserLoginParameterName[] = "enable_multi_user_login";
-const char kGcpwPolicyValidityPeriodParameterName[] = "validity_period_days";
+const char kGcpwPolicyDmEnrollmentParameterName[] = "enableDmEnrollment";
+const char kGcpwPolicyAutoUpdateParameterName[] = "enableGcpwAutoUpdate";
+const char kGcpwPolicyPinnerVersionParameterName[] = "gcpwPinnedVersion";
+const char kGcpwPolicMultiUserLoginParameterName[] = "enableMultiUserLogin";
+const char kGcpwPolicyValidityPeriodParameterName[] = "validityPeriodDays";
 
 // Default value of each user policy.
 constexpr bool kUserPolicyDefaultDeviceEnrollment = true;
diff --git a/chrome/credential_provider/gaiacp/user_policies_manager.cc b/chrome/credential_provider/gaiacp/user_policies_manager.cc
index 1ed86ab6..596538e0 100644
--- a/chrome/credential_provider/gaiacp/user_policies_manager.cc
+++ b/chrome/credential_provider/gaiacp/user_policies_manager.cc
@@ -6,11 +6,16 @@
 
 #include <limits>
 
+#include "base/bind.h"
 #include "base/files/file.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
+#include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "base/win/registry.h"
@@ -28,6 +33,8 @@
 // HTTP endpoint on the GCPW service to fetch user policies.
 const char kUserIdUrlPlaceholder[] = "{user_id}";
 const char kGcpwServiceFetchUserPoliciesPath[] = "/v1/users/{user_id}/policies";
+const char kGcpwServiceFetchUserPoliciesQueryTemplate[] =
+    "?device_resource_id=%s&dm_token=%s";
 
 // Default timeout when trying to make requests to the GCPW service.
 const base::TimeDelta kDefaultFetchPoliciesRequestTimeout =
@@ -48,14 +55,26 @@
 // Registry key to control whether cloud policies feature is enabled.
 const wchar_t kCloudPoliciesEnabledRegKey[] = L"cloud_policies_enabled";
 
+// Name of the key in the server response whose value contains the user
+// policies.
+const char kPolicyFetchResponseKeyName[] = "policies";
+
 // True when cloud policies feature is enabled.
 bool g_cloud_policies_enabled = false;
 
 // Get the path to the directory where the policies will be stored for the user
 // with |sid|.
 base::FilePath GetUserPolicyDirectoryFilePath(const base::string16& sid) {
-  base::FilePath path = GetInstallDirectory();
-  path = path.Append(kGcpwPoliciesDirectory).Append(sid);
+  base::FilePath path;
+  if (!base::PathService::Get(base::DIR_COMMON_APP_DATA, &path)) {
+    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
+    LOGFN(ERROR) << "PathService::Get(DIR_COMMON_APP_DATA) hr=" << putHR(hr);
+    return base::FilePath();
+  }
+  path = path.Append(GetInstallParentDirectoryName())
+             .Append(kCredentialProviderFolder)
+             .Append(kGcpwPoliciesDirectory)
+             .Append(sid);
   return path;
 }
 
@@ -94,6 +113,85 @@
   return policy_file;
 }
 
+// Creates the URL used to fetch the policies from the backend based on the
+// credential present (OAuth vs DM token) for authentication.
+GURL GetFetchUserPoliciesUrl(const base::string16& sid,
+                             bool has_access_token,
+                             const base::string16& device_resource_id,
+                             const base::string16& dm_token) {
+  GURL gcpw_service_url = GetGcpwServiceUrl();
+  base::string16 user_id;
+
+  HRESULT status = GetIdFromSid(sid.c_str(), &user_id);
+  if (FAILED(status)) {
+    LOGFN(ERROR) << "Could not get user id from sid " << sid;
+    return GURL();
+  }
+
+  std::string user_policies_path(kGcpwServiceFetchUserPoliciesPath);
+  std::string placeholder(kUserIdUrlPlaceholder);
+  user_policies_path.replace(user_policies_path.find(placeholder),
+                             placeholder.size(), base::UTF16ToUTF8(user_id));
+
+  if (!has_access_token) {
+    if (device_resource_id.empty() || dm_token.empty()) {
+      LOGFN(ERROR) << "Either device id or dm token empty when no access token "
+                      "present for "
+                   << sid;
+      return GURL();
+    }
+
+    std::string device_resource_id_value =
+        base::UTF16ToUTF8(device_resource_id);
+    std::string dm_token_value = base::UTF16ToUTF8(dm_token);
+    std::string query_suffix = base::StringPrintf(
+        kGcpwServiceFetchUserPoliciesQueryTemplate,
+        device_resource_id_value.c_str(), dm_token_value.c_str());
+    user_policies_path += query_suffix;
+  }
+
+  return gcpw_service_url.Resolve(user_policies_path);
+}
+
+// Defines a task that is called by the ESA to perform the policy fetch
+// operation.
+class UserPoliciesFetchTask : public extension::Task {
+ public:
+  static std::unique_ptr<extension::Task> Create() {
+    std::unique_ptr<extension::Task> esa_task(new UserPoliciesFetchTask());
+    return esa_task;
+  }
+
+  // ESA calls this to retrieve a configuration for the task execution. Return
+  // a default config for now.
+  extension::Config GetConfig() final { return extension::Config(); }
+
+  // ESA calls this to set all the user-device contexts for the execution of the
+  // task.
+  HRESULT SetContext(const std::vector<extension::UserDeviceContext>& c) final {
+    context_ = c;
+    return S_OK;
+  }
+
+  // ESA calls execute function to perform the actual task.
+  HRESULT Execute() final {
+    HRESULT task_status = S_OK;
+    for (const auto& c : context_) {
+      HRESULT hr =
+          UserPoliciesManager::Get()->FetchAndStoreCloudUserPolicies(c);
+      if (FAILED(hr)) {
+        LOGFN(ERROR) << "Failed fetching policies for " << c.user_sid
+                     << ". hr=" << putHR(hr);
+        task_status = hr;
+      }
+    }
+    return task_status;
+  }
+
+ private:
+  std::vector<extension::UserDeviceContext> context_;
+};
+
 }  // namespace
 
 // static
@@ -108,6 +206,11 @@
   return &instance_storage;
 }
 
+// static
+extension::TaskCreator UserPoliciesManager::GetFetchPoliciesTaskCreator() {
+  return base::BindRepeating(&UserPoliciesFetchTask::Create);
+}
+
 UserPoliciesManager::UserPoliciesManager() : fetch_status_(S_OK) {
   g_cloud_policies_enabled =
       GetGlobalFlagOrDefault(kCloudPoliciesEnabledRegKey, 0) == 1;
@@ -121,35 +224,50 @@
 
 GURL UserPoliciesManager::GetGcpwServiceUserPoliciesUrl(
     const base::string16& sid) {
-  GURL gcpw_service_url = GetGcpwServiceUrl();
-  base::string16 user_id;
+  return GetFetchUserPoliciesUrl(sid, true, L"", L"");
+}
 
-  HRESULT status = GetIdFromSid(sid.c_str(), &user_id);
-  if (FAILED(status)) {
-    LOGFN(ERROR) << "Could not get user id from sid " << sid;
-    return GURL();
-  }
+GURL UserPoliciesManager::GetGcpwServiceUserPoliciesUrl(
+    const base::string16& sid,
+    const base::string16& device_resource_id,
+    const base::string16& dm_token) {
+  return GetFetchUserPoliciesUrl(sid, false, device_resource_id, dm_token);
+}
 
-  std::string user_policies_path(kGcpwServiceFetchUserPoliciesPath);
-  std::string placeholder(kUserIdUrlPlaceholder);
-  user_policies_path.replace(user_policies_path.find(placeholder),
-                             placeholder.size(), base::UTF16ToUTF8(user_id));
-  return gcpw_service_url.Resolve(user_policies_path);
+HRESULT UserPoliciesManager::FetchAndStoreCloudUserPolicies(
+    const extension::UserDeviceContext& context) {
+  return FetchAndStorePolicies(
+      context.user_sid,
+      GetGcpwServiceUserPoliciesUrl(
+          context.user_sid, context.device_resource_id, context.dm_token),
+      std::string());
 }
 
 HRESULT UserPoliciesManager::FetchAndStoreCloudUserPolicies(
     const base::string16& sid,
     const std::string& access_token) {
-  fetch_status_ = E_FAIL;
-  base::Optional<base::Value> request_result;
+  if (access_token.empty()) {
+    LOGFN(ERROR) << "Access token not specified";
+    return (fetch_status_ = E_FAIL);
+  }
 
-  GURL user_policies_url =
-      UserPoliciesManager::Get()->GetGcpwServiceUserPoliciesUrl(sid);
+  return FetchAndStorePolicies(sid, GetGcpwServiceUserPoliciesUrl(sid),
+                               access_token);
+}
+
+HRESULT UserPoliciesManager::FetchAndStorePolicies(
+    const base::string16& sid,
+    GURL user_policies_url,
+    const std::string& access_token) {
+  fetch_status_ = E_FAIL;
+
   if (!user_policies_url.is_valid()) {
+    LOGFN(ERROR) << "Invalid user policies fetch URL specified.";
     return (fetch_status_ = E_FAIL);
   }
 
   // Make the fetch policies HTTP request.
+  base::Optional<base::Value> request_result;
   HRESULT hr = WinHttpUrlFetcher::BuildRequestAndFetchResultFromHttpService(
       user_policies_url, access_token, {}, {},
       kDefaultFetchPoliciesRequestTimeout, kMaxNumHttpRetries, &request_result);
@@ -247,8 +365,15 @@
     return false;
   }
 
+  const base::Value* policies =
+      policy_data->FindDictKey(kPolicyFetchResponseKeyName);
+  if (!policies) {
+    LOGFN(ERROR) << "User policies not found!";
+    return false;
+  }
+
   // Override policies with those we just read.
-  *user_policies = UserPolicies::FromValue(*policy_data);
+  *user_policies = UserPolicies::FromValue(*policies);
 
   return true;
 }
diff --git a/chrome/credential_provider/gaiacp/user_policies_manager.h b/chrome/credential_provider/gaiacp/user_policies_manager.h
index 3021199..6014566a 100644
--- a/chrome/credential_provider/gaiacp/user_policies_manager.h
+++ b/chrome/credential_provider/gaiacp/user_policies_manager.h
@@ -8,6 +8,7 @@
 #include "base/strings/string16.h"
 #include "base/time/time.h"
 #include "base/win/windows_types.h"
+#include "chrome/credential_provider/extension/task_manager.h"
 #include "chrome/credential_provider/gaiacp/user_policies.h"
 #include "url/gurl.h"
 
@@ -19,6 +20,10 @@
   // Get the user policies manager instance.
   static UserPoliciesManager* Get();
 
+  // Provides the GCPW extension with a TaskCreator which can be used to create
+  // a task for fetching user policies.
+  static extension::TaskCreator GetFetchPoliciesTaskCreator();
+
   // Return true if cloud policies feature is enabled.
   bool CloudPoliciesEnabled() const;
 
@@ -29,14 +34,27 @@
       const base::string16& sid,
       const std::string& access_token);
 
+  // Fetch the policies for the user-device |context| provided by the GCPW
+  // extension service from the GCPW backend and saves it in file storage
+  // replacing any previously fetched versions.
+  virtual HRESULT FetchAndStoreCloudUserPolicies(
+      const extension::UserDeviceContext& context);
+
   // Return the elapsed time delta since the last time the policies were
   // successfully fetched for the user with |sid|.
   base::TimeDelta GetTimeDeltaSinceLastPolicyFetch(
       const base::string16& sid) const;
 
-  // Get the URL of GCPW service for HTTP request for fetching user policies.
+  // Get the URL of GCPW service for HTTP request for fetching user policies
+  // when the caller has a valid OAuth token for authentication.
   GURL GetGcpwServiceUserPoliciesUrl(const base::string16& sid);
 
+  // Get the URL of GCPW service for HTTP request for fetching user policies
+  // when the caller only has a DM token.
+  GURL GetGcpwServiceUserPoliciesUrl(const base::string16& sid,
+                                     const base::string16& device_resource_id,
+                                     const base::string16& dm_token);
+
   // Retrieves the policies for the user with |sid| from local storage. Returns
   // the default user policy if policy not fetched or on any error.
   virtual bool GetUserPolicies(const base::string16& sid,
@@ -52,6 +70,12 @@
   // Returns the storage used for the instance pointer.
   static UserPoliciesManager** GetInstanceStorage();
 
+  // Fetch the user policies using the given backend url and access token if
+  // specified.
+  HRESULT FetchAndStorePolicies(const base::string16& sid,
+                                GURL user_policies_url,
+                                const std::string& access_token);
+
   UserPoliciesManager();
   virtual ~UserPoliciesManager();
 
diff --git a/chrome/credential_provider/gaiacp/user_policies_manager_unittests.cc b/chrome/credential_provider/gaiacp/user_policies_manager_unittests.cc
index a4f0b43..5da7b982 100644
--- a/chrome/credential_provider/gaiacp/user_policies_manager_unittests.cc
+++ b/chrome/credential_provider/gaiacp/user_policies_manager_unittests.cc
@@ -9,6 +9,7 @@
 #include "base/json/json_writer.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_path_override.h"
+#include "chrome/credential_provider/extension/user_device_context.h"
 #include "chrome/credential_provider/gaiacp/gcpw_strings.h"
 #include "chrome/credential_provider/gaiacp/mdm_utils.h"
 #include "chrome/credential_provider/gaiacp/reg_utils.h"
@@ -30,6 +31,28 @@
       UserPoliciesManager::Get()->GetUserPolicies(L"not-valid", &policies));
 }
 
+TEST_F(GcpUserPoliciesBaseTest, NoAccessToken) {
+  // Create a fake user associated to a gaia id.
+  CComBSTR sid_str;
+  ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser(
+                      kDefaultUsername, L"password", L"Full Name", L"comment",
+                      base::UTF8ToUTF16(kDefaultGaiaId), L"user@company.com",
+                      &sid_str));
+  base::string16 sid = OLE2W(sid_str);
+
+  ASSERT_TRUE(FAILED(
+      UserPoliciesManager::Get()->FetchAndStoreCloudUserPolicies(sid, "")));
+  UserPolicies policies;
+  ASSERT_FALSE(UserPoliciesManager::Get()->GetUserPolicies(sid, &policies));
+}
+
+// Tests effective user policy under various scenarios of cloud policy values.
+// Params:
+// bool : Whether device management enabled.
+// bool : Whether automatic updated enabled.
+// string : Pinned version of GCPW to use if any.
+// bool : Whether multiple users can login.
+// int : Number of days user can remain offline.
 class GcpUserPoliciesFetchAndReadTest
     : public GcpUserPoliciesBaseTest,
       public ::testing::WithParamInterface<
@@ -88,8 +111,10 @@
                     policies_.validity_period_days + 100);
 
   base::Value policies_value = policies_.ToValue();
+  base::Value expected_response_value(base::Value::Type::DICTIONARY);
+  expected_response_value.SetKey("policies", std::move(policies_value));
   std::string expected_response;
-  base::JSONWriter::Write(policies_value, &expected_response);
+  base::JSONWriter::Write(expected_response_value, &expected_response);
 
   GURL user_policies_url =
       UserPoliciesManager::Get()->GetGcpwServiceUserPoliciesUrl(sid_);
@@ -120,12 +145,14 @@
 
   // Only set values for cloud policies for those not already set in registry.
   base::Value policies_value(base::Value::Type::DICTIONARY);
-  policies_value.SetBoolKey("enable_gcpw_auto_update",
+  policies_value.SetBoolKey("enableGcpwAutoUpdate",
                             policies_.enable_gcpw_auto_update);
-  policies_value.SetStringKey("gcpw_pinned_version",
+  policies_value.SetStringKey("gcpwPinnedVersion",
                               policies_.gcpw_pinned_version.ToString());
+  base::Value expected_response_value(base::Value::Type::DICTIONARY);
+  expected_response_value.SetKey("policies", std::move(policies_value));
   std::string expected_response;
-  base::JSONWriter::Write(policies_value, &expected_response);
+  base::JSONWriter::Write(expected_response_value, &expected_response);
 
   fake_http_url_fetcher_factory()->SetFakeResponse(
       UserPoliciesManager::Get()->GetGcpwServiceUserPoliciesUrl(sid_),
@@ -158,5 +185,101 @@
                                             ::testing::Bool(),
                                             ::testing::Values(0, 30)));
 
+// Tests user policy fetch by ESA service.
+// Params:
+// string : The specified device resource ID.
+// bool : Whether a valid user sid is present.
+// string : The specified DM token.
+class GcpUserPoliciesExtensionTest
+    : public GcpUserPoliciesBaseTest,
+      public ::testing::WithParamInterface<
+          std::tuple<const wchar_t*, bool, const wchar_t*>> {
+ public:
+  GcpUserPoliciesExtensionTest();
+
+ protected:
+  extension::TaskCreator fetch_policy_task_creator_;
+};
+
+GcpUserPoliciesExtensionTest::GcpUserPoliciesExtensionTest() {
+  fetch_policy_task_creator_ =
+      UserPoliciesManager::GetFetchPoliciesTaskCreator();
+}
+
+TEST_P(GcpUserPoliciesExtensionTest, WithUserDeviceContext) {
+  const base::string16 device_resource_id(std::get<0>(GetParam()));
+  bool has_valid_sid = std::get<1>(GetParam());
+  const base::string16 dm_token(std::get<2>(GetParam()));
+
+  const bool request_can_succeed =
+      has_valid_sid && !device_resource_id.empty() && !dm_token.empty();
+
+  base::string16 user_sid = L"invalid-user-sid";
+  if (has_valid_sid) {
+    // Create a fake user associated to a gaia id.
+    CComBSTR sid_str;
+    ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser(
+                        kDefaultUsername, L"password", L"Full Name", L"comment",
+                        base::UTF8ToUTF16(kDefaultGaiaId), L"user@company.com",
+                        &sid_str));
+    user_sid = OLE2W(sid_str);
+  }
+
+  UserPolicies policies;
+  policies.gcpw_pinned_version = GcpwVersion("1.2.3.4");
+  base::Value policies_value = policies.ToValue();
+  base::Value expected_response_value(base::Value::Type::DICTIONARY);
+  expected_response_value.SetKey("policies", std::move(policies_value));
+  std::string expected_response;
+  base::JSONWriter::Write(expected_response_value, &expected_response);
+
+  GURL user_policies_url =
+      UserPoliciesManager::Get()->GetGcpwServiceUserPoliciesUrl(
+          user_sid, device_resource_id, dm_token);
+
+  if (request_can_succeed) {
+    ASSERT_TRUE(user_policies_url.is_valid());
+    ASSERT_NE(std::string::npos, user_policies_url.spec().find(kDefaultGaiaId));
+    ASSERT_NE(std::string::npos, user_policies_url.spec().find(
+                                     base::UTF16ToUTF8(device_resource_id)));
+    ASSERT_NE(std::string::npos,
+              user_policies_url.spec().find(base::UTF16ToUTF8(dm_token)));
+  } else {
+    ASSERT_FALSE(user_policies_url.is_valid());
+  }
+
+  // Set cloud policy fetch server response.
+  fake_http_url_fetcher_factory()->SetFakeResponse(
+      user_policies_url, FakeWinHttpUrlFetcher::Headers(), expected_response);
+
+  extension::UserDeviceContext context(device_resource_id, L"", L"", user_sid,
+                                       dm_token);
+
+  auto task(fetch_policy_task_creator_.Run());
+  ASSERT_TRUE(task);
+
+  ASSERT_TRUE(SUCCEEDED(task->SetContext({context})));
+  HRESULT status = task->Execute();
+
+  UserPolicies fetched_policies;
+  if (!has_valid_sid || device_resource_id.empty() || dm_token.empty()) {
+    ASSERT_TRUE(FAILED(status));
+    ASSERT_FALSE(UserPoliciesManager::Get()->GetUserPolicies(
+        user_sid, &fetched_policies));
+  } else {
+    ASSERT_TRUE(SUCCEEDED(status));
+    ASSERT_TRUE(UserPoliciesManager::Get()->GetUserPolicies(user_sid,
+                                                            &fetched_policies));
+    ASSERT_EQ(policies, fetched_policies);
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    GcpUserPoliciesExtensionTest,
+    ::testing::Combine(::testing::Values(L"", L"valid-device-resource-id"),
+                       ::testing::Bool(),
+                       ::testing::Values(L"", L"valid-dm-token")));
+
 }  // namespace testing
 }  // namespace credential_provider
diff --git a/chrome/credential_provider/gaiacp/win_http_url_fetcher.cc b/chrome/credential_provider/gaiacp/win_http_url_fetcher.cc
index 7e0fb1d3..fca0b5a 100644
--- a/chrome/credential_provider/gaiacp/win_http_url_fetcher.cc
+++ b/chrome/credential_provider/gaiacp/win_http_url_fetcher.cc
@@ -194,8 +194,11 @@
   }
 
   url_fetcher->SetRequestHeader("Content-Type", "application/json");
-  url_fetcher->SetRequestHeader("Authorization",
-                                ("Bearer " + access_token).c_str());
+  if (!access_token.empty()) {
+    url_fetcher->SetRequestHeader("Authorization",
+                                  ("Bearer " + access_token).c_str());
+  }
+
   for (auto& header : headers)
     url_fetcher->SetRequestHeader(header.first.c_str(), header.second.c_str());
 
diff --git a/chrome/credential_provider/setup/BUILD.gn b/chrome/credential_provider/setup/BUILD.gn
index b58775b..3fed891 100644
--- a/chrome/credential_provider/setup/BUILD.gn
+++ b/chrome/credential_provider/setup/BUILD.gn
@@ -27,6 +27,7 @@
   deps = [
     "../extension:common",
     "../gaiacp:common",
+    "../gaiacp:util",
     "//chrome/installer/util:with_no_strings",
   ]
 }
@@ -69,6 +70,7 @@
     "../eventlog:gcp_eventlog_messages",
     "../extension:common",
     "../gaiacp:common",
+    "../gaiacp:util",
     "//build:branding_buildflags",
     "//chrome/common:version_header",
     "//components/crash/core/app:app",
diff --git a/chrome/credential_provider/test/BUILD.gn b/chrome/credential_provider/test/BUILD.gn
index b533385..bb499a7 100644
--- a/chrome/credential_provider/test/BUILD.gn
+++ b/chrome/credential_provider/test/BUILD.gn
@@ -39,6 +39,7 @@
     "../gaiacp:gaia_credential_provider_idl",
     "../gaiacp:gaiacp_lib",
     "../gaiacp:string_resources",
+    "../gaiacp:util",
     "../gaiacp:version",
     "../setup:common",
     "//base",
diff --git a/chrome/credential_provider/test/gls_runner_test_base.cc b/chrome/credential_provider/test/gls_runner_test_base.cc
index b94eee6..7d7fe6f35 100644
--- a/chrome/credential_provider/test/gls_runner_test_base.cc
+++ b/chrome/credential_provider/test/gls_runner_test_base.cc
@@ -154,6 +154,12 @@
   ASSERT_TRUE(scoped_temp_program_files_dir_.CreateUniqueTempDir());
   program_files_override_.reset(new base::ScopedPathOverride(
       base::DIR_PROGRAM_FILES, scoped_temp_program_files_dir_.GetPath()));
+
+  // Also override location of "ProgramData" system folder as we store user
+  // policies there.
+  ASSERT_TRUE(scoped_temp_progdata_dir_.CreateUniqueTempDir());
+  programdata_override_.reset(new base::ScopedPathOverride(
+      base::DIR_COMMON_APP_DATA, scoped_temp_progdata_dir_.GetPath()));
 }
 
 void GlsRunnerTestBase::TearDown() {
diff --git a/chrome/credential_provider/test/gls_runner_test_base.h b/chrome/credential_provider/test/gls_runner_test_base.h
index a4b577b..ec3d47b 100644
--- a/chrome/credential_provider/test/gls_runner_test_base.h
+++ b/chrome/credential_provider/test/gls_runner_test_base.h
@@ -240,7 +240,9 @@
   std::string default_token_handle_response_;
 
   base::ScopedTempDir scoped_temp_program_files_dir_;
+  base::ScopedTempDir scoped_temp_progdata_dir_;
   std::unique_ptr<base::ScopedPathOverride> program_files_override_;
+  std::unique_ptr<base::ScopedPathOverride> programdata_override_;
 };
 
 }  // namespace testing
diff --git a/chrome/gpu/browser_exposed_gpu_interfaces.cc b/chrome/gpu/browser_exposed_gpu_interfaces.cc
index 831491b..8124c1c 100644
--- a/chrome/gpu/browser_exposed_gpu_interfaces.cc
+++ b/chrome/gpu/browser_exposed_gpu_interfaces.cc
@@ -30,18 +30,22 @@
 void CreateArcVideoDecodeAccelerator(
     ChromeContentGpuClient* client,
     const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
     mojo::PendingReceiver<::arc::mojom::VideoDecodeAccelerator> receiver) {
   mojo::MakeSelfOwnedReceiver(
       std::make_unique<arc::GpuArcVideoDecodeAccelerator>(
-          gpu_preferences, client->GetProtectedBufferManager()),
+          gpu_preferences, gpu_workarounds,
+          client->GetProtectedBufferManager()),
       std::move(receiver));
 }
 
 void CreateArcVideoEncodeAccelerator(
     const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
     mojo::PendingReceiver<::arc::mojom::VideoEncodeAccelerator> receiver) {
   mojo::MakeSelfOwnedReceiver(
-      std::make_unique<arc::GpuArcVideoEncodeAccelerator>(gpu_preferences),
+      std::make_unique<arc::GpuArcVideoEncodeAccelerator>(gpu_preferences,
+                                                          gpu_workarounds),
       std::move(receiver));
 }
 
@@ -73,14 +77,15 @@
 void ExposeChromeGpuInterfacesToBrowser(
     ChromeContentGpuClient* client,
     const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
     mojo::BinderMap* binders) {
 #if defined(OS_CHROMEOS)
   binders->Add(base::BindRepeating(&CreateArcVideoDecodeAccelerator, client,
-                                   gpu_preferences),
+                                   gpu_preferences, gpu_workarounds),
                base::ThreadTaskRunnerHandle::Get());
-  binders->Add(
-      base::BindRepeating(&CreateArcVideoEncodeAccelerator, gpu_preferences),
-      base::ThreadTaskRunnerHandle::Get());
+  binders->Add(base::BindRepeating(&CreateArcVideoEncodeAccelerator,
+                                   gpu_preferences, gpu_workarounds),
+               base::ThreadTaskRunnerHandle::Get());
   binders->Add(
       base::BindRepeating(&CreateArcVideoProtectedBufferAllocator, client),
       base::ThreadTaskRunnerHandle::Get());
diff --git a/chrome/gpu/browser_exposed_gpu_interfaces.h b/chrome/gpu/browser_exposed_gpu_interfaces.h
index 0c3c471..ba2b28f 100644
--- a/chrome/gpu/browser_exposed_gpu_interfaces.h
+++ b/chrome/gpu/browser_exposed_gpu_interfaces.h
@@ -7,6 +7,7 @@
 
 namespace gpu {
 struct GpuPreferences;
+class GpuDriverBugWorkarounds;
 }
 
 namespace mojo {
@@ -21,6 +22,7 @@
 void ExposeChromeGpuInterfacesToBrowser(
     ChromeContentGpuClient* client,
     const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
     mojo::BinderMap* binders);
 
 #endif  // CHROME_GPU_BROWSER_EXPOSED_GPU_INTERFACES_H_
diff --git a/chrome/gpu/chrome_content_gpu_client.cc b/chrome/gpu/chrome_content_gpu_client.cc
index ebc4994..5c24ce2 100644
--- a/chrome/gpu/chrome_content_gpu_client.cc
+++ b/chrome/gpu/chrome_content_gpu_client.cc
@@ -64,11 +64,13 @@
 
 void ChromeContentGpuClient::ExposeInterfacesToBrowser(
     const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
     mojo::BinderMap* binders) {
   // NOTE: Do not add binders directly within this method. Instead, modify the
   // definition of |ExposeChromeGpuInterfacesToBrowser()|, as this ensures
   // security review coverage.
-  ExposeChromeGpuInterfacesToBrowser(this, gpu_preferences, binders);
+  ExposeChromeGpuInterfacesToBrowser(this, gpu_preferences, gpu_workarounds,
+                                     binders);
 }
 
 void ChromeContentGpuClient::PostIOThreadCreated(
diff --git a/chrome/gpu/chrome_content_gpu_client.h b/chrome/gpu/chrome_content_gpu_client.h
index acffe95..8069dc125 100644
--- a/chrome/gpu/chrome_content_gpu_client.h
+++ b/chrome/gpu/chrome_content_gpu_client.h
@@ -27,8 +27,10 @@
 
   // content::ContentGpuClient:
   void GpuServiceInitialized() override;
-  void ExposeInterfacesToBrowser(const gpu::GpuPreferences& gpu_preferences,
-                                 mojo::BinderMap* binders) override;
+  void ExposeInterfacesToBrowser(
+      const gpu::GpuPreferences& gpu_preferences,
+      const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
+      mojo::BinderMap* binders) override;
   void PostIOThreadCreated(
       base::SingleThreadTaskRunner* io_task_runner) override;
   void PostCompositorThreadCreated(
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index f8e39c5..f4a7da3 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -319,6 +319,8 @@
       "../browser/webshare/win/fake_data_transfer_manager.h",
       "../browser/webshare/win/fake_data_transfer_manager_interop.cc",
       "../browser/webshare/win/fake_data_transfer_manager_interop.h",
+      "../browser/webshare/win/scoped_fake_data_transfer_manager_interop.cc",
+      "../browser/webshare/win/scoped_fake_data_transfer_manager_interop.h",
       "//chrome/app/chrome_crash_reporter_client_win.cc",
     ]
     public_deps += [
@@ -3060,11 +3062,6 @@
           [ "../browser/site_isolation/spellcheck_per_process_browsertest.cc" ]
     }
 
-    if (is_win || is_mac || (is_desktop_linux && !chromeos_is_browser_only)) {
-      sources +=
-          [ "../browser/ui/views/profiles/profile_picker_view_browsertest.cc" ]
-    }
-
     sources += metric_integration_sources
   }
 }
@@ -3481,6 +3478,7 @@
     "../browser/notifications/notification_permission_context_unittest.cc",
     "../browser/notifications/notification_platform_bridge_chromeos_unittest.cc",
     "../browser/notifications/notification_platform_bridge_mac_unittest.mm",
+    "../browser/notifications/notification_platform_bridge_mac_utils_unittest.mm",
     "../browser/notifications/notification_trigger_scheduler_unittest.cc",
     "../browser/notifications/persistent_notification_handler_unittest.cc",
     "../browser/notifications/platform_notification_service_unittest.cc",
@@ -5622,6 +5620,7 @@
       "../browser/ui/cocoa/main_menu_builder_unittest.mm",
       "../browser/ui/cocoa/notifications/notification_builder_mac_unittest.mm",
       "../browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm",
+      "../browser/ui/cocoa/notifications/unnotification_response_builder_mac_unittest.mm",
       "../browser/ui/cocoa/nsmenuitem_additions_unittest.mm",
       "../browser/ui/cocoa/profiles/profile_menu_controller_unittest.mm",
       "../browser/ui/cocoa/scoped_menu_bar_lock_unittest.mm",
@@ -5663,6 +5662,7 @@
       "../browser/ui/views/uninstall_view_unittest.cc",
       "../browser/webshare/win/fake_data_transfer_manager_interop_unittest.cc",
       "../browser/webshare/win/fake_data_transfer_manager_unittest.cc",
+      "../browser/webshare/win/show_share_ui_for_window_operation_unittest.cc",
     ]
     deps += [
       "//chrome:other_version",
@@ -6158,6 +6158,9 @@
       "../browser/password_manager/password_manager_interactive_test_base.h",
       "../browser/password_manager/password_manager_interactive_uitest.cc",
       "../browser/portal/portal_interactive_uittest.cc",
+      "../browser/renderer_context_menu/copy_link_to_text_menu_observer_interactive_uitest.cc",
+      "../browser/renderer_context_menu/mock_render_view_context_menu.cc",
+      "../browser/renderer_context_menu/mock_render_view_context_menu.h",
       "../browser/renderer_context_menu/render_view_context_menu_browsertest_util.cc",
       "../browser/renderer_context_menu/render_view_context_menu_browsertest_util.h",
       "../browser/resource_coordinator/tab_metrics_logger_interactive_uitest.cc",
diff --git a/chrome/test/data/pdf/viewer_pdf_toolbar_new_test.js b/chrome/test/data/pdf/viewer_pdf_toolbar_new_test.js
index 89dfb90..436484555 100644
--- a/chrome/test/data/pdf/viewer_pdf_toolbar_new_test.js
+++ b/chrome/test/data/pdf/viewer_pdf_toolbar_new_test.js
@@ -232,9 +232,18 @@
 
   function testSidenavToggleButton() {
     const toolbar = createToolbar();
+    chrome.test.assertFalse(toolbar.sidenavCollapsed);
+
+    const toggleButton = toolbar.shadowRoot.querySelector('#sidenavToggle');
+    chrome.test.assertTrue(toggleButton.hasAttribute('aria-label'));
+    chrome.test.assertTrue(toggleButton.hasAttribute('title'));
+    chrome.test.assertEq('true', toggleButton.getAttribute('aria-expanded'));
+
+    toolbar.sidenavCollapsed = true;
+    chrome.test.assertEq('false', toggleButton.getAttribute('aria-expanded'));
+
     toolbar.addEventListener(
         'sidenav-toggle-click', () => chrome.test.succeed());
-    const toggleButton = toolbar.shadowRoot.querySelector('#sidenavToggle');
     toggleButton.click();
   },
 ];
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index d4f40a2..0c7d9cf 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -304,6 +304,7 @@
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/nearby_share_subpage_tests.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/network_proxy_section_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/network_summary_item_test.m.js",
+        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/network_summary_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_edit_dictionary_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_languages_page_tests.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.m.js",
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index b37a8f7..94af9a1 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -54,6 +54,7 @@
     "nearby_share_receive_dialog_tests.js",
     "nearby_share_subpage_tests.js",
     "network_proxy_section_test.js",
+    "network_summary_test.js",
     "network_summary_item_test.js",
     "os_edit_dictionary_page_test.js",
     "os_languages_page_tests.js",
diff --git a/chrome/test/data/webui/settings/chromeos/network_summary_test.js b/chrome/test/data/webui/settings/chromeos/network_summary_test.js
new file mode 100644
index 0000000..ec213b78
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/network_summary_test.js
@@ -0,0 +1,27 @@
+// Copyright 2020 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.
+
+// clang-format off
+// #import 'chrome://os-settings/chromeos/os_settings.js';
+
+// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+// clang-format on
+
+suite('NetworkSummary', function() {
+  /** @type {!NetworkSummaryElement|undefined} */
+  let netSummary;
+
+  setup(function() {
+    netSummary = document.createElement('network-summary');
+    document.body.appendChild(netSummary);
+    Polymer.dom.flush();
+  });
+
+  test('Default network summary item', function() {
+    const summaryItems =
+        netSummary.shadowRoot.querySelectorAll('network-summary-item');
+    assertEquals(1, summaryItems.length);
+    assertEquals('WiFi', summaryItems[0].id);
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 160bf95..cceb0dd2 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -1203,6 +1203,20 @@
 });
 
 // eslint-disable-next-line no-var
+var OSSettingsNetworkSummaryTest = class extends OSSettingsBrowserTest {
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      'network_summary_test.js',
+    ]);
+  }
+};
+
+TEST_F('OSSettingsNetworkSummaryTest', 'All', () => {
+  mocha.run();
+});
+
+// eslint-disable-next-line no-var
 var OSSettingsPeoplePageAccountManagerTest =
     class extends OSSettingsBrowserTest {
   /** @override */
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index 2892e848..a04490f2 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -105,6 +105,7 @@
  ['MultideviceSmartLockSubPage', 'multidevice_smartlock_subpage_test.m.js'],
  ['MultideviceSubPage', 'multidevice_subpage_tests.m.js'],
  ['NetworkProxySection', 'network_proxy_section_test.m.js'],
+ ['NetworkSummary', 'network_summary_test.m.js'],
  ['NetworkSummaryItem', 'network_summary_item_test.m.js'],
  ['OsEditDictionaryPage', 'os_edit_dictionary_page_test.m.js'],
  ['OsLanguagesPage', 'os_languages_page_tests.m.js'],
diff --git a/chrome/test/data/webui/settings/passwords_section_test.js b/chrome/test/data/webui/settings/passwords_section_test.js
index 66de7a11..60bebf7 100644
--- a/chrome/test/data/webui/settings/passwords_section_test.js
+++ b/chrome/test/data/webui/settings/passwords_section_test.js
@@ -193,7 +193,7 @@
 async function changeSavedPasswordTestHelper(
     editDialog, entryIds, passwordManager) {
   const PASSWORD1 = 'hello_world';
-
+  const USERNAME1 = 'new_username';
   editDialog.set('entry.password', PASSWORD1);
   assertEquals(PASSWORD1, editDialog.$.passwordInput.value);
 
@@ -203,6 +203,7 @@
   assertTrue(editDialog.$.actionButton.disabled);
 
   const PASSWORD2 = 'hello_world_2';
+  editDialog.$.usernameInput.value = USERNAME1;
   editDialog.$.passwordInput.value = PASSWORD2;
   assertFalse(editDialog.$.passwordInput.invalid);
   assertFalse(editDialog.$.actionButton.disabled);
@@ -210,8 +211,9 @@
   editDialog.$.actionButton.click();
 
   // Check that the changeSavedPassword is called with the right arguments.
-  const {ids, newPassword} =
+  const {ids, newUsername, newPassword} =
       await passwordManager.whenCalled('changeSavedPassword');
+  assertEquals(USERNAME1, newUsername);
   assertEquals(PASSWORD2, newPassword);
 
   assertEquals(entryIds.length, ids.length);
@@ -1160,6 +1162,26 @@
         passwordManager);
   });
 
+  test('editDialogChangeUsernameFailsWhenReused', async function() {
+    loadTimeData.overrideValues({editPasswordsInSettings: true});
+
+    const accountEntry = createMultiStorePasswordEntry(
+        {url: 'goo.gl', username: 'bart', accountId: 0});
+    const editDialog = elementFactory.createPasswordEditDialog(accountEntry);
+    editDialog.usernamesForSameOrigin = new Set(['mark', 'bart']);
+
+    editDialog.$.usernameInput.value = 'mark';
+    assertTrue(editDialog.$.usernameInput.invalid);
+    assertTrue(editDialog.$.actionButton.disabled);
+
+    editDialog.$.usernameInput.value = 'new_mark';
+    assertFalse(editDialog.$.usernameInput.invalid);
+    assertFalse(editDialog.$.actionButton.disabled);
+
+    changeSavedPasswordTestHelper(
+        editDialog, [accountEntry.accountId], passwordManager);
+  });
+
   // Test verifies that the edit dialog informs the password is stored in the
   // account.
   test('verifyStorageDetailsInEditDialogForAccountPassword', function() {
diff --git a/chrome/updater/app/app_uninstall.cc b/chrome/updater/app/app_uninstall.cc
index 0a1df9a4..a0fe64b 100644
--- a/chrome/updater/app/app_uninstall.cc
+++ b/chrome/updater/app/app_uninstall.cc
@@ -5,10 +5,12 @@
 #include "chrome/updater/app/app_uninstall.h"
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "build/build_config.h"
 #include "chrome/updater/app/app.h"
+#include "chrome/updater/constants.h"
 
 #if defined(OS_WIN)
 #include "chrome/updater/win/setup/uninstall.h"
@@ -31,9 +33,22 @@
 };
 
 void AppUninstall::FirstTaskRun() {
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock()}, base::BindOnce(&Uninstall, false),
-      base::BindOnce(&AppUninstall::Shutdown, this));
+#if defined(OS_MAC)
+  // TODO(crbug.com/1114719): Implement --uninstall=self for Win.
+  const std::string uninstall_switch_value =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          kUninstallSwitch);
+  if (!uninstall_switch_value.empty()) {
+    base::ThreadPool::PostTaskAndReplyWithResult(
+        FROM_HERE, {base::MayBlock()}, base::BindOnce(&UninstallCandidate),
+        base::BindOnce(&AppUninstall::Shutdown, this));
+  } else
+#endif
+  {
+    base::ThreadPool::PostTaskAndReplyWithResult(
+        FROM_HERE, {base::MayBlock()}, base::BindOnce(&Uninstall, false),
+        base::BindOnce(&AppUninstall::Shutdown, this));
+  }
 }
 
 scoped_refptr<App> MakeAppUninstall() {
diff --git a/chrome/updater/mac/setup/setup.mm b/chrome/updater/mac/setup/setup.mm
index 8628f0d..3d624b4 100644
--- a/chrome/updater/mac/setup/setup.mm
+++ b/chrome/updater/mac/setup/setup.mm
@@ -8,6 +8,7 @@
 
 #include "base/at_exit.h"
 #include "base/command_line.h"
+#include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
@@ -15,6 +16,8 @@
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
 #include "base/strings/strcat.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
@@ -81,6 +84,12 @@
   return GetUpdaterFolderPath().AppendASCII(UPDATER_VERSION_STRING);
 }
 
+const base::FilePath GetUpdaterExecutablePath(
+    const base::FilePath& updater_folder_path) {
+  return updater_folder_path.Append(GetUpdaterAppName())
+      .Append(GetUpdaterAppExecutablePath());
+}
+
 Launchd::Domain LaunchdDomain() {
   return IsSystemInstall() ? Launchd::Domain::Local : Launchd::Domain::User;
 }
@@ -286,7 +295,7 @@
   return RemoveServiceJobFromLaunchd(CopyControlLaunchdName());
 }
 
-bool DeleteInstallFolder(const base::FilePath& installed_path) {
+bool DeleteFolder(const base::FilePath& installed_path) {
   if (!base::DeletePathRecursively(installed_path)) {
     LOG(ERROR) << "Deleting " << installed_path << " failed";
     return false;
@@ -295,14 +304,18 @@
 }
 
 bool DeleteInstallFolder() {
-  return DeleteInstallFolder(GetUpdaterFolderPath());
+  return DeleteFolder(GetUpdaterFolderPath());
+}
+
+bool DeleteCandidateInstallFolder() {
+  return DeleteFolder(GetVersionedUpdaterFolderPath());
 }
 
 bool DeleteDataFolder() {
   base::FilePath data_path;
   if (!GetBaseDirectory(&data_path))
     return false;
-  return DeleteInstallFolder(data_path);
+  return DeleteFolder(data_path);
 }
 
 }  // namespace
@@ -332,19 +345,6 @@
   return setup_exit_codes::kSuccess;
 }
 
-int UninstallCandidate() {
-  if (!RemoveUpdateWakeJobFromLaunchd())
-    return setup_exit_codes::kFailedToRemoveWakeJobFromLaunchd;
-
-  if (!DeleteInstallFolder(GetVersionedUpdaterFolderPath()))
-    return setup_exit_codes::kFailedToDeleteFolder;
-
-  if (!RemoveUpdateControlJobFromLaunchd())
-    return setup_exit_codes::kFailedToRemoveControlJobFromLaunchd;
-
-  return setup_exit_codes::kSuccess;
-}
-
 int PromoteCandidate() {
   const base::FilePath dest_path = GetVersionedUpdaterFolderPath();
   const base::FilePath updater_executable_path =
@@ -361,8 +361,49 @@
 }
 
 #pragma mark Uninstall
+int UninstallCandidate() {
+  if (!RemoveUpdateWakeJobFromLaunchd())
+    return setup_exit_codes::kFailedToRemoveWakeJobFromLaunchd;
+
+  if (!RemoveUpdateControlJobFromLaunchd())
+    return setup_exit_codes::kFailedToRemoveControlJobFromLaunchd;
+
+  if (!DeleteCandidateInstallFolder())
+    return setup_exit_codes::kFailedToDeleteFolder;
+
+  return setup_exit_codes::kSuccess;
+}
+
+void UninstallOtherVersions() {
+  base::FileEnumerator file_enumerator(GetUpdaterFolderPath(), true,
+                                       base::FileEnumerator::DIRECTORIES);
+  for (base::FilePath version_folder_path = file_enumerator.Next();
+       !version_folder_path.empty() &&
+       version_folder_path != GetVersionedUpdaterFolderPath();
+       version_folder_path = file_enumerator.Next()) {
+    const base::FilePath version_executable_path =
+        GetUpdaterExecutablePath(version_folder_path);
+
+    if (base::PathExists(version_executable_path)) {
+      base::CommandLine command_line(version_executable_path);
+      command_line.AppendSwitchASCII(kUninstallSwitch, "self");
+      command_line.AppendSwitch("--enable-logging");
+      command_line.AppendSwitchASCII("--vmodule", "*/chrome/updater/*=2");
+
+      int exit_code = -1;
+      std::string output;
+      base::GetAppOutputWithExitCode(command_line, &output, &exit_code);
+    } else {
+      VLOG(1) << base::CommandLine::ForCurrentProcess()->GetCommandLineString()
+              << " : Path doesn't exist: " << version_executable_path;
+    }
+  }
+}
+
 int Uninstall(bool is_machine) {
   ALLOW_UNUSED_LOCAL(is_machine);
+  VLOG(1) << base::CommandLine::ForCurrentProcess()->GetCommandLineString()
+          << " : " << __func__;
   const int exit = UninstallCandidate();
   if (exit != setup_exit_codes::kSuccess)
     return exit;
@@ -370,6 +411,8 @@
   if (!RemoveUpdateServiceJobFromLaunchd())
     return setup_exit_codes::kFailedToRemoveActiveUpdateServiceJobFromLaunchd;
 
+  UninstallOtherVersions();
+
   if (!DeleteDataFolder())
     return setup_exit_codes::kFailedToDeleteDataFolder;
 
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsComponent.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsComponent.java
index c04f9041..610184f3 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsComponent.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsComponent.java
@@ -137,6 +137,15 @@
                 context, webContents, enableTouch, isRemoteControlMode, turnOnScreen, mSessionId);
         if (DEBUG) Log.d(TAG, "start activity by intent: " + intent);
         ResumeIntents.addResumeIntent(mSessionId, intent);
+
+        CastAudioManager audioManager =
+                CastAudioManager.getAudioManager(ContextUtils.getApplicationContext());
+        Observable<CastAudioManager.AudioFocusLoss> focusLoss =
+                audioManager.requestAudioFocusWhen(mAudioFocusRequestState)
+                        .filter(state -> state == CastAudioManager.AudioFocusLoss.NORMAL);
+        mAudioFocusRequestState.andThen(focusLoss).subscribe(
+                Observers.onEnter(x -> mComponentClosedHandler.onComponentClosed()));
+
         context.startActivity(intent);
     }
 
@@ -209,13 +218,6 @@
         mSurfaceEventHandler = surfaceEventHandler;
         mIsRemoteControlMode = isRemoteControlMode;
         mTurnOnScreen = turnOnScreen;
-        CastAudioManager audioManager =
-                CastAudioManager.getAudioManager(ContextUtils.getApplicationContext());
-        Observable<CastAudioManager.AudioFocusLoss> focusLoss =
-                audioManager.requestAudioFocusWhen(mAudioFocusRequestState)
-                        .filter(state -> state == CastAudioManager.AudioFocusLoss.NORMAL);
-        mAudioFocusRequestState.andThen(focusLoss).subscribe(
-                Observers.onEnter(x -> mComponentClosedHandler.onComponentClosed()));
 
         if (BuildConfig.DISPLAY_WEB_CONTENTS_IN_SERVICE || isHeadless) {
             if (DEBUG) Log.d(TAG, "Creating service delegate...");
diff --git a/chromeos/crosapi/cpp/BUILD.gn b/chromeos/crosapi/cpp/BUILD.gn
index 20c74da..276fac2e 100644
--- a/chromeos/crosapi/cpp/BUILD.gn
+++ b/chromeos/crosapi/cpp/BUILD.gn
@@ -14,6 +14,8 @@
     "bitmap.h",
     "bitmap_util.cc",
     "bitmap_util.h",
+    "crosapi_constants.cc",
+    "crosapi_constants.h",
   ]
   configs += [ ":crosapi_implementation" ]
   deps = [
diff --git a/chromeos/crosapi/cpp/crosapi_constants.cc b/chromeos/crosapi/cpp/crosapi_constants.cc
new file mode 100644
index 0000000..50f66b3
--- /dev/null
+++ b/chromeos/crosapi/cpp/crosapi_constants.cc
@@ -0,0 +1,14 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/crosapi/cpp/crosapi_constants.h"
+
+namespace crosapi {
+
+// The prefix for a Wayland app id for a Lacros browser window. The full ID is
+// suffixed with a serialized unguessable token unique to each window. The
+// trailing "." is intentional.
+const char kLacrosAppIdPrefix[] = "org.chromium.lacros.";
+
+}  // namespace crosapi
diff --git a/chromeos/crosapi/cpp/crosapi_constants.h b/chromeos/crosapi/cpp/crosapi_constants.h
new file mode 100644
index 0000000..1d141f9
--- /dev/null
+++ b/chromeos/crosapi/cpp/crosapi_constants.h
@@ -0,0 +1,16 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_CROSAPI_CPP_CROSAPI_CONSTANTS_H_
+#define CHROMEOS_CROSAPI_CPP_CROSAPI_CONSTANTS_H_
+
+#include "base/component_export.h"
+
+namespace crosapi {
+
+COMPONENT_EXPORT(CROSAPI) extern const char kLacrosAppIdPrefix[];
+
+}  // namespace crosapi
+
+#endif  // CHROMEOS_CROSAPI_CPP_CROSAPI_CONSTANTS_H_
diff --git a/chromeos/profiles/airmont.afdo.newest.txt b/chromeos/profiles/airmont.afdo.newest.txt
index 84d5a26..432d900 100644
--- a/chromeos/profiles/airmont.afdo.newest.txt
+++ b/chromeos/profiles/airmont.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-airmont-87-4262.0-1600690455-benchmark-87.0.4275.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-airmont-87-4265.0-1601286022-benchmark-87.0.4275.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/broadwell.afdo.newest.txt b/chromeos/profiles/broadwell.afdo.newest.txt
index 9a7782f..fcd570d 100644
--- a/chromeos/profiles/broadwell.afdo.newest.txt
+++ b/chromeos/profiles/broadwell.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-broadwell-87-4247.0-1600681610-benchmark-87.0.4275.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-broadwell-87-4258.0-1601290734-benchmark-87.0.4275.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/silvermont.afdo.newest.txt b/chromeos/profiles/silvermont.afdo.newest.txt
index 72641d2..2906496f 100644
--- a/chromeos/profiles/silvermont.afdo.newest.txt
+++ b/chromeos/profiles/silvermont.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-silvermont-87-4258.0-1600689257-benchmark-87.0.4275.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-silvermont-87-4265.0-1601289755-benchmark-87.0.4275.0-r1-redacted.afdo.xz
diff --git a/components/arc/video_accelerator/DEPS b/components/arc/video_accelerator/DEPS
index 86036be..8b54cc3f 100644
--- a/components/arc/video_accelerator/DEPS
+++ b/components/arc/video_accelerator/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+components/arc/mojom",
+  "+gpu/config/gpu_driver_bug_workarounds.h",
   "+gpu/config/gpu_preferences.h",
   "+gpu/ipc/common/gpu_memory_buffer_support.h",
   "+media/base",
diff --git a/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc b/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
index f202a26..49fc7e87 100644
--- a/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
+++ b/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
@@ -79,8 +79,10 @@
 
 GpuArcVideoDecodeAccelerator::GpuArcVideoDecodeAccelerator(
     const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
     scoped_refptr<ProtectedBufferManager> protected_buffer_manager)
     : gpu_preferences_(gpu_preferences),
+      gpu_workarounds_(gpu_workarounds),
       protected_buffer_manager_(std::move(protected_buffer_manager)) {}
 
 GpuArcVideoDecodeAccelerator::~GpuArcVideoDecodeAccelerator() {
@@ -320,8 +322,8 @@
 
   auto vda_factory = media::GpuVideoDecodeAcceleratorFactory::Create(
       media::GpuVideoDecodeGLClient());
-  vda_ = vda_factory->CreateVDA(
-      this, vda_config, gpu::GpuDriverBugWorkarounds(), gpu_preferences_);
+  vda_ = vda_factory->CreateVDA(this, vda_config, gpu_workarounds_,
+                                gpu_preferences_);
   if (!vda_) {
     VLOGF(1) << "Failed to create VDA.";
     return mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE;
diff --git a/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h b/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h
index 7f4b8e9..6d8e579 100644
--- a/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h
+++ b/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h
@@ -15,6 +15,7 @@
 #include "base/optional.h"
 #include "base/threading/thread_checker.h"
 #include "components/arc/mojom/video_decode_accelerator.mojom.h"
+#include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_preferences.h"
 #include "media/video/video_decode_accelerator.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -40,6 +41,7 @@
  public:
   GpuArcVideoDecodeAccelerator(
       const gpu::GpuPreferences& gpu_preferences,
+      const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
       scoped_refptr<ProtectedBufferManager> protected_buffer_manager);
   ~GpuArcVideoDecodeAccelerator() override;
 
@@ -141,6 +143,7 @@
   ResetCallback pending_reset_callback_;
 
   gpu::GpuPreferences gpu_preferences_;
+  gpu::GpuDriverBugWorkarounds gpu_workarounds_;
   std::unique_ptr<media::VideoDecodeAccelerator> vda_;
   mojo::Remote<mojom::VideoDecodeClient> client_;
 
diff --git a/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc b/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
index 4db437ae..04069c75 100644
--- a/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
+++ b/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
@@ -64,8 +64,10 @@
 }  // namespace
 
 GpuArcVideoEncodeAccelerator::GpuArcVideoEncodeAccelerator(
-    const gpu::GpuPreferences& gpu_preferences)
+    const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds)
     : gpu_preferences_(gpu_preferences),
+      gpu_workarounds_(gpu_workarounds),
       input_storage_type_(
           media::VideoEncodeAccelerator::Config::StorageType::kShmem),
       bitstream_buffer_serial_(0) {}
@@ -109,7 +111,7 @@
     GetSupportedProfilesCallback callback) {
   std::move(callback).Run(
       media::GpuVideoEncodeAcceleratorFactory::GetSupportedProfiles(
-          gpu_preferences_));
+          gpu_preferences_, gpu_workarounds_));
 }
 
 void GpuArcVideoEncodeAccelerator::Initialize(
@@ -142,7 +144,7 @@
   input_storage_type_ = *config.storage_type;
   visible_size_ = config.input_visible_size;
   accelerator_ = media::GpuVideoEncodeAcceleratorFactory::CreateVEA(
-      config, this, gpu_preferences_);
+      config, this, gpu_preferences_, gpu_workarounds_);
   if (accelerator_ == nullptr) {
     DLOG(ERROR) << "Failed to create a VideoEncodeAccelerator.";
     return mojom::VideoEncodeAccelerator::Result::kPlatformFailureError;
diff --git a/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h b/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h
index 8531b7e..47bf8f2 100644
--- a/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h
+++ b/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "components/arc/mojom/video_encode_accelerator.mojom.h"
 #include "components/arc/video_accelerator/video_frame_plane.h"
+#include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_preferences.h"
 #include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/video/video_encode_accelerator.h"
@@ -28,7 +29,8 @@
       public media::VideoEncodeAccelerator::Client {
  public:
   explicit GpuArcVideoEncodeAccelerator(
-      const gpu::GpuPreferences& gpu_preferences);
+      const gpu::GpuPreferences& gpu_preferences,
+      const gpu::GpuDriverBugWorkarounds& gpu_workarounds);
   ~GpuArcVideoEncodeAccelerator() override;
 
  private:
@@ -87,6 +89,7 @@
                           EncodeCallback callback);
 
   gpu::GpuPreferences gpu_preferences_;
+  gpu::GpuDriverBugWorkarounds gpu_workarounds_;
   std::unique_ptr<media::VideoEncodeAccelerator> accelerator_;
   mojo::Remote<::arc::mojom::VideoEncodeClient> client_;
   gfx::Size coded_size_;
diff --git a/components/cronet/stale_host_resolver_unittest.cc b/components/cronet/stale_host_resolver_unittest.cc
index 89a7e03b..64702a98 100644
--- a/components/cronet/stale_host_resolver_unittest.cc
+++ b/components/cronet/stale_host_resolver_unittest.cc
@@ -705,7 +705,7 @@
   std::unique_ptr<net::URLRequestContext> context(builder.Build());
 
   // Experimental options ensure context's resolver is a StaleHostResolver.
-  SetResolver(reinterpret_cast<StaleHostResolver*>(context->host_resolver()),
+  SetResolver(static_cast<StaleHostResolver*>(context->host_resolver()),
               context.get());
   // Note: Experimental config above sets 0ms stale delay.
   CreateCacheEntry(kAgeExpiredSec, net::OK);
diff --git a/components/exo/input_method_surface.cc b/components/exo/input_method_surface.cc
index 3719714..398dab2 100644
--- a/components/exo/input_method_surface.cc
+++ b/components/exo/input_method_surface.cc
@@ -10,6 +10,7 @@
 #include "ui/base/class_property.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/views/accessibility/view_accessibility.h"
 
 DEFINE_UI_CLASS_PROPERTY_KEY(exo::InputMethodSurface*,
@@ -69,10 +70,16 @@
     manager_->AddSurface(this);
   }
 
-  const gfx::Rect new_bounds = gfx::ConvertRectToDIP(
-      GetScale(), root_surface()->hit_test_region().bounds());
-  if (input_method_bounds_ != new_bounds) {
-    input_method_bounds_ = new_bounds;
+  gfx::RectF new_bounds_in_dips = gfx::ConvertRectToDips(
+      root_surface()->hit_test_region().bounds(), GetScale());
+  // TODO(crbug.com/1131682): We should avoid dropping precision to integers
+  // here if we want to know the true rectangle bounds in DIPs. If not, we
+  // should use ToEnclosingRect() if we want to include DIPs that partly overlap
+  // the physical pixel bounds, or ToEnclosedRect() if we do not.
+  gfx::Rect int_bounds_in_dips =
+      gfx::ToFlooredRectDeprecated(new_bounds_in_dips);
+  if (input_method_bounds_ != int_bounds_in_dips) {
+    input_method_bounds_ = int_bounds_in_dips;
     manager_->OnTouchableBoundsChanged(this);
 
     GetViewAccessibility().OverrideBounds(gfx::RectF(input_method_bounds_));
diff --git a/components/offline_pages/core/background/request_coordinator_unittest.cc b/components/offline_pages/core/background/request_coordinator_unittest.cc
index 24cb7213..a703d90b 100644
--- a/components/offline_pages/core/background/request_coordinator_unittest.cc
+++ b/components/offline_pages/core/background/request_coordinator_unittest.cc
@@ -187,7 +187,7 @@
   }
   OfflinerStub* offliner() const { return offliner_; }
   SchedulerStub* scheduler_stub() const {
-    return reinterpret_cast<SchedulerStub*>(coordinator()->scheduler());
+    return static_cast<SchedulerStub*>(coordinator()->scheduler());
   }
   RequestQueue* queue() {
     return coordinator_taco_->request_coordinator()->queue_for_testing();
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index be2e58b..a5fe206 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -201,6 +201,8 @@
     "psl_matching_helper.cc",
     "psl_matching_helper.h",
     "reauth_purpose.h",
+    "site_affiliation/affiliation_fetcher_base.cc",
+    "site_affiliation/affiliation_fetcher_base.h",
     "site_affiliation/affiliation_fetcher_factory.h",
     "site_affiliation/affiliation_fetcher_factory_impl.cc",
     "site_affiliation/affiliation_fetcher_factory_impl.h",
diff --git a/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.cc b/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.cc
index 80e6ddc3..f067abce 100644
--- a/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.cc
+++ b/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.cc
@@ -90,6 +90,7 @@
 
 void AffiliatedMatchHelper::InjectAffiliationAndBrandingInformation(
     std::vector<std::unique_ptr<PasswordForm>> forms,
+    AndroidAffiliationService::StrategyOnCacheMiss strategy_on_cache_miss,
     PasswordFormsCallback result_callback) {
   std::vector<PasswordForm*> android_credentials;
   for (const auto& form : forms) {
@@ -103,7 +104,7 @@
   for (auto* form : android_credentials) {
     affiliation_service_->GetAffiliationsAndBranding(
         FacetURI::FromPotentiallyInvalidSpec(form->signon_realm),
-        AndroidAffiliationService::StrategyOnCacheMiss::FAIL,
+        strategy_on_cache_miss,
         base::BindOnce(&AffiliatedMatchHelper::
                            CompleteInjectAffiliationAndBrandingInformation,
                        weak_ptr_factory_.GetWeakPtr(), base::Unretained(form),
diff --git a/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h b/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h
index 8b6c535c..6e00bb5 100644
--- a/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h
+++ b/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h
@@ -14,6 +14,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+#include "components/password_manager/core/browser/android_affiliation/android_affiliation_service.h"
 #include "components/password_manager/core/browser/password_form_forward.h"
 #include "components/password_manager/core/browser/password_store.h"
 #include "components/password_manager/core/browser/password_store_consumer.h"
@@ -79,11 +80,13 @@
   // Retrieves affiliation and branding information about the Android
   // credentials in |forms|, sets |affiliated_web_realm|, |app_display_name| and
   // |app_icon_url| of forms, and invokes |result_callback|.
-  // NOTE: This will not issue an on-demand network request. If a request to
-  // cache fails, no affiliation and branding information will be injected into
-  // corresponding form.
+  // NOTE: When |strategy_on_cache_miss| is set to |FAIL|, this will not issue
+  // an on-demand network request. And if a request to cache fails, no
+  // affiliation and branding information will be injected into corresponding
+  // form.
   virtual void InjectAffiliationAndBrandingInformation(
       std::vector<std::unique_ptr<PasswordForm>> forms,
+      AndroidAffiliationService::StrategyOnCacheMiss strategy_on_cache_miss,
       PasswordFormsCallback result_callback);
 
   // Returns whether or not |form| represents an Android credential.
diff --git a/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc b/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc
index 837ac36..99c0e7762 100644
--- a/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc
+++ b/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc
@@ -286,7 +286,7 @@
       std::vector<std::unique_ptr<PasswordForm>> forms) {
     expecting_result_callback_ = true;
     match_helper()->InjectAffiliationAndBrandingInformation(
-        std::move(forms),
+        std::move(forms), AndroidAffiliationService::StrategyOnCacheMiss::FAIL,
         base::BindOnce(&AffiliatedMatchHelperTest::OnFormsCallback,
                        base::Unretained(this)));
     RunUntilIdle();
diff --git a/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc b/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc
index 8448d5fe..5c1a61b 100644
--- a/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc
+++ b/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc
@@ -4,57 +4,30 @@
 
 #include "components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h"
 
-#include <stddef.h>
-
-#include <map>
 #include <memory>
 #include <string>
-#include <utility>
 
 #include "base/bind.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
 #include "components/password_manager/core/browser/android_affiliation/affiliation_api.pb.h"
 #include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
-#include "components/password_manager/core/browser/android_affiliation/lookup_affiliation_response_parser.h"
 #include "google_apis/google_api_keys.h"
-#include "net/base/load_flags.h"
 #include "net/base/url_util.h"
-#include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
 #include "url/gurl.h"
 
 namespace password_manager {
 
-// Enumeration listing the possible outcomes of fetching affiliation information
-// from the Affiliation API. This is used in UMA histograms, so do not change
-// existing values, only add new values at the end.
-enum AffiliationFetchResult {
-  AFFILIATION_FETCH_RESULT_SUCCESS,
-  AFFILIATION_FETCH_RESULT_FAILURE,
-  AFFILIATION_FETCH_RESULT_MALFORMED,
-  AFFILIATION_FETCH_RESULT_MAX
-};
-
 AffiliationFetcher::AffiliationFetcher(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     AffiliationFetcherDelegate* delegate)
-    : url_loader_factory_(std::move(url_loader_factory)),
-      delegate_(delegate) {
-  for (const FacetURI& uri : requested_facet_uris_) {
-    DCHECK(uri.is_valid());
-  }
-}
+    : AffiliationFetcherBase(std::move(url_loader_factory), delegate) {}
 
 AffiliationFetcher::~AffiliationFetcher() = default;
 
 void AffiliationFetcher::StartRequest(const std::vector<FacetURI>& facet_uris,
                                       RequestInfo request_info) {
-  DCHECK(!simple_url_loader_);
-  fetch_timer_ = base::ElapsedTimer();
   requested_facet_uris_ = facet_uris;
 
   net::NetworkTrafficAnnotationTag traffic_annotation =
@@ -88,30 +61,25 @@
             }
           }
         })");
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  resource_request->url = BuildQueryURL();
-  resource_request->load_flags =
-      net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
-  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
-  resource_request->method = "POST";
-  simple_url_loader_ = network::SimpleURLLoader::Create(
-      std::move(resource_request), traffic_annotation);
-  simple_url_loader_->AttachStringForUpload(PreparePayload(request_info),
-                                            "application/x-protobuf");
-  simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      url_loader_factory_.get(),
-      base::BindOnce(&AffiliationFetcher::OnSimpleLoaderComplete,
-                     base::Unretained(this)));
+
+  // Prepare the payload based on |facet_uris| and |request_info|.
+  affiliation_pb::LookupAffiliationRequest lookup_request;
+  for (const FacetURI& uri : facet_uris)
+    lookup_request.add_facet(uri.canonical_spec());
+
+  *lookup_request.mutable_mask() = CreateLookupMask(request_info);
+
+  std::string serialized_request;
+  bool success = lookup_request.SerializeToString(&serialized_request);
+  DCHECK(success);
+
+  FinalizeRequest(serialized_request, BuildQueryURL(), traffic_annotation);
 }
 
 const std::vector<FacetURI>& AffiliationFetcher::GetRequestedFacetURIs() const {
   return requested_facet_uris_;
 }
 
-AffiliationFetcherDelegate* AffiliationFetcher::delegate() const {
-  return delegate_;
-}
-
 // static
 GURL AffiliationFetcher::BuildQueryURL() {
   return net::AppendQueryParameter(
@@ -119,90 +87,4 @@
       "key", google_apis::GetAPIKey());
 }
 
-std::string AffiliationFetcher::PreparePayload(RequestInfo request_info) const {
-  affiliation_pb::LookupAffiliationRequest lookup_request;
-  for (const FacetURI& uri : requested_facet_uris_)
-    lookup_request.add_facet(uri.canonical_spec());
-
-  auto mask = std::make_unique<affiliation_pb::LookupAffiliationMask>();
-
-  mask->set_branding_info(request_info.branding_info);
-  // Change password info requires grouping info enabled.
-  mask->set_grouping_info(request_info.change_password_info);
-  mask->set_change_password_info(request_info.change_password_info);
-
-  lookup_request.set_allocated_mask(mask.release());
-
-  std::string serialized_request;
-  bool success = lookup_request.SerializeToString(&serialized_request);
-  DCHECK(success);
-  return serialized_request;
-}
-
-bool AffiliationFetcher::ParseResponse(
-    const std::string& serialized_response,
-    AffiliationFetcherDelegate::Result* result) const {
-  // This function parses the response protocol buffer message for a list of
-  // equivalence classes, and stores them into |results| after performing some
-  // validation and sanitization steps to make sure that the contract of
-  // AffiliationFetcherDelegate is fulfilled. Possible discrepancies are:
-  //   * The server response will not have anything for facets that are not
-  //     affiliated with any other facet, while |result| must have them.
-  //   * The server response might contain future, unknown kinds of facet URIs,
-  //     while |result| must contain only those that are FacetURI::is_valid().
-  //   * The server response being ill-formed or self-inconsistent (in the sense
-  //     that there are overlapping equivalence classes) is indicative of server
-  //     side issues likely not remedied by re-fetching. Report failure in this
-  //     case so the caller can be notified and it can act accordingly.
-  //   * The |result| will be free of duplicate or empty equivalence classes.
-
-  affiliation_pb::LookupAffiliationResponse response;
-  if (!response.ParseFromString(serialized_response))
-    return false;
-
-  return ParseLookupAffiliationResponse(requested_facet_uris_, response,
-                                        result);
-}
-
-void AffiliationFetcher::OnSimpleLoaderComplete(
-    std::unique_ptr<std::string> response_body) {
-  base::UmaHistogramTimes("PasswordManager.AffiliationFetcher.FetchTime",
-                          fetch_timer_.Elapsed());
-  // Note that invoking the |delegate_| may destroy |this| synchronously, so the
-  // invocation must happen last.
-  std::unique_ptr<AffiliationFetcherDelegate::Result> result_data(
-      new AffiliationFetcherDelegate::Result);
-  if (response_body) {
-    if (ParseResponse(*response_body, result_data.get())) {
-      UMA_HISTOGRAM_ENUMERATION(
-          "PasswordManager.AffiliationFetcher.FetchResult",
-          AFFILIATION_FETCH_RESULT_SUCCESS, AFFILIATION_FETCH_RESULT_MAX);
-      delegate_->OnFetchSucceeded(std::move(result_data));
-    } else {
-      UMA_HISTOGRAM_ENUMERATION(
-          "PasswordManager.AffiliationFetcher.FetchResult",
-          AFFILIATION_FETCH_RESULT_MALFORMED, AFFILIATION_FETCH_RESULT_MAX);
-      delegate_->OnMalformedResponse();
-    }
-  } else {
-    UMA_HISTOGRAM_ENUMERATION("PasswordManager.AffiliationFetcher.FetchResult",
-                              AFFILIATION_FETCH_RESULT_FAILURE,
-                              AFFILIATION_FETCH_RESULT_MAX);
-    int response_code = -1;
-    if (simple_url_loader_->ResponseInfo() &&
-        simple_url_loader_->ResponseInfo()->headers) {
-      response_code =
-          simple_url_loader_->ResponseInfo()->headers->response_code();
-    }
-    base::UmaHistogramSparse(
-        "PasswordManager.AffiliationFetcher.FetchHttpResponseCode",
-        response_code);
-    // Network error codes are negative. See: src/net/base/net_error_list.h.
-    base::UmaHistogramSparse(
-        "PasswordManager.AffiliationFetcher.FetchErrorCode",
-        -simple_url_loader_->NetError());
-    delegate_->OnFetchFailed();
-  }
-}
-
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h b/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h
index e8f4870..362f704a 100644
--- a/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h
+++ b/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h
@@ -5,78 +5,29 @@
 #ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCHER_H_
 #define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCHER_H_
 
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/timer/elapsed_timer.h"
-#include "components/password_manager/core/browser/android_affiliation/affiliation_fetcher_interface.h"
-
-class GURL;
-
-namespace network {
-class SharedURLLoaderFactory;
-class SimpleURLLoader;
-}  // namespace network
+#include "components/password_manager/core/browser/site_affiliation/affiliation_fetcher_base.h"
 
 namespace password_manager {
 
-// Fetches authoritative information regarding which facets are affiliated with
-// each other, that is, which facets belong to the same logical application.
-// See affiliation_utils.h for a definition of what this means.
-//
-// An instance is good for exactly one fetch, and may be used from any thread
-// that runs a message loop (i.e. not a worker pool thread).
-class AffiliationFetcher : public AffiliationFetcherInterface {
+class AffiliationFetcher : public AffiliationFetcherBase {
  public:
   AffiliationFetcher(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       AffiliationFetcherDelegate* delegate);
   ~AffiliationFetcher() override;
 
-  // Builds the URL for the Affiliation API's lookup method.
-  static GURL BuildQueryURL();
-
-  // Actually starts the request to retrieve affiliations and optionally
-  // groupings for each facet in |facet_uris| along with the details based on
-  // |request_info|. Calls the delegate with the results on the same thread when
-  // done. If |this| is destroyed before completion, the in-flight request is
-  // cancelled, and the delegate will not be called. Further details:
-  //   * No cookies are sent/saved with the request.
-  //   * In case of network/server errors, the request will not be retried.
-  //   * Results are guaranteed to be always fresh and will never be cached.
+  // AffiliationFetcherInterface
   void StartRequest(const std::vector<FacetURI>& facet_uris,
                     RequestInfo request_info) override;
 
+  // AffiliationFetcherInterface
   const std::vector<FacetURI>& GetRequestedFacetURIs() const override;
 
-  AffiliationFetcherDelegate* delegate() const;
+  // Builds the URL for the Affiliation API's lookup method.
+  static GURL BuildQueryURL();
 
  private:
-  // Prepares and returns the serialized protocol buffer message that will be
-  // the payload of the POST request. Sets mask request based on |request_info|.
-  std::string PreparePayload(RequestInfo request_info) const;
-
-  // Parses and validates the response protocol buffer message for a list of
-  // equivalence classes, stores them into |result| and returns true on success.
-  // It is guaranteed that every one of the requested Facet URIs will be a
-  // member of exactly one returned equivalence class.
-  // Returns false if the response was gravely ill-formed or self-inconsistent.
-  // Unknown kinds of facet URIs and new protocol buffer fields will be ignored.
-  bool ParseResponse(const std::string& serialized_response,
-                     AffiliationFetcherDelegate::Result* result) const;
-
-  void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
-
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   std::vector<FacetURI> requested_facet_uris_;
-  AffiliationFetcherDelegate* const delegate_;
-  // Timer to track the response time of the request.
-  base::ElapsedTimer fetch_timer_;
-
-  std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
 };
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.cc b/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.cc
index c880129..623748ca 100644
--- a/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.cc
+++ b/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.cc
@@ -58,6 +58,7 @@
 
 void MockAffiliatedMatchHelper::InjectAffiliationAndBrandingInformation(
     std::vector<std::unique_ptr<PasswordForm>> forms,
+    AndroidAffiliationService::StrategyOnCacheMiss strategy_on_cache_miss,
     PasswordFormsCallback result_callback) {
   const std::vector<AffiliationAndBrandingInformation>& information =
       OnInjectAffiliationAndBrandingInformationCalled();
diff --git a/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.h b/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.h
index 57b9206..30879d4 100644
--- a/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.h
+++ b/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.h
@@ -65,6 +65,7 @@
 
   void InjectAffiliationAndBrandingInformation(
       std::vector<std::unique_ptr<PasswordForm>> forms,
+      AndroidAffiliationService::StrategyOnCacheMiss strategy_on_cache_miss,
       PasswordFormsCallback result_callback) override;
 
   DISALLOW_COPY_AND_ASSIGN(MockAffiliatedMatchHelper);
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index dea671a8..6304427 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -1213,7 +1213,8 @@
     LoginsResult forms) {
   if (affiliated_match_helper_) {
     affiliated_match_helper_->InjectAffiliationAndBrandingInformation(
-        std::move(forms), std::move(callback));
+        std::move(forms), AndroidAffiliationService::StrategyOnCacheMiss::FAIL,
+        std::move(callback));
   } else {
     std::move(callback).Run(std::move(forms));
   }
diff --git a/components/password_manager/core/browser/site_affiliation/affiliation_fetcher_base.cc b/components/password_manager/core/browser/site_affiliation/affiliation_fetcher_base.cc
new file mode 100644
index 0000000..d59083b
--- /dev/null
+++ b/components/password_manager/core/browser/site_affiliation/affiliation_fetcher_base.cc
@@ -0,0 +1,142 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/site_affiliation/affiliation_fetcher_base.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "components/password_manager/core/browser/android_affiliation/lookup_affiliation_response_parser.h"
+#include "net/base/load_flags.h"
+#include "net/base/url_util.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+namespace password_manager {
+
+// Enumeration listing the possible outcomes of fetching affiliation information
+// from the Affiliation API. This is used in UMA histograms, so do not change
+// existing values, only add new values at the end.
+enum class AffiliationFetchResult {
+  kSuccess = 0,
+  kFailure = 1,
+  kMalformed = 2,
+  kMaxValue = kMalformed,
+};
+
+namespace {
+
+void LogFetchResult(AffiliationFetchResult result) {
+  base::UmaHistogramEnumeration(
+      "PasswordManager.AffiliationFetcher.FetchResult", result);
+}
+
+}  // namespace
+
+affiliation_pb::LookupAffiliationMask CreateLookupMask(
+    const AffiliationFetcherInterface::RequestInfo& request_info) {
+  affiliation_pb::LookupAffiliationMask mask;
+
+  mask.set_branding_info(request_info.branding_info);
+  // Change password info requires grouping info enabled.
+  mask.set_grouping_info(request_info.change_password_info);
+  mask.set_change_password_info(request_info.change_password_info);
+
+  return mask;
+}
+
+AffiliationFetcherBase::AffiliationFetcherBase(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    AffiliationFetcherDelegate* delegate)
+    : url_loader_factory_(std::move(url_loader_factory)), delegate_(delegate) {}
+
+AffiliationFetcherBase::~AffiliationFetcherBase() = default;
+
+AffiliationFetcherDelegate* AffiliationFetcherBase::delegate() const {
+  return delegate_;
+}
+
+void AffiliationFetcherBase::FinalizeRequest(
+    const std::string& payload,
+    const GURL& query_url,
+    net::NetworkTrafficAnnotationTag traffic_annotation) {
+  fetch_timer_ = base::ElapsedTimer();
+
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = query_url;
+  resource_request->load_flags =
+      net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
+  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+  resource_request->method = "POST";
+
+  DCHECK(!simple_url_loader_);
+  simple_url_loader_ = network::SimpleURLLoader::Create(
+      std::move(resource_request), traffic_annotation);
+  simple_url_loader_->AttachStringForUpload(payload, "application/x-protobuf");
+  simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory_.get(),
+      base::BindOnce(&AffiliationFetcherBase::OnSimpleLoaderComplete,
+                     base::Unretained(this)));
+}
+
+bool AffiliationFetcherBase::ParseResponse(
+    const std::string& serialized_response,
+    AffiliationFetcherDelegate::Result* result) const {
+  // This function parses the response protocol buffer message for a list of
+  // equivalence classes, and stores them into |results| after performing some
+  // validation and sanitization steps to make sure that the contract of
+  // AffiliationFetcherDelegate is fulfilled. Possible discrepancies are:
+  //   * The server response will not have anything for facets that are not
+  //     affiliated with any other facet, while |result| must have them.
+  //   * The server response might contain future, unknown kinds of facet URIs,
+  //     while |result| must contain only those that are FacetURI::is_valid().
+  //   * The server response being ill-formed or self-inconsistent (in the sense
+  //     that there are overlapping equivalence classes) is indicative of server
+  //     side issues likely not remedied by re-fetching. Report failure in this
+  //     case so the caller can be notified and it can act accordingly.
+  //   * The |result| will be free of duplicate or empty equivalence classes.
+
+  affiliation_pb::LookupAffiliationResponse response;
+  if (!response.ParseFromString(serialized_response))
+    return false;
+
+  return ParseLookupAffiliationResponse(GetRequestedFacetURIs(), response,
+                                        result);
+}
+
+void AffiliationFetcherBase::OnSimpleLoaderComplete(
+    std::unique_ptr<std::string> response_body) {
+  base::UmaHistogramTimes("PasswordManager.AffiliationFetcher.FetchTime",
+                          fetch_timer_.Elapsed());
+  // Note that invoking the |delegate_| may destroy |this| synchronously, so the
+  // invocation must happen last.
+  auto result_data = std::make_unique<AffiliationFetcherDelegate::Result>();
+  if (response_body) {
+    if (ParseResponse(*response_body, result_data.get())) {
+      LogFetchResult(AffiliationFetchResult::kSuccess);
+      delegate_->OnFetchSucceeded(std::move(result_data));
+    } else {
+      LogFetchResult(AffiliationFetchResult::kMalformed);
+      delegate_->OnMalformedResponse();
+    }
+  } else {
+    LogFetchResult(AffiliationFetchResult::kFailure);
+    int response_code = -1;
+    if (simple_url_loader_->ResponseInfo() &&
+        simple_url_loader_->ResponseInfo()->headers) {
+      response_code =
+          simple_url_loader_->ResponseInfo()->headers->response_code();
+    }
+    base::UmaHistogramSparse(
+        "PasswordManager.AffiliationFetcher.FetchHttpResponseCode",
+        response_code);
+    // Network error codes are negative. See: src/net/base/net_error_list.h.
+    base::UmaHistogramSparse(
+        "PasswordManager.AffiliationFetcher.FetchErrorCode",
+        -simple_url_loader_->NetError());
+    delegate_->OnFetchFailed();
+  }
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/site_affiliation/affiliation_fetcher_base.h b/components/password_manager/core/browser/site_affiliation/affiliation_fetcher_base.h
new file mode 100644
index 0000000..ae0f177f
--- /dev/null
+++ b/components/password_manager/core/browser/site_affiliation/affiliation_fetcher_base.h
@@ -0,0 +1,85 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SITE_AFFILIATION_AFFILIATION_FETCHER_BASE_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SITE_AFFILIATION_AFFILIATION_FETCHER_BASE_H_
+
+#include <string>
+
+#include "components/password_manager/core/browser/android_affiliation/affiliation_fetcher_interface.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/timer/elapsed_timer.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_api.pb.h"
+
+namespace net {
+struct NetworkTrafficAnnotationTag;
+}
+
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+}  // namespace network
+
+namespace password_manager {
+
+// Creates lookup mask based on |request_info|.
+affiliation_pb::LookupAffiliationMask CreateLookupMask(
+    const AffiliationFetcherInterface::RequestInfo& request_info);
+
+// A base class for affiliation fetcher. Should not be used directly.
+//
+// Fetches authoritative information regarding which facets are affiliated with
+// each other, that is, which facets belong to the same logical application.
+// Apart from affiliations the service also supports groups and other details,
+// all of which have to be specified when starting a request.
+// See affiliation_utils.h for the definitions.
+//
+// An instance is good for exactly one fetch, and may be used from any thread
+// that runs a message loop (i.e. not a worker pool thread).
+class AffiliationFetcherBase : public virtual AffiliationFetcherInterface {
+ public:
+  ~AffiliationFetcherBase() override;
+
+  AffiliationFetcherDelegate* delegate() const;
+
+ protected:
+  AffiliationFetcherBase(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      AffiliationFetcherDelegate* delegate);
+
+  // Actually starts the request to retrieve affiliations and optionally
+  // groupings for each facet in |facet_uris| along with the details based on
+  // |request_info|. Calls the delegate with the results on the same thread when
+  // done. If |this| is destroyed before completion, the in-flight request is
+  // cancelled, and the delegate will not be called. Further details:
+  //   * No cookies are sent/saved with the request.
+  //   * In case of network/server errors, the request will not be retried.
+  //   * Results are guaranteed to be always fresh and will never be cached.
+  void FinalizeRequest(const std::string& payload,
+                       const GURL& query_url,
+                       net::NetworkTrafficAnnotationTag traffic_annotation);
+
+ private:
+  // Parses and validates the response protocol buffer message for a list of
+  // equivalence classes, stores them into |result| and returns true on success.
+  // It is guaranteed that every one of the requested Facet URIs will be a
+  // member of exactly one returned equivalence class.
+  // Returns false if the response was gravely ill-formed or self-inconsistent.
+  // Unknown kinds of facet URIs and new protocol buffer fields will be ignored.
+  bool ParseResponse(const std::string& serialized_response,
+                     AffiliationFetcherDelegate::Result* result) const;
+
+  void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
+
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  AffiliationFetcherDelegate* const delegate_;
+  base::ElapsedTimer fetch_timer_;
+
+  std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
+};
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SITE_AFFILIATION_AFFILIATION_FETCHER_BASE_H_
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 0e27eff..65f28da 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -8620,9 +8620,9 @@
       'id': 119,
       'caption': '''Report OS and firmware version''',
       'tags': ['admin-sharing'],
-      'desc': '''Report OS and firmware version of enrolled devices.
+      'desc': '''Setting the policy to Enabled or leaving it unset has enrolled devices periodically report their OS and firmware version.
 
-      If this setting is not set or set to True, enrolled devices will report the OS and firmware version periodically. If this setting is set to False, version info will not be reported.''',
+      Setting the policy to Disabled means enrolled devices don't report version info.''',
       'arc_support': 'This policy has no effect on the logging done by Android.',
     },
     {
@@ -8640,9 +8640,9 @@
       'id': 120,
       'caption': '''Report device activity times''',
       'tags': ['admin-sharing'],
-      'desc': '''Report device activity times.
+      'desc': '''Setting the policy to Enabled or leaving it unset has enrolled devices report time periods when a user is active on the device.
 
-      If this setting is not set or set to True, enrolled devices will report time periods when a user is active on the device. If this setting is set to False, device activity times will not be recorded or reported.''',
+      Setting the policy to Disabled means enrolled devices don't record or report activity times.''',
       'arc_support': 'This policy has no effect on the logging done by Android.',
     },
     {
@@ -8660,9 +8660,9 @@
       'id': 121,
       'caption': '''Report device boot mode''',
       'tags': ['admin-sharing'],
-      'desc': '''Report the state of the device's dev switch at boot.
+      'desc': '''Setting the policy to Enabled or leaving it unset has enrolled devices report the state of the device's dev switch when the machine booted.
 
-      If the policy is set to false, the state of the dev switch will not be reported.''',
+      Setting the policy to Disabled means enrolled devices don't report the state of the dev switch.''',
       'arc_support': 'This policy has no effect on the logging done by Android.',
     },
     {
@@ -8701,9 +8701,9 @@
       'id': 224,
       'caption': '''Report device network interfaces''',
       'tags': ['admin-sharing'],
-      'desc': '''Report list of network interfaces with their types and hardware addresses to the server.
+      'desc': '''Setting the policy to Enabled or leaving it unset has enrolled devices report the list of network interfaces with their types and hardware addresses.
 
-      If the policy is set to false, the interface list will not be reported.''',
+      Setting the policy to Disabled means enrolled devices don't report the network interface.''',
       'arc_support': 'This policy has no effect on the logging done by Android.',
     },
     {
@@ -8721,9 +8721,9 @@
       'id': 248,
       'caption': '''Report device users''',
       'tags': ['admin-sharing'],
-      'desc': '''Report list of device users that have recently logged in.
+      'desc': '''Setting the policy to Enabled or leaving it unset has enrolled devices report the list of device users that signed in recently.
 
-      If the policy is set to false, the users will not be reported.''',
+      Setting the policy to Disabled means enrolled devices don't report the list of users.''',
       'arc_support': 'This policy has no effect on the logging done by Android.',
     },
     {
@@ -8741,10 +8741,9 @@
       'id': 290,
       'caption': '''Report hardware status''',
       'tags': ['admin-sharing'],
-      'desc': '''Report hardware statistics such as CPU/RAM usage.
+      'desc': '''Setting the policy to Enabled or leaving it unset has enrolled devices report hardware statistics such as CPU/RAM usage.
 
-      If the policy is set to false, the statistics will not be reported.
-      If set to true or left unset, statistics will be reported.''',
+      Setting the policy to Disabled means enrolled devices don't report the hardware statistics.''',
       'arc_support': 'This policy has no effect on the logging done by Android.',
     },
     {
@@ -8762,12 +8761,9 @@
       'id': 291,
       'caption': '''Report information about active kiosk sessions''',
       'tags': ['admin-sharing'],
-      'desc': '''Report information about the active kiosk session, such as
-      application ID and version.
+      'desc': '''Setting the policy to Enabled or leaving it unset has enrolled devices report the active kiosk session information such as application ID and version.
 
-      If the policy is set to false, the kiosk session information will not be
-      reported. If set to true or left unset, kiosk session information will be
-      reported.''',
+      Setting the policy to Disabled means enrolled devices don't report kiosk session information.''',
       'arc_support': 'This policy has no effect on the logging done by Android.',
     },
     {
@@ -8853,10 +8849,9 @@
       'id': 512,
       'caption': '''Report power status''',
       'tags': ['admin-sharing'],
-      'desc': '''Report hardware statistics and identifiers related to power.
+      'desc': '''Setting the policy to Enabled has enrolled devices report hardware statistics and identifiers related to power.
 
-      If the policy is set to false or left unset, the statistics will not be reported.
-      If set to true, statistics will be reported.''',
+      Setting the policy to Disabled or leaving it unset means enrolled devices don't report power statistics.''',
       'arc_support': 'This policy has no effect on the logging done by Android.',
     },
     {
@@ -8874,10 +8869,9 @@
       'id': 513,
       'caption': '''Report storage status''',
       'tags': ['admin-sharing'],
-      'desc': '''Report hardware statistics and identifiers for storage devices.
+      'desc': '''Setting the policy to Enabled has enrolled devices report hardware statistics and identifiers for storage devices.
 
-      If the policy is set to false or left unset, the statistics will not be reported.
-      If set to true, statistics will be reported.''',
+      Setting the policy to Disabled or leaving it unset means enrolled devices don't report storage statistics.''',
       'arc_support': 'This policy has no effect on the logging done by Android.',
     },
     {
@@ -8895,10 +8889,9 @@
       'id': 514,
       'caption': '''Report board status''',
       'tags': ['admin-sharing'],
-      'desc': '''Report hardware statistics for SoC components.
+      'desc': '''Setting the policy to Enabled has enrolled devices report hardware statistics for SoC components.
 
-      If the policy is set to false or left unset, the statistics will not be reported.
-      If set to true, statistics will be reported.''',
+      Setting the policy to Disabled or leaving it unset means enrolled devices don't report the statistics.''',
       'arc_support': 'This policy has no effect on the logging done by Android.',
     },
     {
@@ -9107,10 +9100,9 @@
       'id': 292,
       'caption': '''Frequency of device status report uploads''',
       'tags': ['admin-sharing'],
-      'desc': '''How frequently device status uploads are sent, in milliseconds.
+      'desc': '''Setting the policy determines how frequently to send device status uploads, in milliseconds. The minimum allowed is 60 seconds.
 
-      If this policy is unset, the default frequency is 3 hours. The minimum
-      allowed frequency is 60 seconds.''',
+      If not set, the default interval of 3 hours applies.''',
       'arc_support': 'This policy has no effect on the logging done by Android.',
     },
     {
@@ -9128,13 +9120,9 @@
       'id': 349,
       'caption': '''Report information about status of Android''',
       'tags': ['admin-sharing'],
-      'desc': '''Information about the status of Android is sent back to the
-      server.
+      'desc': '''If Android apps are on, then setting the policy to True has enrolled devices report Android status information.
 
-      If the policy is set to false or left unset, no status information is reported.
-      If set to true, status information is reported.
-
-      This policy only applies if Android apps are enabled.''',
+      Setting the policy to Disabled or leaving it unset means enrolled devices don't report Android status information''',
     },
     {
       'name': 'ReportCrostiniUsageEnabled',
@@ -9174,11 +9162,9 @@
       'id': 293,
       'caption': '''Send network packets to the management server to monitor online status''',
       'tags': ['admin-sharing'],
-      'desc': '''Send network packets to the management server to monitor online status, to allow
-      the server to detect if the device is offline.
+      'desc': '''Setting the policy to Enabled sends monitoring network packets (<ph name="HEARTBEATS_TERM">heartbeats</ph>) to the management server to monitor online status, to allow the server to detect if the device is offline.
 
-      If this policy is set to true, monitoring network packets (so-called <ph name="HEARTBEATS_TERM">heartbeats</ph>) will be sent.
-      If set to false or unset, no packets will be sent.''',
+      Setting the policy to Disabled or leaving it unset sends no packets.''',
       'arc_support': 'This policy has no effect on the logging done by Android.',
     },
     {
@@ -9196,11 +9182,9 @@
       'id': 294,
       'caption': '''Frequency of monitoring network packets''',
       'tags': [],
-      'desc': '''How frequently monitoring network packets are sent, in milliseconds.
+      'desc': '''Setting the policy determines how frequently to send monitoring network packets, in milliseconds. Intervals range from 30 seconds to 24 hours. Values outside this range are clamped to this range.
 
-      If this policy is unset, the default interval is 3 minutes. The minimum
-      interval is 30 seconds and the maximum interval is 24 hours - values
-      outside of this range will be clamped to this range.''',
+      If not set, the default interval of 3 minutes applies.''',
       'arc_support': 'This policy has no effect on the logging done by Android.',
     },
     {
@@ -9218,11 +9202,9 @@
       'id': 306,
       'caption': '''Send system logs to the management server''',
       'tags': [],
-      'desc': '''Send system logs to the management server, to allow
-      admins to monitor system logs.
+      'desc': '''Setting the policy to Enabled sends system logs to the management server, to allow admins to monitor system logs.
 
-      If this policy is set to true, system logs will be sent. If set
-      to false or unset, then no system logs will be sent.''',
+      Setting the policy to Disabled or leaving it unset reports no system logs.''',
       'arc_support': 'This policy has no effect on the logging done by Android.',
     },
     {
@@ -10423,7 +10405,11 @@
       'owners': ['szym@chromium.org', 'pmarko@chromium.org'],
       'type': 'main',
       'schema': { 'type': 'boolean' },
-      'supported_on': ['chrome.*:25-'],
+      'supported_on': [
+        'android:73-',
+        'chrome.*:25-',
+        'chrome_os:73-',
+      ],
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
@@ -10436,6 +10422,8 @@
 
       This does not affect which DNS servers are used; just the software stack which is used to communicate with them. For example if the operating system is configured to use an enterprise DNS server, that same server would be used by the built-in DNS client. It is however possible that the built-in DNS client will address servers in different ways by using more modern DNS-related protocols such as DNS-over-TLS.
 
+      This policy has no effect on DNS-over-HTTPS. Please see the <ph name="DNS_OVER_HTTPS_MODE_POLICY_NAME">DnsOverHttpsMode</ph> policy to change that behavior.
+
       If this policy is set to true, the built-in DNS client will be used, if available.
 
       If this policy is set to false, the built-in DNS client will never be used.
@@ -10471,7 +10459,11 @@
           'caption': '''Enable DNS-over-HTTPS without insecure fallback''',
         }
       ],
-      'supported_on': ['chrome_os:78-', 'chrome.*:78-'],
+      'supported_on': [
+        'android:85-',
+        'chrome_os:78-',
+        'chrome.*:78-',
+      ],
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
@@ -10498,7 +10490,11 @@
       'owners': ['dalyk@chromium.org', 'ericorth@chromium.org', 'bingler@chromium.org'],
       'type': 'string',
       'schema': { 'type': 'string' },
-      'supported_on': ['chrome_os:80-', 'chrome.*:80-'],
+      'supported_on': [
+        'android:85-',
+        'chrome_os:80-',
+        'chrome.*:80-',
+      ],
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
diff --git a/components/viz/service/frame_sinks/video_detector.cc b/components/viz/service/frame_sinks/video_detector.cc
index cf618bf..fb54aca 100644
--- a/components/viz/service/frame_sinks/video_detector.cc
+++ b/components/viz/service/frame_sinks/video_detector.cc
@@ -4,13 +4,17 @@
 
 #include "components/viz/service/frame_sinks/video_detector.h"
 
+#include <memory>
+#include <utility>
+#include <vector>
+
 #include "base/time/time.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/service/surfaces/surface.h"
 #include "components/viz/service/surfaces/surface_manager.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_conversions.h"
 
 namespace viz {
 
@@ -38,8 +42,8 @@
     const CompositorFrame& frame = surface->GetActiveFrame();
 
     gfx::Rect damage =
-        gfx::ConvertRectToDIP(frame.device_scale_factor(),
-                              frame.render_pass_list.back()->damage_rect);
+        gfx::ScaleToEnclosingRect(frame.render_pass_list.back()->damage_rect,
+                                  1.f / frame.device_scale_factor());
 
     if (damage.width() < kMinDamageWidth || damage.height() < kMinDamageHeight)
       return false;
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index 1e12b06..904fd7d9 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -648,7 +648,8 @@
   DCHECK(main_runner_->BelongsToCurrentThread());
   mojo::MakeSelfOwnedReceiver(
       std::make_unique<arc::GpuArcVideoDecodeAccelerator>(
-          gpu_preferences_, protected_buffer_manager_),
+          gpu_preferences_, gpu_channel_manager_->gpu_driver_bug_workarounds(),
+          protected_buffer_manager_),
       std::move(vda_receiver));
 }
 
@@ -656,7 +657,8 @@
     mojo::PendingReceiver<arc::mojom::VideoEncodeAccelerator> vea_receiver) {
   DCHECK(main_runner_->BelongsToCurrentThread());
   mojo::MakeSelfOwnedReceiver(
-      std::make_unique<arc::GpuArcVideoEncodeAccelerator>(gpu_preferences_),
+      std::make_unique<arc::GpuArcVideoEncodeAccelerator>(
+          gpu_preferences_, gpu_channel_manager_->gpu_driver_bug_workarounds()),
       std::move(vea_receiver));
 }
 
@@ -704,14 +706,10 @@
     mojo::PendingReceiver<media::mojom::VideoEncodeAcceleratorProvider>
         vea_provider_receiver) {
   DCHECK(io_runner_->BelongsToCurrentThread());
-
-  gpu::GpuDriverBugWorkarounds gpu_workarounds(
-      gpu_feature_info_.enabled_gpu_driver_bug_workarounds);
-
   media::MojoVideoEncodeAcceleratorProvider::Create(
       std::move(vea_provider_receiver),
       base::BindRepeating(&media::GpuVideoEncodeAcceleratorFactory::CreateVEA),
-      gpu_preferences_, gpu_workarounds);
+      gpu_preferences_, gpu_channel_manager_->gpu_driver_bug_workarounds());
 }
 
 void GpuServiceImpl::CreateGpuMemoryBuffer(
diff --git a/content/browser/appcache/appcache_request_handler_unittest.cc b/content/browser/appcache/appcache_request_handler_unittest.cc
index d4e8e4b..2bf55da 100644
--- a/content/browser/appcache/appcache_request_handler_unittest.cc
+++ b/content/browser/appcache/appcache_request_handler_unittest.cc
@@ -691,7 +691,7 @@
   void OnBadMessage(const std::string& reason) { NOTREACHED(); }
 
   MockAppCacheStorage* mock_storage() {
-    return reinterpret_cast<MockAppCacheStorage*>(mock_service_->storage());
+    return static_cast<MockAppCacheStorage*>(mock_service_->storage());
   }
 
   void CreateRequestAndHandler(const GURL& url,
diff --git a/content/browser/appcache/appcache_update_job_unittest.cc b/content/browser/appcache/appcache_update_job_unittest.cc
index 10c2778..ed6ac5c 100644
--- a/content/browser/appcache/appcache_update_job_unittest.cc
+++ b/content/browser/appcache/appcache_update_job_unittest.cc
@@ -2463,7 +2463,7 @@
   void FailStoreNewestCacheTest() {
     MakeService();
     MockAppCacheStorage* storage =
-        reinterpret_cast<MockAppCacheStorage*>(service_->storage());
+        static_cast<MockAppCacheStorage*>(service_->storage());
     storage->SimulateStoreGroupAndNewestCacheFailure();
 
     group_ = base::MakeRefCounted<AppCacheGroup>(
@@ -2490,7 +2490,7 @@
   void UpgradeFailStoreNewestCacheTest() {
     MakeService();
     MockAppCacheStorage* storage =
-        reinterpret_cast<MockAppCacheStorage*>(service_->storage());
+        static_cast<MockAppCacheStorage*>(service_->storage());
     storage->SimulateStoreGroupAndNewestCacheFailure();
 
     group_ = base::MakeRefCounted<AppCacheGroup>(
@@ -2543,7 +2543,7 @@
   void MasterEntryFailStoreNewestCacheTest() {
     MakeService();
     MockAppCacheStorage* storage =
-        reinterpret_cast<MockAppCacheStorage*>(service_->storage());
+        static_cast<MockAppCacheStorage*>(service_->storage());
     storage->SimulateStoreGroupAndNewestCacheFailure();
 
     const GURL kManifestUrl = MockHttpServer::GetMockUrl("files/notmodified");
@@ -2589,7 +2589,7 @@
   void UpgradeFailMakeGroupObsoleteTest() {
     MakeService();
     MockAppCacheStorage* storage =
-        reinterpret_cast<MockAppCacheStorage*>(service_->storage());
+        static_cast<MockAppCacheStorage*>(service_->storage());
     storage->SimulateMakeGroupObsoleteFailure();
 
     group_ = base::MakeRefCounted<AppCacheGroup>(
@@ -4528,7 +4528,7 @@
                 !group_->first_evictable_error_time().is_null());
       if (expect_evictable_error_) {
         MockAppCacheStorage* storage =
-            reinterpret_cast<MockAppCacheStorage*>(service_->storage());
+            static_cast<MockAppCacheStorage*>(service_->storage());
         EXPECT_EQ(group_->first_evictable_error_time(),
                   storage->stored_eviction_times_[group_->group_id()].second);
       }
@@ -4563,7 +4563,7 @@
         // is unknown to the test). Check group and newest cache were stored
         // when update succeeds.
         MockAppCacheStorage* storage =
-            reinterpret_cast<MockAppCacheStorage*>(service_->storage());
+            static_cast<MockAppCacheStorage*>(service_->storage());
         EXPECT_TRUE(storage->IsGroupStored(group_.get()));
         EXPECT_TRUE(storage->IsCacheStored(group_->newest_complete_cache()));
 
diff --git a/content/browser/appcache/mock_appcache_storage_unittest.cc b/content/browser/appcache/mock_appcache_storage_unittest.cc
index b7938ac..8214b946 100644
--- a/content/browser/appcache/mock_appcache_storage_unittest.cc
+++ b/content/browser/appcache/mock_appcache_storage_unittest.cc
@@ -123,7 +123,7 @@
   // Should complete asyncly.
   MockAppCacheService service;
   MockAppCacheStorage* storage =
-      reinterpret_cast<MockAppCacheStorage*>(service.storage());
+      static_cast<MockAppCacheStorage*>(service.storage());
   MockStorageDelegate delegate;
   GURL manifest_url("http://blah/");
   service.storage()->LoadOrCreateGroup(manifest_url, &delegate);
@@ -169,7 +169,7 @@
   // load should complete asyncly.
   MockAppCacheService service;
   MockAppCacheStorage* storage =
-      reinterpret_cast<MockAppCacheStorage*>(service.storage());
+      static_cast<MockAppCacheStorage*>(service.storage());
 
   // Setup some preconditions. Create a group and newest cache that
   // appears to be "stored" and "not currently in use".
@@ -220,7 +220,7 @@
   // Store a group and its newest cache. Should complete asyncly.
   MockAppCacheService service;
   MockAppCacheStorage* storage =
-      reinterpret_cast<MockAppCacheStorage*>(service.storage());
+      static_cast<MockAppCacheStorage*>(service.storage());
 
   // Setup some preconditions. Create a group and newest cache that
   // appears to be "unstored".
@@ -252,7 +252,7 @@
   // Store a group and its newest cache. Should complete asyncly.
   MockAppCacheService service;
   MockAppCacheStorage* storage =
-      reinterpret_cast<MockAppCacheStorage*>(service.storage());
+      static_cast<MockAppCacheStorage*>(service.storage());
 
   // Setup some preconditions. Create a group and old complete cache
   // that appear to be "stored", and a newest unstored complete cache.
@@ -297,7 +297,7 @@
   // Store a group with updates to its existing newest complete cache.
   MockAppCacheService service;
   MockAppCacheStorage* storage =
-      reinterpret_cast<MockAppCacheStorage*>(service.storage());
+      static_cast<MockAppCacheStorage*>(service.storage());
 
   // Setup some preconditions. Create a group and a complete cache that
   // appear to be "stored".
@@ -339,7 +339,7 @@
   // Make a group obsolete, should complete asyncly.
   MockAppCacheService service;
   MockAppCacheStorage* storage =
-      reinterpret_cast<MockAppCacheStorage*>(service.storage());
+      static_cast<MockAppCacheStorage*>(service.storage());
 
   // Setup some preconditions. Create a group and newest cache that
   // appears to be "stored" and "currently in use".
@@ -384,7 +384,7 @@
   // Should complete syncly.
   MockAppCacheService service;
   MockAppCacheStorage* storage =
-      reinterpret_cast<MockAppCacheStorage*>(service.storage());
+      static_cast<MockAppCacheStorage*>(service.storage());
 
   // Setup some preconditions. Create a cache with an entry.
   GURL entry_url("http://blah/entry");
@@ -404,7 +404,7 @@
   // Should complete asyncly.
   MockAppCacheService service;
   MockAppCacheStorage* storage =
-      reinterpret_cast<MockAppCacheStorage*>(service.storage());
+      static_cast<MockAppCacheStorage*>(service.storage());
 
   // Conduct the test.
   MockStorageDelegate delegate;
@@ -429,7 +429,7 @@
   // Should complete asyncly.
   MockAppCacheService service;
   MockAppCacheStorage* storage =
-      reinterpret_cast<MockAppCacheStorage*>(service.storage());
+      static_cast<MockAppCacheStorage*>(service.storage());
 
   // Setup some preconditions. Create a complete cache with an entry.
   const int64_t kCacheId = storage->NewCacheId();
@@ -464,7 +464,7 @@
   // Should complete asyncly.
   MockAppCacheService service;
   MockAppCacheStorage* storage =
-      reinterpret_cast<MockAppCacheStorage*>(service.storage());
+      static_cast<MockAppCacheStorage*>(service.storage());
 
   // Setup some preconditions. Create a complete cache with a
   // fallback namespace and entry.
@@ -520,7 +520,7 @@
   // Should complete asyncly.
   MockAppCacheService service;
   MockAppCacheStorage* storage =
-      reinterpret_cast<MockAppCacheStorage*>(service.storage());
+      static_cast<MockAppCacheStorage*>(service.storage());
 
   // Setup some preconditions. Create 2 complete caches with an entry
   // for the same url.
@@ -577,7 +577,7 @@
   // Should complete asyncly.
   MockAppCacheService service;
   MockAppCacheStorage* storage =
-      reinterpret_cast<MockAppCacheStorage*>(service.storage());
+      static_cast<MockAppCacheStorage*>(service.storage());
 
   // Setup some preconditions. Create a complete cache with a
   // foreign entry and an online namespace.
diff --git a/content/browser/loader/navigation_url_loader_impl_unittest.cc b/content/browser/loader/navigation_url_loader_impl_unittest.cc
index e0cb41128..0393112 100644
--- a/content/browser/loader/navigation_url_loader_impl_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_impl_unittest.cc
@@ -216,7 +216,8 @@
             nullptr /* blob_url_loader_factory */,
             base::UnguessableToken::Create() /* devtools_navigation_token */,
             base::UnguessableToken::Create() /* devtools_frame_token */,
-            false /* obey_origin_policy */, {} /* cors_exempt_headers */));
+            false /* obey_origin_policy */, {} /* cors_exempt_headers */,
+            nullptr /* client_security_state */));
     std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors;
     most_recent_resource_request_ = base::nullopt;
     interceptors.push_back(std::make_unique<TestNavigationLoaderInterceptor>(
diff --git a/content/browser/loader/navigation_url_loader_unittest.cc b/content/browser/loader/navigation_url_loader_unittest.cc
index 387f55cf..1d01369f 100644
--- a/content/browser/loader/navigation_url_loader_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_unittest.cc
@@ -90,7 +90,8 @@
             nullptr /* blob_url_loader_factory */,
             base::UnguessableToken::Create() /* devtools_navigation_token */,
             base::UnguessableToken::Create() /* devtools_frame_token */,
-            false /* obey_origin_policy */, {} /* cors_exempt_headers */));
+            false /* obey_origin_policy */, {} /* cors_exempt_headers */,
+            nullptr /* client_security_state */));
     return NavigationURLLoader::Create(
         browser_context_.get(),
         BrowserContext::GetDefaultStoragePartition(browser_context_.get()),
diff --git a/content/browser/renderer_host/input/mouse_wheel_event_queue_unittest.cc b/content/browser/renderer_host/input/mouse_wheel_event_queue_unittest.cc
index f69601a..38a22cb5 100644
--- a/content/browser/renderer_host/input/mouse_wheel_event_queue_unittest.cc
+++ b/content/browser/renderer_host/input/mouse_wheel_event_queue_unittest.cc
@@ -127,7 +127,7 @@
   EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum, \
             event->data.scroll_end.inertial_phase);
 
-#if defined(CHROME_OS)
+#if defined(OS_CHROMEOS)
 #define EXPECT_SYNTHETIC_INERTIAL_GESTURE_SCROLL_END(event) \
   EXPECT_GESTURE_SCROLL_END_IMPL(event);                    \
   EXPECT_TRUE(event->data.scroll_end.synthetic);            \
@@ -459,7 +459,7 @@
 // Tests that a Wheel event with synthetic momentumn_phase == PhaseEnded that is
 // generated by the fling controller properly populates
 // scroll_end.data.scroll_end.generated_by_fling_controller.
-#if defined(CHROME_OS)
+#if defined(OS_CHROMEOS)
 TEST_F(MouseWheelEventQueueTest, WheelEndWithMomentumPhaseEndedInformation) {
   const ui::ScrollGranularity scroll_units =
       ui::ScrollGranularity::kScrollByPrecisePixel;
@@ -488,7 +488,7 @@
   EXPECT_SYNTHETIC_INERTIAL_GESTURE_SCROLL_END(sent_gesture_event(0));
   EXPECT_EQ(1U, GetAndResetSentEventCount());
 }
-#endif  // defined(CHROME_OS)
+#endif  // defined(OS_CHROMEOS)
 
 TEST_F(MouseWheelEventQueueTest, GestureSendingInterrupted) {
   const ui::ScrollGranularity scroll_units =
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index fd526dc..136d721c 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -2806,7 +2806,7 @@
                                    : nullptr,
           devtools_navigation_token(), frame_tree_node_->devtools_frame_token(),
           OriginPolicyThrottle::ShouldRequestOriginPolicy(common_params_->url),
-          std::move(cors_exempt_headers)),
+          std::move(cors_exempt_headers), nullptr /* client_security_state */),
       std::move(navigation_ui_data), service_worker_handle_.get(),
       appcache_handle_.get(), std::move(prefetched_signed_exchange_cache_),
       this, IsServedFromBackForwardCache(), CreateCookieAccessObserver(),
diff --git a/content/browser/renderer_host/navigation_request_info.cc b/content/browser/renderer_host/navigation_request_info.cc
index fa3b75b..45ac252a 100644
--- a/content/browser/renderer_host/navigation_request_info.cc
+++ b/content/browser/renderer_host/navigation_request_info.cc
@@ -23,7 +23,8 @@
     const base::UnguessableToken& devtools_navigation_token,
     const base::UnguessableToken& devtools_frame_token,
     bool obey_origin_policy,
-    net::HttpRequestHeaders cors_exempt_headers)
+    net::HttpRequestHeaders cors_exempt_headers,
+    network::mojom::ClientSecurityStatePtr client_security_state)
     : common_params(std::move(common_params)),
       begin_params(std::move(begin_params)),
       isolation_info(isolation_info),
@@ -39,7 +40,8 @@
       devtools_navigation_token(devtools_navigation_token),
       devtools_frame_token(devtools_frame_token),
       obey_origin_policy(obey_origin_policy),
-      cors_exempt_headers(std::move(cors_exempt_headers)) {}
+      cors_exempt_headers(std::move(cors_exempt_headers)),
+      client_security_state(std::move(client_security_state)) {}
 
 NavigationRequestInfo::~NavigationRequestInfo() {}
 
diff --git a/content/browser/renderer_host/navigation_request_info.h b/content/browser/renderer_host/navigation_request_info.h
index e406903..ea285de 100644
--- a/content/browser/renderer_host/navigation_request_info.h
+++ b/content/browser/renderer_host/navigation_request_info.h
@@ -16,6 +16,7 @@
 #include "net/base/isolation_info.h"
 #include "net/http/http_request_headers.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/mojom/client_security_state.mojom-forward.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -25,23 +26,25 @@
 // ResourceDispatcherHost. It is initialized on the UI thread, and then passed
 // to the IO thread by a NavigationRequest object.
 struct CONTENT_EXPORT NavigationRequestInfo {
-  NavigationRequestInfo(mojom::CommonNavigationParamsPtr common_params,
-                        mojom::BeginNavigationParamsPtr begin_params,
-                        const net::IsolationInfo& isolation_info,
-                        bool is_main_frame,
-                        bool parent_is_main_frame,
-                        bool are_ancestors_secure,
-                        int frame_tree_node_id,
-                        bool is_for_guests_only,
-                        bool report_raw_headers,
-                        bool is_prerendering,
-                        bool upgrade_if_insecure,
-                        std::unique_ptr<network::PendingSharedURLLoaderFactory>
-                            blob_url_loader_factory,
-                        const base::UnguessableToken& devtools_navigation_token,
-                        const base::UnguessableToken& devtools_frame_token,
-                        bool obey_origin_policy,
-                        net::HttpRequestHeaders cors_exempt_headers);
+  NavigationRequestInfo(
+      mojom::CommonNavigationParamsPtr common_params,
+      mojom::BeginNavigationParamsPtr begin_params,
+      const net::IsolationInfo& isolation_info,
+      bool is_main_frame,
+      bool parent_is_main_frame,
+      bool are_ancestors_secure,
+      int frame_tree_node_id,
+      bool is_for_guests_only,
+      bool report_raw_headers,
+      bool is_prerendering,
+      bool upgrade_if_insecure,
+      std::unique_ptr<network::PendingSharedURLLoaderFactory>
+          blob_url_loader_factory,
+      const base::UnguessableToken& devtools_navigation_token,
+      const base::UnguessableToken& devtools_frame_token,
+      bool obey_origin_policy,
+      net::HttpRequestHeaders cors_exempt_headers,
+      network::mojom::ClientSecurityStatePtr client_security_state);
   NavigationRequestInfo(const NavigationRequestInfo& other) = delete;
   ~NavigationRequestInfo();
 
@@ -89,6 +92,10 @@
   const bool obey_origin_policy;
 
   const net::HttpRequestHeaders cors_exempt_headers;
+
+  // Specifies the security state applying to the navigation. For iframes, this
+  // is the security state of their parent. Nullptr otherwise.
+  const network::mojom::ClientSecurityStatePtr client_security_state;
 };
 
 }  // namespace content
diff --git a/content/browser/sms/sms_service_unittest.cc b/content/browser/sms/sms_service_unittest.cc
index fb3735a..32447b1 100644
--- a/content/browser/sms/sms_service_unittest.cc
+++ b/content/browser/sms/sms_service_unittest.cc
@@ -318,7 +318,7 @@
 
   NiceMock<MockSmsWebContentsDelegate> delegate;
   WebContentsImpl* web_contents_impl =
-      reinterpret_cast<WebContentsImpl*>(web_contents());
+      static_cast<WebContentsImpl*>(web_contents());
   web_contents_impl->SetDelegate(&delegate);
 
   NiceMock<MockSmsProvider> provider;
@@ -404,7 +404,7 @@
   NavigateAndCommit(GURL(kTestUrl));
   NiceMock<MockSmsWebContentsDelegate> delegate;
   WebContentsImpl* web_contents_impl =
-      reinterpret_cast<WebContentsImpl*>(web_contents());
+      static_cast<WebContentsImpl*>(web_contents());
   web_contents_impl->SetDelegate(&delegate);
 
   NiceMock<MockSmsProvider> provider;
@@ -442,7 +442,7 @@
   NavigateAndCommit(GURL(kTestUrl));
   NiceMock<MockSmsWebContentsDelegate> delegate;
   WebContentsImpl* web_contents_impl =
-      reinterpret_cast<WebContentsImpl*>(web_contents());
+      static_cast<WebContentsImpl*>(web_contents());
   web_contents_impl->SetDelegate(&delegate);
 
   NiceMock<MockSmsProvider> provider;
@@ -762,7 +762,7 @@
 
   NiceMock<MockSmsWebContentsDelegate> delegate;
   WebContentsImpl* web_contents_impl =
-      reinterpret_cast<WebContentsImpl*>(web_contents());
+      static_cast<WebContentsImpl*>(web_contents());
   web_contents_impl->SetDelegate(&delegate);
 
   NiceMock<MockSmsProvider> provider;
diff --git a/content/browser/sms/user_consent_handler_unittest.cc b/content/browser/sms/user_consent_handler_unittest.cc
index e2f9682c..a3c0b6fb 100644
--- a/content/browser/sms/user_consent_handler_unittest.cc
+++ b/content/browser/sms/user_consent_handler_unittest.cc
@@ -34,7 +34,7 @@
   void SetUp() override {
     RenderViewHostTestHarness::SetUp();
     WebContentsImpl* web_contents_impl =
-        reinterpret_cast<WebContentsImpl*>(web_contents());
+        static_cast<WebContentsImpl*>(web_contents());
     web_contents_impl->SetDelegate(&delegate_);
   }
 
@@ -136,7 +136,7 @@
       web_contents()->GetMainFrame()->GetLastCommittedOrigin();
 
   WebContentsImpl* web_contents_impl =
-      reinterpret_cast<WebContentsImpl*>(web_contents());
+      static_cast<WebContentsImpl*>(web_contents());
   web_contents_impl->SetDelegate(nullptr);
 
   ExpectNoSmsPrompt();
@@ -150,4 +150,4 @@
 }
 
 }  // namespace
-}  // namespace content
\ No newline at end of file
+}  // namespace content
diff --git a/content/browser/web_contents/web_contents_view_mac.mm b/content/browser/web_contents/web_contents_view_mac.mm
index 6b8ddc25..c5521a2 100644
--- a/content/browser/web_contents/web_contents_view_mac.mm
+++ b/content/browser/web_contents/web_contents_view_mac.mm
@@ -428,7 +428,7 @@
   std::list<RenderWidgetHostViewMac*> result;
   for (auto iter = child_views_.begin(); iter != child_views_.end();) {
     if (*iter) {
-      result.push_back(reinterpret_cast<RenderWidgetHostViewMac*>(iter->get()));
+      result.push_back(static_cast<RenderWidgetHostViewMac*>(iter->get()));
       iter++;
     } else {
       iter = child_views_.erase(iter);
diff --git a/content/gpu/browser_exposed_gpu_interfaces.cc b/content/gpu/browser_exposed_gpu_interfaces.cc
index 45a9935..480c22e 100644
--- a/content/gpu/browser_exposed_gpu_interfaces.cc
+++ b/content/gpu/browser_exposed_gpu_interfaces.cc
@@ -15,11 +15,13 @@
 
 namespace content {
 
-void ExposeGpuInterfacesToBrowser(const gpu::GpuPreferences& gpu_preferences,
-                                  mojo::BinderMap* binders) {
+void ExposeGpuInterfacesToBrowser(
+    const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
+    mojo::BinderMap* binders) {
   if (GetContentClient()->gpu()) {  // May be null in tests.
-    GetContentClient()->gpu()->ExposeInterfacesToBrowser(gpu_preferences,
-                                                         binders);
+    GetContentClient()->gpu()->ExposeInterfacesToBrowser(
+        gpu_preferences, gpu_workarounds, binders);
   }
 
 #if defined(USE_OZONE)
diff --git a/content/gpu/browser_exposed_gpu_interfaces.h b/content/gpu/browser_exposed_gpu_interfaces.h
index ff3dcc3..1fdf5b0 100644
--- a/content/gpu/browser_exposed_gpu_interfaces.h
+++ b/content/gpu/browser_exposed_gpu_interfaces.h
@@ -7,6 +7,7 @@
 
 namespace gpu {
 struct GpuPreferences;
+class GpuDriverBugWorkarounds;
 }
 
 namespace mojo {
@@ -22,8 +23,10 @@
 // Embedder-specific GPU interfaces can be exposed to the browser via
 // |ContentGpuClient::ExposeInterfacesToBrowser()| or embedder-specific helper
 // functions.
-void ExposeGpuInterfacesToBrowser(const gpu::GpuPreferences& gpu_preferences,
-                                  mojo::BinderMap* binders);
+void ExposeGpuInterfacesToBrowser(
+    const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
+    mojo::BinderMap* binders);
 
 }  // namespace content
 
diff --git a/content/gpu/gpu_child_thread.cc b/content/gpu/gpu_child_thread.cc
index 571b3b14..65b7957 100644
--- a/content/gpu/gpu_child_thread.cc
+++ b/content/gpu/gpu_child_thread.cc
@@ -210,8 +210,10 @@
   // |ExposeGpuInterfacesToBrowser()| in browser_exposed_gpu_interfaces.cc, as
   // that will ensure security review coverage.
   mojo::BinderMap binders;
-  content::ExposeGpuInterfacesToBrowser(gpu_service->gpu_preferences(),
-                                        &binders);
+  content::ExposeGpuInterfacesToBrowser(
+      gpu_service->gpu_preferences(),
+      gpu_service->gpu_channel_manager()->gpu_driver_bug_workarounds(),
+      &binders);
   ExposeInterfacesToBrowser(std::move(binders));
 }
 
diff --git a/content/public/gpu/content_gpu_client.h b/content/public/gpu/content_gpu_client.h
index 88e80bda..2231ce9 100644
--- a/content/public/gpu/content_gpu_client.h
+++ b/content/public/gpu/content_gpu_client.h
@@ -15,6 +15,7 @@
 
 namespace gpu {
 struct GpuPreferences;
+class GpuDriverBugWorkarounds;
 class SharedImageManager;
 class SyncPointManager;
 }
@@ -38,6 +39,7 @@
   // has received a |CreateGpuService()| call from the browser.
   virtual void ExposeInterfacesToBrowser(
       const gpu::GpuPreferences& gpu_preferences,
+      const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
       mojo::BinderMap* binders) {}
 
   // Called right after the IO/compositor thread is created.
diff --git a/content/renderer/media/audio/mock_audio_device_factory.cc b/content/renderer/media/audio/mock_audio_device_factory.cc
deleted file mode 100644
index 12a7c3b..0000000
--- a/content/renderer/media/audio/mock_audio_device_factory.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/renderer/media/audio/mock_audio_device_factory.h"
-
-namespace content {
-
-MockCapturerSource::MockCapturerSource() {}
-
-MockCapturerSource::~MockCapturerSource() {}
-
-void MockCapturerSource::SetVolume(double volume) {}
-
-void MockCapturerSource::SetOutputDeviceForAec(
-    const std::string& output_device_id) {}
-
-MockAudioDeviceFactory::MockAudioDeviceFactory()
-    : mock_capturer_source_(new MockCapturerSource()),
-      did_create_once_(false) {}
-
-MockAudioDeviceFactory::~MockAudioDeviceFactory() {}
-
-scoped_refptr<media::AudioCapturerSource>
-MockAudioDeviceFactory::CreateAudioCapturerSource(
-    const blink::LocalFrameToken& frame_token,
-    const media::AudioSourceParameters& params) {
-  CHECK(!did_create_once_);
-  did_create_once_ = true;
-  return scoped_refptr<media::AudioCapturerSource>(mock_capturer_source_);
-}
-
-}  // namespace content
diff --git a/content/renderer/media/audio/mock_audio_device_factory.h b/content/renderer/media/audio/mock_audio_device_factory.h
deleted file mode 100644
index 6836598..0000000
--- a/content/renderer/media/audio/mock_audio_device_factory.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_RENDERER_MEDIA_AUDIO_MOCK_AUDIO_DEVICE_FACTORY_H_
-#define CONTENT_RENDERER_MEDIA_AUDIO_MOCK_AUDIO_DEVICE_FACTORY_H_
-
-#include <string>
-
-#include "media/base/audio_capturer_source.h"
-#include "media/base/audio_renderer_sink.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "third_party/blink/public/common/tokens/tokens.h"
-#include "third_party/blink/public/web/modules/media/audio/web_audio_device_factory.h"
-
-namespace content {
-
-// MockAudioDeviceFactory creates an instance of this.
-class MockCapturerSource : public media::AudioCapturerSource {
- public:
-  MockCapturerSource();
-  MOCK_METHOD2(Initialize,
-               void(const media::AudioParameters& params,
-                    CaptureCallback* callback));
-  MOCK_METHOD0(Start, void());
-  MOCK_METHOD0(Stop, void());
-  MOCK_METHOD1(SetAutomaticGainControl, void(bool enable));
-  void SetVolume(double volume) override;
-  void SetOutputDeviceForAec(const std::string& output_device_id) override;
-
- protected:
-  ~MockCapturerSource() override;
-};
-
-// Creates one MockCapturerSource instance for unit testing. This replaces the
-// need for unit tests to open a real platform audio output. Instantiating this
-// class sets the global content::AudioDeviceFactory implementation to |this|.
-class MockAudioDeviceFactory : public blink::WebAudioDeviceFactory {
- public:
-  MockAudioDeviceFactory();
-  ~MockAudioDeviceFactory() override;
-
-  // Returns the MockCapturerSource created by this factory.
-  const scoped_refptr<MockCapturerSource>& mock_capturer_source() const {
-    return mock_capturer_source_;
-  }
-
-  // These methods are just mocked because tests currently don't need them to be
-  // implemented.
-  MOCK_METHOD3(CreateFinalAudioRendererSink,
-               scoped_refptr<media::AudioRendererSink>(
-                   const blink::LocalFrameToken& frame_token,
-                   const media::AudioSinkParameters& params,
-                   base::TimeDelta auth_timeout));
-  MOCK_METHOD3(CreateAudioRendererSink,
-               scoped_refptr<media::AudioRendererSink>(
-                   blink::WebAudioDeviceSourceType source_type,
-                   const blink::LocalFrameToken& frame_token,
-                   const media::AudioSinkParameters& params));
-  MOCK_METHOD3(CreateSwitchableAudioRendererSink,
-               scoped_refptr<media::SwitchableAudioRendererSink>(
-                   blink::WebAudioDeviceSourceType source_type,
-                   const blink::LocalFrameToken& frame_token,
-                   const media::AudioSinkParameters& params));
-
-  // Returns mock_capturer_source_ once. If called a second time, the process
-  // will crash.
-  scoped_refptr<media::AudioCapturerSource> CreateAudioCapturerSource(
-      const blink::LocalFrameToken& frame_token,
-      const media::AudioSourceParameters& params) override;
-
- private:
-  scoped_refptr<MockCapturerSource> mock_capturer_source_;
-  bool did_create_once_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockAudioDeviceFactory);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_MEDIA_AUDIO_MOCK_AUDIO_DEVICE_FACTORY_H_
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index a427ce5..5aa43f63 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2388,7 +2388,8 @@
   // so its routing id is registered for receiving IPC messages.
   CHECK_NE(proxy_routing_id, MSG_ROUTING_NONE);
   RenderFrameProxy* proxy = RenderFrameProxy::CreateProxyToReplaceFrame(
-      this, proxy_routing_id, replicated_frame_state.scope, frame_token);
+      agent_scheduling_group_, this, proxy_routing_id,
+      replicated_frame_state.scope, frame_token);
 
   RenderViewImpl* render_view = render_view_;
   bool is_main_frame = is_main_frame_;
@@ -4090,8 +4091,8 @@
                                &initial_replicated_state, &portal_token,
                                &frame_token, &devtools_frame_token);
   RenderFrameProxy* proxy = RenderFrameProxy::CreateProxyForPortal(
-      this, proxy_routing_id, frame_token, devtools_frame_token,
-      portal_element);
+      agent_scheduling_group_, this, proxy_routing_id, frame_token,
+      devtools_frame_token, portal_element);
   proxy->SetReplicatedState(initial_replicated_state);
   return std::make_pair(proxy->web_frame(), portal_token);
 }
@@ -4108,8 +4109,8 @@
                               &replicated_state, &frame_token,
                               &devtools_frame_token);
   RenderFrameProxy* proxy = RenderFrameProxy::CreateProxyForPortal(
-      this, proxy_routing_id, frame_token, devtools_frame_token,
-      portal_element);
+      agent_scheduling_group_, this, proxy_routing_id, frame_token,
+      devtools_frame_token, portal_element);
   proxy->FrameSinkIdChanged(frame_sink_id);
   proxy->SetReplicatedState(replicated_state);
   return proxy->web_frame();
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 67b19bf..cfa90e5 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -24,6 +24,7 @@
 #include "content/public/common/impression.h"
 #include "content/public/common/use_zoom_for_dsf_policy.h"
 #include "content/public/renderer/content_renderer_client.h"
+#include "content/renderer/agent_scheduling_group.h"
 #include "content/renderer/child_frame_compositing_helper.h"
 #include "content/renderer/impression_conversions.h"
 #include "content/renderer/loader/web_url_request_util.h"
@@ -65,13 +66,15 @@
 
 // static
 RenderFrameProxy* RenderFrameProxy::CreateProxyToReplaceFrame(
+    AgentSchedulingGroup& agent_scheduling_group,
     RenderFrameImpl* frame_to_replace,
     int routing_id,
     blink::mojom::TreeScopeType scope,
     const base::UnguessableToken& proxy_frame_token) {
   CHECK_NE(routing_id, MSG_ROUTING_NONE);
 
-  std::unique_ptr<RenderFrameProxy> proxy(new RenderFrameProxy(routing_id));
+  std::unique_ptr<RenderFrameProxy> proxy(
+      new RenderFrameProxy(agent_scheduling_group, routing_id));
   proxy->devtools_frame_token_ = frame_to_replace->GetDevToolsFrameToken();
 
   // When a RenderFrame is replaced by a RenderProxy, the WebRemoteFrame should
@@ -109,6 +112,7 @@
 
 // static
 RenderFrameProxy* RenderFrameProxy::CreateFrameProxy(
+    AgentSchedulingGroup& agent_scheduling_group,
     int routing_id,
     int render_view_routing_id,
     const base::Optional<base::UnguessableToken>& opener_frame_token,
@@ -126,7 +130,8 @@
       return nullptr;
   }
 
-  std::unique_ptr<RenderFrameProxy> proxy(new RenderFrameProxy(routing_id));
+  std::unique_ptr<RenderFrameProxy> proxy(
+      new RenderFrameProxy(agent_scheduling_group, routing_id));
   proxy->devtools_frame_token_ = devtools_frame_token;
   RenderViewImpl* render_view = nullptr;
   RenderWidget* ancestor_widget = nullptr;
@@ -177,12 +182,14 @@
 }
 
 RenderFrameProxy* RenderFrameProxy::CreateProxyForPortal(
+    AgentSchedulingGroup& agent_scheduling_group,
     RenderFrameImpl* parent,
     int proxy_routing_id,
     const base::UnguessableToken& frame_token,
     const base::UnguessableToken& devtools_frame_token,
     const blink::WebElement& portal_element) {
-  auto proxy = base::WrapUnique(new RenderFrameProxy(proxy_routing_id));
+  auto proxy = base::WrapUnique(
+      new RenderFrameProxy(agent_scheduling_group, proxy_routing_id));
   proxy->devtools_frame_token_ = devtools_frame_token;
   blink::WebRemoteFrame* web_frame = blink::WebRemoteFrame::CreateForPortal(
       blink::mojom::TreeScopeType::kDocument, proxy.get(),
@@ -219,8 +226,10 @@
   return nullptr;
 }
 
-RenderFrameProxy::RenderFrameProxy(int routing_id)
-    : routing_id_(routing_id),
+RenderFrameProxy::RenderFrameProxy(AgentSchedulingGroup& agent_scheduling_group,
+                                   int routing_id)
+    : agent_scheduling_group_(agent_scheduling_group),
+      routing_id_(routing_id),
       provisional_frame_routing_id_(MSG_ROUTING_NONE),
       // TODO(samans): Investigate if it is safe to delay creation of this
       // object until a FrameSinkId is provided.
@@ -229,14 +238,14 @@
   std::pair<RoutingIDProxyMap::iterator, bool> result =
       g_routing_id_proxy_map.Get().insert(std::make_pair(routing_id_, this));
   CHECK(result.second) << "Inserting a duplicate item.";
-  RenderThread::Get()->AddRoute(routing_id_, this);
-  blink_interface_registry_.reset(new BlinkInterfaceRegistryImpl(
-      binder_registry_.GetWeakPtr(), associated_interfaces_.GetWeakPtr()));
+  agent_scheduling_group_.AddRoute(routing_id_, this);
+  blink_interface_registry_ = std::make_unique<BlinkInterfaceRegistryImpl>(
+      binder_registry_.GetWeakPtr(), associated_interfaces_.GetWeakPtr());
 }
 
 RenderFrameProxy::~RenderFrameProxy() {
   CHECK(!web_frame_);
-  RenderThread::Get()->RemoveRoute(routing_id_);
+  agent_scheduling_group_.RemoveRoute(routing_id_);
   g_routing_id_proxy_map.Get().erase(routing_id_);
 }
 
@@ -444,7 +453,7 @@
 }
 
 bool RenderFrameProxy::Send(IPC::Message* message) {
-  return RenderThread::Get()->Send(message);
+  return agent_scheduling_group_.Send(message);
 }
 
 void RenderFrameProxy::OnDeleteProxy() {
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index 778c3c98..318662d 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -37,6 +37,7 @@
 
 namespace content {
 
+class AgentSchedulingGroup;
 class BlinkInterfaceRegistryImpl;
 class ChildFrameCompositingHelper;
 class RenderFrameImpl;
@@ -76,6 +77,7 @@
   // created RenderFrameProxy. |frame_to_replace| is the frame that the new
   // proxy will eventually swap places with.
   static RenderFrameProxy* CreateProxyToReplaceFrame(
+      AgentSchedulingGroup& agent_scheduling_group,
       RenderFrameImpl* frame_to_replace,
       int routing_id,
       blink::mojom::TreeScopeType scope,
@@ -95,6 +97,7 @@
   // RenderFrame) because a new child of a local frame should always start out
   // as a frame, not a proxy.
   static RenderFrameProxy* CreateFrameProxy(
+      AgentSchedulingGroup& agent_scheduling_group,
       int routing_id,
       int render_view_routing_id,
       const base::Optional<base::UnguessableToken>& opener_frame_token,
@@ -106,6 +109,7 @@
   // Creates a RenderFrameProxy to be used with a portal owned by |parent|.
   // |routing_id| is the routing id of this new RenderFrameProxy.
   static RenderFrameProxy* CreateProxyForPortal(
+      AgentSchedulingGroup& agent_scheduling_group,
       RenderFrameImpl* parent,
       int proxy_routing_id,
       const base::UnguessableToken& frame_token,
@@ -209,7 +213,8 @@
   void FrameSinkIdChanged(const viz::FrameSinkId& frame_sink_id);
 
  private:
-  RenderFrameProxy(int routing_id);
+  RenderFrameProxy(AgentSchedulingGroup& agent_scheduling_group,
+                   int routing_id);
 
   void Init(blink::WebRemoteFrame* frame,
             RenderViewImpl* render_view,
@@ -244,6 +249,11 @@
 
   const viz::LocalSurfaceId& GetLocalSurfaceId() const;
 
+  // The |AgentSchedulingGroup| this proxy is associated with. NOTE: This is
+  // different than the |AgentSchedulingGroup| associated with the frame being
+  // proxied.
+  AgentSchedulingGroup& agent_scheduling_group_;
+
   // The routing ID by which this RenderFrameProxy is known.
   const int routing_id_;
 
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 46c1d5d..5b6cab9d 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1897,9 +1897,17 @@
     const FrameReplicationState& replicated_state,
     const base::UnguessableToken& frame_token,
     const base::UnguessableToken& devtools_frame_token) {
-  RenderFrameProxy::CreateFrameProxy(
-      routing_id, render_view_routing_id, opener_frame_token, parent_routing_id,
-      replicated_state, frame_token, devtools_frame_token);
+  // TODO(crbug.com/1111231): For as long as frame proxies are created via the
+  // `Renderer` interface (as opposed to `AgentSchedulingGroup`), we will always
+  // have *exactly one* `AgentSchedulingGroup` in the process.
+  DCHECK_EQ(agent_scheduling_groups_.size(), 1ul);
+  AgentSchedulingGroup& agent_scheduling_group =
+      *agent_scheduling_groups_.begin()->get();
+
+  RenderFrameProxy::CreateFrameProxy(agent_scheduling_group, routing_id,
+                                     render_view_routing_id, opener_frame_token,
+                                     parent_routing_id, replicated_state,
+                                     frame_token, devtools_frame_token);
 }
 
 void RenderThreadImpl::OnNetworkConnectionChanged(
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 5205a19..2a7f845f 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -331,9 +331,10 @@
         std::move(show_callback));
   } else {
     RenderFrameProxy::CreateFrameProxy(
-        params->proxy_routing_id, GetRoutingID(), params->opener_frame_token,
-        MSG_ROUTING_NONE, params->replicated_frame_state,
-        params->main_frame_frame_token, params->devtools_main_frame_token);
+        agent_scheduling_group_, params->proxy_routing_id, GetRoutingID(),
+        params->opener_frame_token, MSG_ROUTING_NONE,
+        params->replicated_frame_state, params->main_frame_frame_token,
+        params->devtools_main_frame_token);
   }
 
   // TODO(davidben): Move this state from Blink into content.
diff --git a/content/shell/gpu/shell_content_gpu_client.cc b/content/shell/gpu/shell_content_gpu_client.cc
index a8a0050..22faaccb 100644
--- a/content/shell/gpu/shell_content_gpu_client.cc
+++ b/content/shell/gpu/shell_content_gpu_client.cc
@@ -16,6 +16,7 @@
 
 void ShellContentGpuClient::ExposeInterfacesToBrowser(
     const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
     mojo::BinderMap* binders) {
   binders->Add<mojom::PowerMonitorTest>(
       base::BindRepeating(&PowerMonitorTestImpl::MakeSelfOwnedReceiver),
diff --git a/content/shell/gpu/shell_content_gpu_client.h b/content/shell/gpu/shell_content_gpu_client.h
index 72c585d6..e4387dec 100644
--- a/content/shell/gpu/shell_content_gpu_client.h
+++ b/content/shell/gpu/shell_content_gpu_client.h
@@ -19,8 +19,10 @@
   ~ShellContentGpuClient() override;
 
   // ContentGpuClient:
-  void ExposeInterfacesToBrowser(const gpu::GpuPreferences& gpu_preferences,
-                                 mojo::BinderMap* binders) override;
+  void ExposeInterfacesToBrowser(
+      const gpu::GpuPreferences& gpu_preferences,
+      const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
+      mojo::BinderMap* binders) override;
 
   DISALLOW_COPY_AND_ASSIGN(ShellContentGpuClient);
 };
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index ef7ff23c..8cefd73 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2057,8 +2057,6 @@
     "../renderer/loader/test_request_peer.h",
     "../renderer/loader/url_loader_client_impl_unittest.cc",
     "../renderer/loader/web_url_loader_impl_unittest.cc",
-    "../renderer/media/audio/mock_audio_device_factory.cc",
-    "../renderer/media/audio/mock_audio_device_factory.h",
     "../renderer/media/batching_media_log_unittest.cc",
     "../renderer/media/inspector_media_event_handler_unittest.cc",
     "../renderer/media/power_status_helper_impl_unittest.cc",
diff --git a/docs/clang_sheriffing.md b/docs/clang_sheriffing.md
index 1f4f5f3..4cf52f3 100644
--- a/docs/clang_sheriffing.md
+++ b/docs/clang_sheriffing.md
@@ -14,8 +14,21 @@
 change, filing bugs upstream, and often reverting bad changes in LLVM. This
 document describes some of the processes and techniques for doing that.
 
-https://sheriff-o-matic.appspot.com/chromium.clang is the sheriff-o-matic
-view of that waterfall, which can be easier to work with.
+Some may find the [sheriff-o-matic]
+(https://sheriff-o-matic.appspot.com/chromium.clang) view of the waterfall
+easier to work with.
+
+To keep others informed, [file a bug]
+(https://bugs.chromium.org/p/chromium/issues/entry) .
+earlier rather than later for build breaks likely caused by changes in
+clang or the rest fo the toolchain. Make sure to set the component field to
+`Tools > LLVM`, which will include the entire Chrome toolchain (Lexan) team.
+
+At the beginning of your sheriff rotation, it may be
+useful to [search for recent bot breaks]
+(https://bugs.chromium.org/p/chromium/issues/list?q=component%3ATools%3ELLVM&can=2&sort=-modified).
+We prefer searching like this to having sheriffs compose status email at the
+end of their week.
 
 In addition to the waterfall, make sure
 [dry run attempts at updating clang](https://chromium-review.googlesource.com/q/owner:thakis%2540chromium.org+%2522roll+clang%2522)
@@ -24,8 +37,12 @@
 quickly noticed and fixed by the original author of a breaking commit,
 but that is sadly not always the case.
 
-We try to update Clang (roughly) weekly. Time permitting, try to [update the
-compiler](updating_clang.md) when bots are green enough.
+Each sheriff should attempt to update the compiler by performing
+[a Clang roll](updating_clang.md) during their week, assuming the bots are
+green enough.
+
+The Sheriff is also responsible for taking notes during the weekly Chrome toolchain
+(Lexan) status sync-up meeting.
 
 [TOC]
 
diff --git a/docs/security/autougprade-mixed.md b/docs/security/autougprade-mixed.md
deleted file mode 100644
index e734d70..0000000
--- a/docs/security/autougprade-mixed.md
+++ /dev/null
@@ -1 +0,0 @@
-# This document is located at [autoupgrade-mixed.md](https://chromium.googlesource.com/chromium/src/+/master/docs/security/autoupgrade-mixed.md)
diff --git a/gpu/command_buffer/service/external_vk_image_skia_representation.cc b/gpu/command_buffer/service/external_vk_image_skia_representation.cc
index 5a88fd6..380d617b 100644
--- a/gpu/command_buffer/service/external_vk_image_skia_representation.cc
+++ b/gpu/command_buffer/service/external_vk_image_skia_representation.cc
@@ -85,7 +85,7 @@
   // VkImage to VK_QUEUE_FAMILY_EXTERNAL before calling EndAccess().
   if (backing_impl()->need_synchronization()) {
     *end_state = std::make_unique<GrBackendSurfaceMutableState>(
-        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_QUEUE_FAMILY_EXTERNAL);
+        VK_IMAGE_LAYOUT_UNDEFINED, VK_QUEUE_FAMILY_EXTERNAL);
   }
 
   return surface;
@@ -125,7 +125,7 @@
   // VK_QUEUE_FAMILY_EXTERNAL before calling EndAccess().
   if (!backing_impl()->use_separate_gl_texture()) {
     *end_state = std::make_unique<GrBackendSurfaceMutableState>(
-        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_QUEUE_FAMILY_EXTERNAL);
+        VK_IMAGE_LAYOUT_UNDEFINED, VK_QUEUE_FAMILY_EXTERNAL);
   }
 
   access_mode_ = kRead;
diff --git a/infra/config/PRESUBMIT.py b/infra/config/PRESUBMIT.py
index 358d02e..3cd5940b 100644
--- a/infra/config/PRESUBMIT.py
+++ b/infra/config/PRESUBMIT.py
@@ -8,45 +8,44 @@
 for more details on the presubmit API built into depot_tools.
 """
 
+PRESUBMIT_VERSION = '2.0.0'
 
-def _CommonChecks(input_api, output_api):
-  commands = []
 
+def CheckLintLuciMilo(input_api, output_api):
   if ('infra/config/generated/luci-milo.cfg' in input_api.LocalPaths() or
       'infra/config/lint-luci-milo.py' in input_api.LocalPaths()):
-    commands.append(
-      input_api.Command(
-          name='lint-luci-milo',
-          cmd=[input_api.python_executable, 'lint-luci-milo.py'],
-          kwargs={},
-          message=output_api.PresubmitError))
+    return input_api.RunTests([
+        input_api.Command(
+            name='lint-luci-milo',
+            cmd=[input_api.python_executable, 'lint-luci-milo.py'],
+            kwargs={},
+            message=output_api.PresubmitError),
+    ])
+  return []
+
+def CheckTestingBuildbot(input_api, output_api):
   if ('infra/config/generated/luci-milo.cfg' in input_api.LocalPaths() or
       'infra/config/generated/luci-milo-dev.cfg' in input_api.LocalPaths()):
-    commands.append(
-      input_api.Command(
-        name='testing/buildbot config checks',
-        cmd=[input_api.python_executable, input_api.os_path.join(
+    return input_api.RunTests([
+        input_api.Command(
+            name='testing/buildbot config checks',
+            cmd=[input_api.python_executable, input_api.os_path.join(
                 '..', '..', 'testing', 'buildbot',
                 'generate_buildbot_json.py',),
-            '--check'],
-        kwargs={}, message=output_api.PresubmitError))
+                 '--check'],
+            kwargs={},
+            message=output_api.PresubmitError),
+    ])
+  return []
 
-  commands.extend(input_api.canned_checks.CheckLucicfgGenOutput(
+def CheckLucicfgGenOutputMain(input_api, output_api):
+  return input_api.RunTests(input_api.canned_checks.CheckLucicfgGenOutput(
       input_api, output_api, 'main.star'))
-  commands.extend(input_api.canned_checks.CheckLucicfgGenOutput(
+
+def CheckLucicfgGenOutputDev(input_api, output_api):
+  return input_api.RunTests(input_api.canned_checks.CheckLucicfgGenOutput(
       input_api, output_api, 'dev.star'))
 
-  results = []
-
-  results.extend(input_api.RunTests(commands))
-  results.extend(input_api.canned_checks.CheckChangedLUCIConfigs(
-      input_api, output_api))
-
-  return results
-
-
-def CheckChangeOnUpload(input_api, output_api):
-  return _CommonChecks(input_api, output_api)
-
-def CheckChangeOnCommit(input_api, output_api):
-  return _CommonChecks(input_api, output_api)
+def CheckChangedLUCIConfigs(input_api, output_api):
+  return input_api.canned_checks.CheckChangedLUCIConfigs(
+      input_api, output_api)
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg
index 4cb2a714..71619b0 100644
--- a/infra/config/generated/commit-queue.cfg
+++ b/infra/config/generated/commit-queue.cfg
@@ -239,7 +239,7 @@
       }
       builders {
         name: "chromium/try/android-marshmallow-x86-rel"
-        experiment_percentage: 5
+        experiment_percentage: 20
         location_regexp: ".*"
         location_regexp_exclude: ".+/[+]/docs/.+"
         location_regexp_exclude: ".+/[+]/infra/config/.+"
@@ -285,7 +285,7 @@
       }
       builders {
         name: "chromium/try/android-pie-arm64-rel"
-        experiment_percentage: 60
+        experiment_percentage: 80
         location_regexp: ".*"
         location_regexp_exclude: ".+/[+]/docs/.+"
         location_regexp_exclude: ".+/[+]/infra/config/.+"
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md
index e7d42c2..8c89d05 100644
--- a/infra/config/generated/cq-builders.md
+++ b/infra/config/generated/cq-builders.md
@@ -348,10 +348,10 @@
 as required builders.
 
 * [android-marshmallow-x86-rel](https://ci.chromium.org/p/chromium/builders/try/android-marshmallow-x86-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android-marshmallow-x86-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android-marshmallow-x86-rel))
-  * Experiment percentage: 5
+  * Experiment percentage: 20
 
 * [android-pie-arm64-rel](https://ci.chromium.org/p/chromium/builders/try/android-pie-arm64-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android-pie-arm64-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android-pie-arm64-rel))
-  * Experiment percentage: 60
+  * Experiment percentage: 80
 
 * [fuchsia-compile-x64-dbg](https://ci.chromium.org/p/chromium/builders/try/fuchsia-compile-x64-dbg) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+fuchsia-compile-x64-dbg)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+fuchsia-compile-x64-dbg))
   * Experiment percentage: 50
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index dabd95c..8fd3bfe 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -265,7 +265,7 @@
     goma_jobs = goma.jobs.J300,
     ssd = True,
     tryjob = try_.job(
-        experiment_percentage = 5,
+        experiment_percentage = 20,
     ),
 )
 
@@ -324,7 +324,7 @@
     # TODO(crbug.com/1111436): Enable on CQ fully once the tests run fine.
     main_list_view = settings.main_list_view_name,
     tryjob = try_.job(
-        experiment_percentage = 60,
+        experiment_percentage = 80,
     ),
 )
 
diff --git a/ios/chrome/browser/credential_provider/BUILD.gn b/ios/chrome/browser/credential_provider/BUILD.gn
index 354a10fb..8a00e1d1 100644
--- a/ios/chrome/browser/credential_provider/BUILD.gn
+++ b/ios/chrome/browser/credential_provider/BUILD.gn
@@ -32,6 +32,7 @@
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/passwords",
     "//ios/chrome/browser/signin",
+    "//ios/chrome/browser/sync",
     "//ios/chrome/common/app_group",
     "//ios/chrome/common/credential_provider",
     "//ios/public/provider/chrome/browser/signin",
diff --git a/ios/chrome/browser/credential_provider/archivable_credential+password_form.mm b/ios/chrome/browser/credential_provider/archivable_credential+password_form.mm
index 0097c91..6112740 100644
--- a/ios/chrome/browser/credential_provider/archivable_credential+password_form.mm
+++ b/ios/chrome/browser/credential_provider/archivable_credential+password_form.mm
@@ -28,20 +28,52 @@
 - (instancetype)initWithPasswordForm:(const autofill::PasswordForm&)passwordForm
                              favicon:(NSString*)favicon
                 validationIdentifier:(NSString*)validationIdentifier {
-  if (passwordForm.url.is_empty() || passwordForm.blocked_by_user ||
-      password_manager::IsValidAndroidFacetURI(passwordForm.signon_realm)) {
+  if (passwordForm.blocked_by_user) {
     return nil;
   }
   std::string site_name =
       password_manager::GetShownOrigin(url::Origin::Create(passwordForm.url));
   NSString* keychainIdentifier =
       SysUTF8ToNSString(passwordForm.encrypted_password);
+
+  NSString* serviceIdentifier = SysUTF8ToNSString(passwordForm.url.spec());
+  NSString* serviceName = SysUTF8ToNSString(site_name);
+
+  if (password_manager::IsValidAndroidFacetURI(passwordForm.signon_realm)) {
+    NSString* webRealm = SysUTF8ToNSString(passwordForm.affiliated_web_realm);
+    url::Origin origin =
+        url::Origin::Create(GURL(passwordForm.affiliated_web_realm));
+    std::string shownOrigin = password_manager::GetShownOrigin(origin);
+
+    // Set serviceIdentifier:
+    if (webRealm.length) {
+      // Prefer webRealm.
+      serviceIdentifier = webRealm;
+    } else if (!serviceIdentifier.length) {
+      // Fallback to signon_realm.
+      serviceIdentifier = SysUTF8ToNSString(passwordForm.signon_realm);
+    }
+
+    // Set serviceName:
+    if (!shownOrigin.empty()) {
+      // Prefer shownOrigin to match non Android credentials.
+      serviceName = SysUTF8ToNSString(shownOrigin);
+    } else if (!passwordForm.app_display_name.empty()) {
+      serviceName = SysUTF8ToNSString(passwordForm.app_display_name);
+    } else if (!serviceName.length) {
+      // Fallback to serviceIdentifier.
+      serviceName = serviceIdentifier;
+    }
+  }
+
+  DCHECK(serviceIdentifier.length);
+
   return [self initWithFavicon:favicon
             keychainIdentifier:keychainIdentifier
                           rank:passwordForm.times_used
               recordIdentifier:RecordIdentifierForPasswordForm(passwordForm)
-             serviceIdentifier:SysUTF8ToNSString(passwordForm.url.spec())
-                   serviceName:SysUTF8ToNSString(site_name)
+             serviceIdentifier:serviceIdentifier
+                   serviceName:serviceName
                           user:SysUTF16ToNSString(passwordForm.username_value)
           validationIdentifier:validationIdentifier];
 }
diff --git a/ios/chrome/browser/credential_provider/archivable_credential+password_form_unittest.mm b/ios/chrome/browser/credential_provider/archivable_credential+password_form_unittest.mm
index 8d06c798..1f5b8f33 100644
--- a/ios/chrome/browser/credential_provider/archivable_credential+password_form_unittest.mm
+++ b/ios/chrome/browser/credential_provider/archivable_credential+password_form_unittest.mm
@@ -52,4 +52,61 @@
               credential.recordIdentifier);
 }
 
+// Tests the creation of a credential from a password form.
+TEST_F(ArchivableCredentialPasswordFormTest, AndroidCredentialCreation) {
+  PasswordForm form;
+  form.signon_realm = "android://hash@com.example.my.app";
+  form.password_element = base::ASCIIToUTF16("pwd");
+  form.password_value = base::ASCIIToUTF16("example");
+
+  ArchivableCredential* credentialOnlyRealm =
+      [[ArchivableCredential alloc] initWithPasswordForm:form
+                                                 favicon:nil
+                                    validationIdentifier:nil];
+
+  EXPECT_TRUE(credentialOnlyRealm);
+  EXPECT_NSEQ(@"android://hash@com.example.my.app",
+              credentialOnlyRealm.serviceName);
+  EXPECT_NSEQ(@"android://hash@com.example.my.app",
+              credentialOnlyRealm.serviceIdentifier);
+
+  form.app_display_name = "my.app";
+
+  ArchivableCredential* credentialRealmAndAppName =
+      [[ArchivableCredential alloc] initWithPasswordForm:form
+                                                 favicon:nil
+                                    validationIdentifier:nil];
+
+  EXPECT_NSEQ(@"my.app", credentialRealmAndAppName.serviceName);
+  EXPECT_NSEQ(@"android://hash@com.example.my.app",
+              credentialRealmAndAppName.serviceIdentifier);
+
+  form.affiliated_web_realm = "https://m.app.example.com";
+
+  ArchivableCredential* credentialAffiliatedRealm =
+      [[ArchivableCredential alloc] initWithPasswordForm:form
+                                                 favicon:nil
+                                    validationIdentifier:nil];
+
+  EXPECT_NSEQ(@"app.example.com", credentialAffiliatedRealm.serviceName);
+  EXPECT_NSEQ(@"https://m.app.example.com",
+              credentialAffiliatedRealm.serviceIdentifier);
+}
+
+// Tests the creation of blacklisted forms is not possible.
+TEST_F(ArchivableCredentialPasswordFormTest, BlacklistedCreation) {
+  PasswordForm form;
+  form.signon_realm = "android://hash@com.example.my.app";
+  form.password_element = base::ASCIIToUTF16("pwd");
+  form.password_value = base::ASCIIToUTF16("example");
+  form.blocked_by_user = true;
+
+  ArchivableCredential* credential =
+      [[ArchivableCredential alloc] initWithPasswordForm:form
+                                                 favicon:nil
+                                    validationIdentifier:nil];
+
+  EXPECT_FALSE(credential);
+}
+
 }  // namespace
diff --git a/ios/chrome/browser/credential_provider/credential_provider_service.h b/ios/chrome/browser/credential_provider/credential_provider_service.h
index 16fa904..3b104d4 100644
--- a/ios/chrome/browser/credential_provider/credential_provider_service.h
+++ b/ios/chrome/browser/credential_provider/credential_provider_service.h
@@ -10,24 +10,32 @@
 #include "components/password_manager/core/browser/password_store.h"
 #include "components/password_manager/core/browser/password_store_consumer.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/sync/driver/sync_service_observer.h"
 #import "ios/chrome/browser/signin/authentication_service.h"
 
+@class ArchivableCredential;
 @class ArchivableCredentialStore;
 
+namespace syncer {
+class SyncService;
+}
+
 // A browser-context keyed service that is used to keep the Credential Provider
 // Extension data up to date.
 class CredentialProviderService
     : public KeyedService,
       public password_manager::PasswordStoreConsumer,
       public password_manager::PasswordStore::Observer,
-      public signin::IdentityManager::Observer {
+      public signin::IdentityManager::Observer,
+      public syncer::SyncServiceObserver {
  public:
   // Initializes the service.
   CredentialProviderService(
       scoped_refptr<password_manager::PasswordStore> password_store,
       AuthenticationService* authentication_service,
       ArchivableCredentialStore* credential_store,
-      signin::IdentityManager* identity_manager);
+      signin::IdentityManager* identity_manager,
+      syncer::SyncService* sync_service);
   ~CredentialProviderService() override;
 
   // KeyedService:
@@ -44,8 +52,25 @@
   // the old ones are deleted.
   void RequestSyncAllCredentials();
 
+  // Evaluates if a credential refresh is needed, and request all the
+  // credentials to sync them if needed.
+  void RequestSyncAllCredentialsIfNeeded();
+
+  // Replaces all data with credentials created from the passed forms and then
+  // syncs to disk.
+  void SyncAllCredentials(
+      std::vector<std::unique_ptr<autofill::PasswordForm>> forms);
+
   // Syncs the credential store to disk.
-  void SyncStore(void (^completion)(NSError*)) const;
+  void SyncStore(bool set_first_time_sync_flag);
+
+  // Add credentials from |forms|.
+  void AddCredentials(
+      std::vector<std::unique_ptr<autofill::PasswordForm>> forms);
+
+  // Removes credentials from |forms|.
+  void RemoveCredentials(
+      std::vector<std::unique_ptr<autofill::PasswordForm>> forms);
 
   // Syncs account_validation_id_.
   void UpdateAccountValidationId();
@@ -58,6 +83,14 @@
   void OnLoginsChanged(
       const password_manager::PasswordStoreChangeList& changes) override;
 
+  // Completion called after the affiliations are injected in the added forms.
+  // If no affiliation matcher is available, it is called right away.
+  void OnInjectedAffiliationAfterLoginsChanged(
+      std::vector<std::unique_ptr<autofill::PasswordForm>> forms);
+
+  // syncer::SyncServiceObserver:
+  void OnSyncConfigurationCompleted(syncer::SyncService* sync) override;
+
   // The interface for getting and manipulating a user's saved passwords.
   scoped_refptr<password_manager::PasswordStore> password_store_;
 
@@ -65,7 +98,10 @@
   AuthenticationService* authentication_service_ = nullptr;
 
   // Identity manager to observe.
-  signin::IdentityManager* identity_manager_;
+  signin::IdentityManager* identity_manager_ = nullptr;
+
+  // Sync Service to observe.
+  syncer::SyncService* sync_service_ = nullptr;
 
   // The interface for saving and updating credentials.
   ArchivableCredentialStore* archivable_credential_store_ = nil;
@@ -73,6 +109,9 @@
   // The current validation ID or nil.
   NSString* account_validation_id_ = nil;
 
+  // Weak pointer factory.
+  base::WeakPtrFactory<CredentialProviderService> weak_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(CredentialProviderService);
 };
 
diff --git a/ios/chrome/browser/credential_provider/credential_provider_service.mm b/ios/chrome/browser/credential_provider/credential_provider_service.mm
index f2beeb23..db791ed 100644
--- a/ios/chrome/browser/credential_provider/credential_provider_service.mm
+++ b/ios/chrome/browser/credential_provider/credential_provider_service.mm
@@ -13,9 +13,14 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "build/build_config.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
 #include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+#include "components/password_manager/core/browser/android_affiliation/android_affiliation_service.h"
+#include "components/password_manager/core/browser/password_manager_util.h"
+#include "components/password_manager/core/browser/password_store.h"
 #include "components/password_manager/core/browser/password_store_change.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/sync/driver/sync_service.h"
 #include "ios/chrome/browser/credential_provider/archivable_credential+password_form.h"
 #import "ios/chrome/browser/credential_provider/credential_provider_util.h"
 #include "ios/chrome/common/app_group/app_group_constants.h"
@@ -32,9 +37,11 @@
 namespace {
 
 using autofill::PasswordForm;
+using password_manager::AffiliatedMatchHelper;
 using password_manager::PasswordStore;
 using password_manager::PasswordStoreChange;
 using password_manager::PasswordStoreChangeList;
+using password_manager::AndroidAffiliationService;
 
 // ASCredentialIdentityStoreError enum to report UMA metrics. Must be in sync
 // with iOSCredentialIdentityStoreErrorForReporting in
@@ -121,31 +128,18 @@
       getCredentialIdentityStoreStateWithCompletion:stateCompletion];
 }
 
-ArchivableCredential* CredentialFromForm(const PasswordForm& form,
-                                         NSString* validation_id) {
-  ArchivableCredential* credential =
-      [[ArchivableCredential alloc] initWithPasswordForm:form
-                                                 favicon:nil
-                                    validationIdentifier:validation_id];
-  if (!credential) {
-    // Verify that the credential is nil because it's an Android one or
-    // blacklisted.
-    DCHECK(password_manager::IsValidAndroidFacetURI(form.signon_realm) ||
-           form.blocked_by_user);
-  }
-  return credential;
-}
-
 }  // namespace
 
 CredentialProviderService::CredentialProviderService(
     scoped_refptr<PasswordStore> password_store,
     AuthenticationService* authentication_service,
     ArchivableCredentialStore* credential_store,
-    signin::IdentityManager* identity_manager)
+    signin::IdentityManager* identity_manager,
+    syncer::SyncService* sync_service)
     : password_store_(password_store),
       authentication_service_(authentication_service),
       identity_manager_(identity_manager),
+      sync_service_(sync_service),
       archivable_credential_store_(credential_store) {
   DCHECK(password_store_);
   password_store_->AddObserver(this);
@@ -157,14 +151,16 @@
     identity_manager_->AddObserver(this);
   }
 
-  // TODO(crbug.com/1066803): Wait for things to settle down before
-  // syncs, and sync credentials after Sync finishes or some
-  // seconds in the future.
-  if (ShouldSyncASIdentityStore()) {
-    SyncASIdentityStore(credential_store);
+  bool is_sync_active = false;
+  if (sync_service_) {
+    sync_service_->AddObserver(this);
+    is_sync_active = sync_service_->IsSyncFeatureActive();
   }
-  if (ShouldSyncAllCredentials()) {
-    RequestSyncAllCredentials();
+
+  // If Sync is active, wait for the configuration to finish before syncing.
+  // This will wait for affiliated_match_helper to be available.
+  if (!is_sync_active) {
+    RequestSyncAllCredentialsIfNeeded();
   }
 }
 
@@ -175,16 +171,9 @@
   if (identity_manager_) {
     identity_manager_->RemoveObserver(this);
   }
-}
-
-void CredentialProviderService::OnPrimaryAccountSet(
-    const CoreAccountInfo& primary_account_info) {
-  RequestSyncAllCredentials();
-}
-
-void CredentialProviderService::OnPrimaryAccountCleared(
-    const CoreAccountInfo& previous_primary_account_info) {
-  RequestSyncAllCredentials();
+  if (sync_service_) {
+    sync_service_->RemoveObserver(this);
+  }
 }
 
 void CredentialProviderService::RequestSyncAllCredentials() {
@@ -192,6 +181,65 @@
   password_store_->GetAutofillableLogins(this);
 }
 
+void CredentialProviderService::RequestSyncAllCredentialsIfNeeded() {
+  if (ShouldSyncASIdentityStore()) {
+    SyncASIdentityStore(archivable_credential_store_);
+  }
+  if (ShouldSyncAllCredentials()) {
+    RequestSyncAllCredentials();
+  }
+}
+
+void CredentialProviderService::SyncAllCredentials(
+    std::vector<std::unique_ptr<PasswordForm>> forms) {
+  [archivable_credential_store_ removeAllCredentials];
+  AddCredentials(std::move(forms));
+  SyncStore(true);
+}
+
+void CredentialProviderService::SyncStore(bool set_first_time_sync_flag) {
+  __weak ArchivableCredentialStore* weak_archivable_credential_store =
+      archivable_credential_store_;
+  [archivable_credential_store_ saveDataWithCompletion:^(NSError* error) {
+    if (error) {
+      return;
+    }
+    if (set_first_time_sync_flag) {
+      NSUserDefaults* user_defaults = [NSUserDefaults standardUserDefaults];
+      for (NSString* key in UnusedUserDefaultsCredentialProviderKeys()) {
+        [user_defaults removeObjectForKey:key];
+      }
+      NSString* key = kUserDefaultsCredentialProviderFirstTimeSyncCompleted;
+      [user_defaults setBool:YES forKey:key];
+    }
+    if (weak_archivable_credential_store) {
+      SyncASIdentityStore(weak_archivable_credential_store);
+    }
+  }];
+}
+
+void CredentialProviderService::AddCredentials(
+    std::vector<std::unique_ptr<PasswordForm>> forms) {
+  for (const auto& form : forms) {
+    ArchivableCredential* credential = [[ArchivableCredential alloc]
+        initWithPasswordForm:*form
+                     favicon:nil
+        validationIdentifier:account_validation_id_];
+    DCHECK(credential);
+    [archivable_credential_store_ addCredential:credential];
+  }
+}
+
+void CredentialProviderService::RemoveCredentials(
+    std::vector<std::unique_ptr<PasswordForm>> forms) {
+  for (const auto& form : forms) {
+    NSString* recordID = RecordIdentifierForPasswordForm(*form);
+    DCHECK(recordID);
+    [archivable_credential_store_
+        removeCredentialWithRecordIdentifier:recordID];
+  }
+}
+
 void CredentialProviderService::UpdateAccountValidationId() {
   if (authentication_service_->IsAuthenticatedIdentityManaged()) {
     account_validation_id_ =
@@ -204,69 +252,82 @@
          forKey:AppGroupUserDefaultsCredentialProviderManagedUserID()];
 }
 
-void CredentialProviderService::SyncStore(void (^completion)(NSError*)) const {
-  [archivable_credential_store_ saveDataWithCompletion:^(NSError* error) {
-    DCHECK(!error) << "An error occurred while saving to disk";
-    if (completion) {
-      completion(error);
-    }
-  }];
-}
-
 void CredentialProviderService::OnGetPasswordStoreResults(
     std::vector<std::unique_ptr<PasswordForm>> results) {
-  [archivable_credential_store_ removeAllCredentials];
-  for (const auto& form : results) {
-    ArchivableCredential* credential =
-        CredentialFromForm(*form, account_validation_id_);
-    if (credential) {
-      [archivable_credential_store_ addCredential:credential];
-    }
+  auto callback = base::BindOnce(&CredentialProviderService::SyncAllCredentials,
+                                 weak_factory_.GetWeakPtr());
+  AffiliatedMatchHelper* matcher = password_store_->affiliated_match_helper();
+  if (matcher) {
+    matcher->InjectAffiliationAndBrandingInformation(
+        std::move(results),
+        AndroidAffiliationService::StrategyOnCacheMiss::FETCH_OVER_NETWORK,
+        std::move(callback));
+  } else {
+    std::move(callback).Run(std::move(results));
   }
-  SyncStore(^(NSError* error) {
-    if (!error) {
-      NSUserDefaults* user_defaults = [NSUserDefaults standardUserDefaults];
-      NSString* key = kUserDefaultsCredentialProviderFirstTimeSyncCompleted;
-      [user_defaults setBool:YES forKey:key];
-      SyncASIdentityStore(archivable_credential_store_);
-    }
-  });
+}
+
+void CredentialProviderService::OnPrimaryAccountSet(
+    const CoreAccountInfo& primary_account_info) {
+  RequestSyncAllCredentials();
+}
+
+void CredentialProviderService::OnPrimaryAccountCleared(
+    const CoreAccountInfo& previous_primary_account_info) {
+  RequestSyncAllCredentials();
 }
 
 void CredentialProviderService::OnLoginsChanged(
     const PasswordStoreChangeList& changes) {
+  std::vector<std::unique_ptr<PasswordForm>> forms_to_add;
+  std::vector<std::unique_ptr<PasswordForm>> forms_to_remove;
   for (const PasswordStoreChange& change : changes) {
-    ArchivableCredential* credential =
-        CredentialFromForm(change.form(), account_validation_id_);
     if (change.form().blocked_by_user) {
       continue;
     }
     switch (change.type()) {
       case PasswordStoreChange::ADD:
-        [archivable_credential_store_ addCredential:credential];
+        forms_to_add.push_back(std::make_unique<PasswordForm>(change.form()));
         break;
       case PasswordStoreChange::UPDATE:
-        [archivable_credential_store_ updateCredential:credential];
+        forms_to_remove.push_back(
+            std::make_unique<PasswordForm>(change.form()));
+        forms_to_add.push_back(std::make_unique<PasswordForm>(change.form()));
         break;
       case PasswordStoreChange::REMOVE:
-        // Using the record identifier from the form, as the credential might
-        // not be valid anymore.
-        [archivable_credential_store_
-            removeCredentialWithRecordIdentifier:
-                RecordIdentifierForPasswordForm(change.form())];
+        forms_to_remove.push_back(
+            std::make_unique<PasswordForm>(change.form()));
         break;
-
       default:
         NOTREACHED();
         break;
     }
   }
-  SyncStore(^(NSError* error) {
-    if (!error) {
-      // TODO(crbug.com/1077747): Support ASCredentialIdentityStore incremental
-      // updates. Currently calling multiple methods on it to save and remove
-      // causes it to crash. This needs to be further investigated.
-      SyncASIdentityStore(archivable_credential_store_);
-    }
-  });
+
+  RemoveCredentials(std::move(forms_to_remove));
+
+  auto callback = base::BindOnce(
+      &CredentialProviderService::OnInjectedAffiliationAfterLoginsChanged,
+      weak_factory_.GetWeakPtr());
+
+  AffiliatedMatchHelper* matcher = password_store_->affiliated_match_helper();
+  if (matcher) {
+    matcher->InjectAffiliationAndBrandingInformation(
+        std::move(forms_to_add),
+        AndroidAffiliationService::StrategyOnCacheMiss::FETCH_OVER_NETWORK,
+        std::move(callback));
+  } else {
+    std::move(callback).Run(std::move(forms_to_add));
+  }
+}
+
+void CredentialProviderService::OnInjectedAffiliationAfterLoginsChanged(
+    std::vector<std::unique_ptr<PasswordForm>> forms) {
+  AddCredentials(std::move(forms));
+  SyncStore(false);
+}
+
+void CredentialProviderService::OnSyncConfigurationCompleted(
+    syncer::SyncService* sync) {
+  RequestSyncAllCredentialsIfNeeded();
 }
diff --git a/ios/chrome/browser/credential_provider/credential_provider_service_factory.mm b/ios/chrome/browser/credential_provider/credential_provider_service_factory.mm
index 774c3970..b5669ac 100644
--- a/ios/chrome/browser/credential_provider/credential_provider_service_factory.mm
+++ b/ios/chrome/browser/credential_provider/credential_provider_service_factory.mm
@@ -11,6 +11,7 @@
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #import "ios/chrome/browser/signin/authentication_service_factory.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
+#include "ios/chrome/browser/sync/profile_sync_service_factory.h"
 #import "ios/chrome/common/credential_provider/archivable_credential_store.h"
 #import "ios/chrome/common/credential_provider/constants.h"
 
@@ -39,6 +40,7 @@
   DependsOn(IOSChromePasswordStoreFactory::GetInstance());
   DependsOn(AuthenticationServiceFactory::GetInstance());
   DependsOn(IdentityManagerFactory::GetInstance());
+  DependsOn(ProfileSyncServiceFactory::GetInstance());
 }
 
 CredentialProviderServiceFactory::~CredentialProviderServiceFactory() = default;
@@ -58,7 +60,10 @@
           initWithFileURL:CredentialProviderSharedArchivableStoreURL()];
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForBrowserState(browser_state);
+  syncer::SyncService* sync_service =
+      ProfileSyncServiceFactory::GetForBrowserState(browser_state);
+
   return std::make_unique<CredentialProviderService>(
       password_store, authentication_service, credential_store,
-      identity_manager);
+      identity_manager, sync_service);
 }
diff --git a/ios/chrome/browser/credential_provider/credential_provider_service_unittest.mm b/ios/chrome/browser/credential_provider/credential_provider_service_unittest.mm
index 34c3ae6..aba3dfc 100644
--- a/ios/chrome/browser/credential_provider/credential_provider_service_unittest.mm
+++ b/ios/chrome/browser/credential_provider/credential_provider_service_unittest.mm
@@ -63,7 +63,7 @@
             chrome_browser_state_.get()));
 
     credential_provider_service_ = std::make_unique<CredentialProviderService>(
-        password_store_, auth_service_, credential_store_, nullptr);
+        password_store_, auth_service_, credential_store_, nullptr, nullptr);
   }
 
   void TearDown() override {
@@ -193,4 +193,21 @@
   }));
 }
 
+// Test that CredentialProviderService observes changes in the password store.
+TEST_F(CredentialProviderServiceTest, AndroidCredential) {
+  EXPECT_EQ(0u, credential_store_.credentials.count);
+
+  PasswordForm form;
+  form.url = GURL(form.signon_realm);
+  form.signon_realm = "android://hash@com.example.my.app";
+  form.password_element = base::ASCIIToUTF16("pwd");
+  form.password_value = base::ASCIIToUTF16("example");
+
+  password_store_->AddLogin(form);
+  task_environment_.RunUntilIdle();
+
+  // Expect the store to be populated with 1 credential.
+  ASSERT_EQ(1u, credential_store_.credentials.count);
+}
+
 }  // namespace
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index e00fe0b..269c63e 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -365,6 +365,10 @@
      flag_descriptions::kWebPageTextAccessibilityName,
      flag_descriptions::kWebPageTextAccessibilityDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(web::kWebPageTextAccessibility)},
+    {"webpage-alternative-text-zoom",
+     flag_descriptions::kWebPageAlternativeTextZoomName,
+     flag_descriptions::kWebPageAlternativeTextZoomDescription,
+     flags_ui::kOsIos, FEATURE_VALUE_TYPE(web::kWebPageAlternativeTextZoom)},
     {"toolbar-container", flag_descriptions::kToolbarContainerName,
      flag_descriptions::kToolbarContainerDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(toolbar_container::kToolbarContainerEnabled)},
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 9ad97b6..26cd7d8 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -510,6 +510,11 @@
     "When enabled, text in web pages will respect the user's Dynamic Type "
     "setting.";
 
+const char kWebPageAlternativeTextZoomName[] =
+    "Use different method for zooming web pages";
+const char kWebPageAlternativeTextZoomDescription[] =
+    "When enabled, switches the method used to zoom web pages.";
+
 const char kWellKnownChangePasswordName[] =
     "Support for .well-known/change-password";
 const char kWellKnownChangePasswordDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 7fcf0e0..34268d5d 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -453,6 +453,11 @@
 extern const char kWebPageTextAccessibilityName[];
 extern const char kWebPageTextAccessibilityDescription[];
 
+// Title and description for the flag to enable a different method of zooming
+// web pages.
+extern const char kWebPageAlternativeTextZoomName[];
+extern const char kWebPageAlternativeTextZoomDescription[];
+
 extern const char kWellKnownChangePasswordName[];
 extern const char kWellKnownChangePasswordDescription[];
 
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/confirm_infobar_banner_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/confirm_infobar_banner_interaction_handler.mm
index a87f3d82..53b75eae 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/confirm_infobar_banner_interaction_handler.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/confirm_infobar_banner_interaction_handler.mm
@@ -26,7 +26,8 @@
 
 void ConfirmInfobarBannerInteractionHandler::MainButtonTapped(
     InfoBarIOS* infobar) {
-  infobar->set_accepted(GetInfobarDelegate(infobar)->Accept());
+  // Confirm Infobars don't need to update badge status.
+  GetInfobarDelegate(infobar)->Accept();
 }
 
 void ConfirmInfobarBannerInteractionHandler::BannerVisibilityChanged(
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/confirm_infobar_banner_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/confirm_infobar_banner_interaction_handler_unittest.mm
index 2c54cc6..fa09a429 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/confirm_infobar_banner_interaction_handler_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/confirm_infobar_banner_interaction_handler_unittest.mm
@@ -52,13 +52,10 @@
   InfoBarIOS* infobar_;
 };
 
-// Tests MainButtonTapped() calls Accept() on the mock delegate and resets
-// the infobar to be accepted.
+// Tests MainButtonTapped() calls Accept() on the mock delegate.
 TEST_F(ConfirmInfobarBannerInteractionHandlerTest, MainButton) {
-  ASSERT_FALSE(infobar_->accepted());
   EXPECT_CALL(mock_delegate(), Accept()).WillOnce(testing::Return(true));
   handler_.MainButtonTapped(infobar_);
-  EXPECT_TRUE(infobar_->accepted());
 }
 
 // Tests that BannerVisibilityChanged() InfobarDismissed() on the mock delegate.
diff --git a/ios/chrome/browser/main/browser_agent_util.mm b/ios/chrome/browser/main/browser_agent_util.mm
index cb6d0e3..228123e9 100644
--- a/ios/chrome/browser/main/browser_agent_util.mm
+++ b/ios/chrome/browser/main/browser_agent_util.mm
@@ -42,6 +42,12 @@
   DeviceSharingBrowserAgent::CreateForBrowser(browser);
   UrlLoadingNotifierBrowserAgent::CreateForBrowser(browser);
   AppLauncherBrowserAgent::CreateForBrowser(browser);
+
+  // SessionRestorartionAgent requires WebUsageEnablerBrowserAgent.
+  SessionRestorationBrowserAgent::CreateForBrowser(
+      browser, [SessionServiceIOS sharedService]);
+
+  // WebStateListMetricsBrowserAgent requires SessionRestorationBrowserAgent.
   WebStateListMetricsBrowserAgent::CreateForBrowser(browser);
   ClosingWebStateObserverBrowserAgent::CreateForBrowser(browser);
   SnapshotBrowserAgent::CreateForBrowser(browser);
@@ -53,10 +59,6 @@
   // UrlLoadingBrowserAgent requires UrlLoadingNotifierBrowserAgent.
   UrlLoadingBrowserAgent::CreateForBrowser(browser);
 
-  // SessionRestorartionAgent requires WebUsageEnablerBrowserAgent.
-  SessionRestorationBrowserAgent::CreateForBrowser(
-      browser, [SessionServiceIOS sharedService]);
-
   // TabUsageRecorderBrowserAgent and WebStateListMetricsBrowserAgent observe
   // the SessionRestorationBrowserAgent, so they should be created after the the
   // SessionRestorationBrowserAgent is created.
diff --git a/ios/chrome/browser/open_in/BUILD.gn b/ios/chrome/browser/open_in/BUILD.gn
index 91c30d21..fe01f6d 100644
--- a/ios/chrome/browser/open_in/BUILD.gn
+++ b/ios/chrome/browser/open_in/BUILD.gn
@@ -38,6 +38,7 @@
   testonly = true
   sources = [ "open_in_tab_helper_unittest.mm" ]
   deps = [
+    ":features",
     ":open_in",
     "//base",
     "//base/test:test_support",
diff --git a/ios/chrome/browser/open_in/open_in_tab_helper.h b/ios/chrome/browser/open_in/open_in_tab_helper.h
index 92e97b05..409b496 100644
--- a/ios/chrome/browser/open_in/open_in_tab_helper.h
+++ b/ios/chrome/browser/open_in/open_in_tab_helper.h
@@ -14,6 +14,43 @@
 class HttpResponseHeaders;
 }  //  namespace net
 
+namespace content_type {
+
+// .pptx extension.
+extern const char kMimeTypeMicrosoftPowerPointOpenXML[];
+
+// .docx extension.
+extern const char kMimeTypeMicrosoftWordOpenXML[];
+
+// .xlsx extension.
+extern const char kMimeTypeMicrosoftExcelOpenXML[];
+
+// .pdf extension.
+extern const char kMimeTypePDF[];
+
+// .doc extension.
+extern const char kMimeTypeMicrosoftWord[];
+
+// .jpeg or .jpg extension.
+extern const char kMimeTypeJPEG[];
+
+// .png extension.
+extern const char kMimeTypePNG[];
+
+// .ppt extension.
+extern const char kMimeTypeMicrosoftPowerPoint[];
+
+// .rtf extension.
+extern const char kMimeTypeRTF[];
+
+// .svg extension.
+extern const char kMimeTypeSVG[];
+
+// .xls extension.
+extern const char kMimeTypeMicrosoftExcel[];
+
+}  // namespace content_type
+
 // Enum used to determine the MIME type of a previewed file. Entries should
 // always keep synced with the IOS.OpenIn.MimeType UMA histogram.
 enum class OpenInMimeType {
diff --git a/ios/chrome/browser/open_in/open_in_tab_helper.mm b/ios/chrome/browser/open_in/open_in_tab_helper.mm
index de6767c0..763de7eb 100644
--- a/ios/chrome/browser/open_in/open_in_tab_helper.mm
+++ b/ios/chrome/browser/open_in/open_in_tab_helper.mm
@@ -25,45 +25,34 @@
 #error "This file requires ARC support."
 #endif
 
-namespace {
+namespace content_type {
 
-// .pptx extension.
 const char kMimeTypeMicrosoftPowerPointOpenXML[] =
     "application/vnd.openxmlformats-officedocument.presentationml.presentation";
 
-// .docx extension.
 const char kMimeTypeMicrosoftWordOpenXML[] =
     "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
 
-// .xlsx extension.
 const char kMimeTypeMicrosoftExcelOpenXML[] =
     "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
 
-// .pdf extension.
 const char kMimeTypePDF[] = "application/pdf";
 
-// .doc extension.
 const char kMimeTypeMicrosoftWord[] = "application/msword";
 
-// .jpeg or .jpg extension.
 const char kMimeTypeJPEG[] = "image/jpeg";
 
-// .png extension.
 const char kMimeTypePNG[] = "image/png";
 
-// .ppt extension.
 const char kMimeTypeMicrosoftPowerPoint[] = "application/vnd.ms-powerpoint";
 
-// .rtf extension.
 const char kMimeTypeRTF[] = "application/rtf";
 
-// .svg extension.
 const char kMimeTypeSVG[] = "image/svg+xml";
 
-// .xls extension.
 const char kMimeTypeMicrosoftExcel[] = "application/vnd.ms-excel";
 
-}  // namespace
+}  // namespace content_type
 
 // static
 void OpenInTabHelper::CreateForWebState(web::WebState* web_state) {
@@ -89,27 +78,27 @@
 
 OpenInMimeType OpenInTabHelper::GetUmaResult(
     const std::string& mime_type) const {
-  if (mime_type == kMimeTypePDF)
+  if (mime_type == content_type::kMimeTypePDF)
     return OpenInMimeType::kMimeTypePDF;
-  if (mime_type == kMimeTypeMicrosoftWord)
+  if (mime_type == content_type::kMimeTypeMicrosoftWord)
     return OpenInMimeType::kMimeTypeMicrosoftWord;
-  if (mime_type == kMimeTypeMicrosoftWordOpenXML)
+  if (mime_type == content_type::kMimeTypeMicrosoftWordOpenXML)
     return OpenInMimeType::kMimeTypeMicrosoftWordOpenXML;
-  if (mime_type == kMimeTypeJPEG)
+  if (mime_type == content_type::kMimeTypeJPEG)
     return OpenInMimeType::kMimeTypeJPEG;
-  if (mime_type == kMimeTypePNG)
+  if (mime_type == content_type::kMimeTypePNG)
     return OpenInMimeType::kMimeTypePNG;
-  if (mime_type == kMimeTypeMicrosoftPowerPoint)
+  if (mime_type == content_type::kMimeTypeMicrosoftPowerPoint)
     return OpenInMimeType::kMimeTypeMicrosoftPowerPoint;
-  if (mime_type == kMimeTypeMicrosoftPowerPointOpenXML)
+  if (mime_type == content_type::kMimeTypeMicrosoftPowerPointOpenXML)
     return OpenInMimeType::kMimeTypeMicrosoftPowerPointOpenXML;
-  if (mime_type == kMimeTypeRTF)
+  if (mime_type == content_type::kMimeTypeRTF)
     return OpenInMimeType::kMimeTypeRTF;
-  if (mime_type == kMimeTypeSVG)
+  if (mime_type == content_type::kMimeTypeSVG)
     return OpenInMimeType::kMimeTypeSVG;
-  if (mime_type == kMimeTypeMicrosoftExcel)
+  if (mime_type == content_type::kMimeTypeMicrosoftExcel)
     return OpenInMimeType::kMimeTypeMicrosoftExcel;
-  if (mime_type == kMimeTypeMicrosoftExcelOpenXML)
+  if (mime_type == content_type::kMimeTypeMicrosoftExcelOpenXML)
     return OpenInMimeType::kMimeTypeMicrosoftExcelOpenXML;
   return OpenInMimeType::kMimeTypeNotHandled;
 }
diff --git a/ios/chrome/browser/open_in/open_in_tab_helper_unittest.mm b/ios/chrome/browser/open_in/open_in_tab_helper_unittest.mm
index 40e82088..bf90da3 100644
--- a/ios/chrome/browser/open_in/open_in_tab_helper_unittest.mm
+++ b/ios/chrome/browser/open_in/open_in_tab_helper_unittest.mm
@@ -8,6 +8,8 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/strings/sys_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#import "ios/chrome/browser/open_in/features.h"
 #import "ios/chrome/browser/open_in/open_in_tab_helper_delegate.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/web/public/navigation/navigation_item.h"
@@ -56,18 +58,110 @@
 
 namespace {
 
-const char kContentDispositionWithFileName[] =
-    "attachment; filename=\"suggested_filename.pdf\"";
-const char kPdfContentType[] = "application/pdf";
 const char kInvalidFileNameUrl[] = "https://test.test/";
-const char kValidFileNameUrl[] = "https://test.test/file_name.pdf";
 const char kContentDispositionWithoutFileName[] =
     "attachment; parameter=parameter_value";
 const char kHtmlContentType[] = "text/html";
+const char kValidFileNamePDF[] = "https://test.test/file_name.pdf";
+const char kValidFileNameMicrosoftPowerPointOpenXML[] =
+    "https://test.test/file_name.pptx";
+
+// Returns the content type according to the current testing value.
+std::string ContentTypeForMimeType(OpenInMimeType parameter) {
+  switch (parameter) {
+    case OpenInMimeType::kMimeTypeMicrosoftPowerPointOpenXML:
+      return content_type::kMimeTypeMicrosoftPowerPointOpenXML;
+      break;
+    case OpenInMimeType::kMimeTypeMicrosoftWordOpenXML:
+      return content_type::kMimeTypeMicrosoftWordOpenXML;
+      break;
+    case OpenInMimeType::kMimeTypeMicrosoftExcelOpenXML:
+      return content_type::kMimeTypeMicrosoftExcelOpenXML;
+      break;
+    case OpenInMimeType::kMimeTypePDF:
+      return content_type::kMimeTypePDF;
+      break;
+    case OpenInMimeType::kMimeTypeMicrosoftWord:
+      return content_type::kMimeTypeMicrosoftWord;
+      break;
+    case OpenInMimeType::kMimeTypeJPEG:
+      return content_type::kMimeTypeJPEG;
+      break;
+    case OpenInMimeType::kMimeTypePNG:
+      return content_type::kMimeTypePNG;
+      break;
+    case OpenInMimeType::kMimeTypeMicrosoftPowerPoint:
+      return content_type::kMimeTypeMicrosoftPowerPoint;
+      break;
+    case OpenInMimeType::kMimeTypeRTF:
+      return content_type::kMimeTypeRTF;
+      break;
+    case OpenInMimeType::kMimeTypeSVG:
+      return content_type::kMimeTypeSVG;
+      break;
+    case OpenInMimeType::kMimeTypeMicrosoftExcel:
+      return content_type::kMimeTypeMicrosoftExcel;
+      break;
+    // Should not be reached.
+    case OpenInMimeType::kMimeTypeNotHandled:
+      return "";
+      break;
+  }
+}
+
+// Returns the file extension according to the current testing value.
+std::string ExtensionForMimeType(OpenInMimeType parameter) {
+  switch (parameter) {
+    case OpenInMimeType::kMimeTypeMicrosoftPowerPointOpenXML:
+      return ".pptx";
+      break;
+    case OpenInMimeType::kMimeTypeMicrosoftWordOpenXML:
+      return ".docx";
+      break;
+    case OpenInMimeType::kMimeTypeMicrosoftExcelOpenXML:
+      return ".xlsx";
+      break;
+    case OpenInMimeType::kMimeTypePDF:
+      return ".pdf";
+      break;
+    case OpenInMimeType::kMimeTypeMicrosoftWord:
+      return ".doc";
+      break;
+    case OpenInMimeType::kMimeTypeJPEG:
+      return ".jpeg";
+      break;
+    case OpenInMimeType::kMimeTypePNG:
+      return ".png";
+      break;
+    case OpenInMimeType::kMimeTypeMicrosoftPowerPoint:
+      return ".ppt";
+      break;
+    case OpenInMimeType::kMimeTypeRTF:
+      return ".rtf";
+      break;
+    case OpenInMimeType::kMimeTypeSVG:
+      return ".svg";
+      break;
+    case OpenInMimeType::kMimeTypeMicrosoftExcel:
+      return ".xls";
+      break;
+    // Should not be reached.
+    case OpenInMimeType::kMimeTypeNotHandled:
+      return "";
+      break;
+  }
+}
+
+// Returns the file name according to the current testing value.
+std::string FileNameForMimeType(OpenInMimeType parameter) {
+  return "filename" + ExtensionForMimeType(parameter);
+}
 }  // namespace
 
 // Test fixture for OpenInTabHelper class.
-class OpenInTabHelperTest : public PlatformTest {
+class OpenInTabHelperTest
+    : public PlatformTest,
+      public ::testing::WithParamInterface<OpenInMimeType> {
  protected:
   OpenInTabHelperTest()
       : delegate_([[FakeOpenInTabHelperDelegate alloc] init]) {
@@ -130,55 +224,116 @@
   EXPECT_TRUE(delegate_.openInDestroyed);
 }
 
-// Tests that openIn is enabled for PDF documents and that it uses the file name
-// from the content desposition key in the response headers.
-TEST_F(OpenInTabHelperTest, OpenInForPDFWithFileNameFromContentDesposition) {
+// Tests that openIn is enabled for exportable files and that it uses the file
+// name from the content desposition key in the response headers.
+TEST_P(OpenInTabHelperTest,
+       OpenInForExportableFilesWithFileNameFromContentDesposition) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(kExtendOpenInFilesSupport);
   ASSERT_FALSE(delegate_.openInDisabled);
 
-  GURL url(kValidFileNameUrl);
-  NavigateTo(url, kPdfContentType, kContentDispositionWithFileName);
+  const std::string file_name =
+      FileNameForMimeType(OpenInTabHelperTest::GetParam());
+  GURL url("https://test.test/" + file_name);
+
+  NavigateTo(url,
+             ContentTypeForMimeType(OpenInTabHelperTest::GetParam()).c_str(),
+             ("attachment; filename=\"suggested_" + file_name + "\"").c_str());
+
   EXPECT_FALSE(delegate_.openInDisabled);
   EXPECT_EQ(url, delegate_.lastOpenedDocumentURL);
-  EXPECT_NSEQ(@"suggested_filename.pdf", delegate_.lastSuggestedFileName);
+
+  std::string suggested_file_name = "suggested_" + file_name;
+  EXPECT_NSEQ(base::SysUTF8ToNSString(suggested_file_name),
+              delegate_.lastSuggestedFileName);
 }
 
-// Tests that openIn is enabled for PDF documents and that it uses the file name
-// from the URL if the content desposition key in the response headers doesn't
-// have file name.
-TEST_F(OpenInTabHelperTest, OpenInForPDFWithFileNameFromURL) {
+// Tests that openIn is enabled for exportable files and that it uses the file
+// name from the URL if the content desposition key in the response headers
+// doesn't have file name.
+TEST_P(OpenInTabHelperTest, OpenInForExportableFilesWithFileNameFromURL) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(kExtendOpenInFilesSupport);
   ASSERT_FALSE(delegate_.openInDisabled);
 
-  GURL url(kValidFileNameUrl);
-  NavigateTo(url, kPdfContentType, kContentDispositionWithoutFileName);
-  EXPECT_FALSE(delegate_.openInDisabled);
+  const std::string file_name =
+      FileNameForMimeType(OpenInTabHelperTest::GetParam());
+  GURL url("https://test.test/" + file_name);
+  NavigateTo(url,
+             ContentTypeForMimeType(OpenInTabHelperTest::GetParam()).c_str(),
+             kContentDispositionWithoutFileName);
 
+  EXPECT_FALSE(delegate_.openInDisabled);
   EXPECT_EQ(url, delegate_.lastOpenedDocumentURL);
-  EXPECT_NSEQ(@"file_name.pdf", delegate_.lastSuggestedFileName);
+
+  EXPECT_NSEQ(base::SysUTF8ToNSString(file_name),
+              delegate_.lastSuggestedFileName);
 }
 
-// Tests that openIn is enabled for PDF documents and that it uses the default
-// file name if neither the URL nor the content desposition key in the response
-// headers has a file name.
-TEST_F(OpenInTabHelperTest, OpenInForPDFWithDefaultFileName) {
+// Tests that openIn is enabled for exportable files and that it uses the
+// default file name if neither the URL nor the content desposition key in the
+// response headers has a file name.
+TEST_P(OpenInTabHelperTest, OpenInForExportableFilesWithDefaultFileName) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(kExtendOpenInFilesSupport);
   ASSERT_FALSE(delegate_.openInDisabled);
 
   GURL url(kInvalidFileNameUrl);
-  NavigateTo(url, kPdfContentType, kContentDispositionWithoutFileName);
+  NavigateTo(url,
+             ContentTypeForMimeType(OpenInTabHelperTest::GetParam()).c_str(),
+             kContentDispositionWithoutFileName);
+
   EXPECT_FALSE(delegate_.openInDisabled);
   EXPECT_EQ(url, delegate_.lastOpenedDocumentURL);
+
   std::string default_file_name =
-      l10n_util::GetStringUTF8(IDS_IOS_OPEN_IN_FILE_DEFAULT_TITLE) + ".pdf";
+      l10n_util::GetStringUTF8(IDS_IOS_OPEN_IN_FILE_DEFAULT_TITLE) +
+      ExtensionForMimeType(OpenInTabHelperTest::GetParam());
+
+  // .rtf files return a default name without extension.
+  if (OpenInTabHelperTest::GetParam() == OpenInMimeType::kMimeTypeRTF) {
+    default_file_name =
+        l10n_util::GetStringUTF8(IDS_IOS_OPEN_IN_FILE_DEFAULT_TITLE);
+  }
 
   EXPECT_NSEQ(base::SysUTF8ToNSString(default_file_name),
               delegate_.lastSuggestedFileName);
 }
 
-// Tests that openIn is disabled for non PDF documents.
-TEST_F(OpenInTabHelperTest, OpenInDisabledForNonPDF) {
+// Tests that openIn is disabled for non exportable files.
+TEST_F(OpenInTabHelperTest, OpenInDisabledForNonExportableFiles) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(kExtendOpenInFilesSupport);
   ASSERT_FALSE(delegate_.openInDisabled);
-  GURL url(kValidFileNameUrl);
-  NavigateTo(url, kHtmlContentType, kContentDispositionWithoutFileName);
+
+  // Testing PDF.
+  GURL url_pdf(kValidFileNamePDF);
+  NavigateTo(url_pdf, kHtmlContentType, kContentDispositionWithoutFileName);
+
+  EXPECT_EQ(GURL::EmptyGURL(), delegate_.lastOpenedDocumentURL);
+  EXPECT_FALSE(delegate_.lastSuggestedFileName);
+  EXPECT_TRUE(delegate_.openInDisabled);
+
+  // Testing Microsoft PowerPoint OpenXML.
+  GURL url_pptx(kValidFileNameMicrosoftPowerPointOpenXML);
+  NavigateTo(url_pptx, kHtmlContentType, kContentDispositionWithoutFileName);
+
   EXPECT_EQ(GURL::EmptyGURL(), delegate_.lastOpenedDocumentURL);
   EXPECT_FALSE(delegate_.lastSuggestedFileName);
   EXPECT_TRUE(delegate_.openInDisabled);
 }
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    OpenInTabHelperTest,
+    ::testing::Values(OpenInMimeType::kMimeTypePDF,
+                      OpenInMimeType::kMimeTypeMicrosoftWord,
+                      OpenInMimeType::kMimeTypeMicrosoftWordOpenXML,
+                      OpenInMimeType::kMimeTypeJPEG,
+                      OpenInMimeType::kMimeTypePNG,
+                      OpenInMimeType::kMimeTypeMicrosoftPowerPoint,
+                      OpenInMimeType::kMimeTypeMicrosoftPowerPointOpenXML,
+                      OpenInMimeType::kMimeTypeRTF,
+                      OpenInMimeType::kMimeTypeSVG,
+                      OpenInMimeType::kMimeTypeMicrosoftExcel,
+                      OpenInMimeType::kMimeTypeMicrosoftExcelOpenXML));
diff --git a/ios/chrome/browser/policy/BUILD.gn b/ios/chrome/browser/policy/BUILD.gn
index 00ad941..09ac1b6 100644
--- a/ios/chrome/browser/policy/BUILD.gn
+++ b/ios/chrome/browser/policy/BUILD.gn
@@ -175,6 +175,9 @@
     "//ios/chrome/browser:utils",
     "//ios/chrome/browser/translate:eg_test_support+eg2",
     "//ios/chrome/browser/ui/popup_menu:constants",
+    "//ios/chrome/browser/ui/settings:constants",
+    "//ios/chrome/browser/ui/settings/elements:constants",
+    "//ios/chrome/browser/ui/table_view/cells:cells_constants",
     "//ios/chrome/test/earl_grey:eg_test_support+eg2",
     "//ios/testing/earl_grey:eg_test_support+eg2",
     "//ios/third_party/earl_grey2:test_lib",
diff --git a/ios/chrome/browser/policy/policy_egtest.mm b/ios/chrome/browser/policy/policy_egtest.mm
index 9ed61ebe..556cd30 100644
--- a/ios/chrome/browser/policy/policy_egtest.mm
+++ b/ios/chrome/browser/policy/policy_egtest.mm
@@ -14,6 +14,9 @@
 #include "ios/chrome/browser/pref_names.h"
 #import "ios/chrome/browser/translate/translate_app_interface.h"
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
+#import "ios/chrome/browser/ui/settings/elements/elements_constants.h"
+#import "ios/chrome/browser/ui/settings/settings_table_view_controller_constants.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #include "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
@@ -121,11 +124,34 @@
 }
 
 // Tests for the DefaultSearchProviderEnabled policy.
+// 1. Test if the policy can be properly set.
+// 2. Test the managed UI item and clicking action.
 - (void)testDefaultSearchProviderEnabled {
   // Disable default search provider via policy and make sure it does not crash
   // the omnibox UI.
   SetPolicy(false, policy::key::kDefaultSearchProviderEnabled);
   [ChromeEarlGrey loadURL:GURL("chrome://policy")];
+  [EarlGrey dismissKeyboardWithError:nil];
+
+  // Open settings menu and check if the settings UI is a managed UI.
+  [ChromeEarlGreyUI openSettingsMenu];
+
+  // Click on the info button of the managed item.
+  [ChromeEarlGreyUI tapSettingsMenuButton:grey_accessibilityID(
+                                              kTableViewCellInfoButtonViewId)];
+
+  // Check if the contextual bubble is shown.
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
+                                          kEnterpriseInfoBubbleViewId)]
+      assertWithMatcher:grey_sufficientlyVisible()];
+
+  // Tap outside of the bubble.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsDoneButton()]
+      performAction:grey_tap()];
+  // Check if the contextual bubble is hidden.
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
+                                          kEnterpriseInfoBubbleViewId)]
+      assertWithMatcher:grey_notVisible()];
 }
 
 // Tests for the PasswordManagerEnabled policy.
diff --git a/ios/chrome/browser/ui/recent_tabs/BUILD.gn b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
index 38b4c1d..887efa4 100644
--- a/ios/chrome/browser/ui/recent_tabs/BUILD.gn
+++ b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
@@ -77,6 +77,7 @@
     "//components/sessions",
     "//components/strings",
     "//components/sync",
+    "//ios/chrome/app:tests_hook",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/drag_and_drop",
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
index 42e1305..ba23a30b 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
@@ -63,15 +63,24 @@
   [ChromeEarlGreyUI tapToolsMenuButton:RecentTabsMenuButton()];
 }
 
+// Returns the matcher for the Recent Tabs table.
+id<GREYMatcher> RecentTabsTable() {
+  return grey_accessibilityID(
+      kRecentTabsTableViewControllerAccessibilityIdentifier);
+}
+
 // Returns the matcher for the entry of the page in the recent tabs panel.
 id<GREYMatcher> TitleOfTestPage() {
   return grey_allOf(
-      grey_ancestor(grey_accessibilityID(
-          kRecentTabsTableViewControllerAccessibilityIdentifier)),
+      grey_ancestor(RecentTabsTable()),
       chrome_test_util::StaticTextWithAccessibilityLabel(kTitleOfTestPage),
       grey_sufficientlyVisible(), nil);
 }
 
+GURL TestPageURL() {
+  return web::test::HttpServer::MakeUrl(kURLOfTestPage);
+}
+
 }  // namespace
 
 // Earl grey integration tests for Recent Tabs Panel Controller.
@@ -84,25 +93,16 @@
   [super setUp];
   [ChromeEarlGrey clearBrowsingHistory];
   web::test::SetUpSimpleHttpServer(std::map<GURL, std::string>{{
-      web::test::HttpServer::MakeUrl(kURLOfTestPage),
+      TestPageURL(),
       std::string(kHTMLOfTestPage),
   }});
   [RecentTabsAppInterface clearCollapsedListViewSectionStates];
 }
 
-// Closes the recent tabs panel.
-- (void)closeRecentTabs {
-  id<GREYMatcher> exitMatcher =
-      grey_accessibilityID(kTableViewNavigationDismissButtonId);
-  [[EarlGrey selectElementWithMatcher:exitMatcher] performAction:grey_tap()];
-  // Wait until the recent tabs panel is dismissed.
-  [ChromeEarlGreyUI waitForAppToIdle];
-}
-
 // Tests that a closed tab appears in the Recent Tabs panel, and that tapping
 // the entry in the Recent Tabs panel re-opens the closed tab.
 - (void)testClosedTabAppearsInRecentTabsPanel {
-  const GURL testPageURL = web::test::HttpServer::MakeUrl(kURLOfTestPage);
+  const GURL testPageURL = TestPageURL();
 
   // Open the test page in a new tab.
   [ChromeEarlGrey loadURL:testPageURL];
@@ -170,10 +170,8 @@
 
   // Scroll to sign-in promo, if applicable.
   [[EarlGrey
-      selectElementWithMatcher:
-          grey_allOf(grey_accessibilityID(
-                         kRecentTabsTableViewControllerAccessibilityIdentifier),
-                     grey_sufficientlyVisible(), nil)]
+      selectElementWithMatcher:grey_allOf(RecentTabsTable(),
+                                          grey_sufficientlyVisible(), nil)]
       performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];
 
   // Sign-in promo should be visible with cold state.
@@ -197,10 +195,8 @@
 
   // Scroll to sign-in promo, if applicable
   [[EarlGrey
-      selectElementWithMatcher:
-          grey_allOf(grey_accessibilityID(
-                         kRecentTabsTableViewControllerAccessibilityIdentifier),
-                     grey_sufficientlyVisible(), nil)]
+      selectElementWithMatcher:grey_allOf(RecentTabsTable(),
+                                          grey_sufficientlyVisible(), nil)]
       performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];
 
   [SigninEarlGreyUI
@@ -226,10 +222,8 @@
       performAction:grey_tap()];
   // Scroll to sign-in promo, if applicable
   [[EarlGrey
-      selectElementWithMatcher:
-          grey_allOf(grey_accessibilityID(
-                         kRecentTabsTableViewControllerAccessibilityIdentifier),
-                     grey_sufficientlyVisible(), nil)]
+      selectElementWithMatcher:grey_allOf(RecentTabsTable(),
+                                          grey_sufficientlyVisible(), nil)]
       performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];
   [SigninEarlGreyUI
       verifySigninPromoVisibleWithMode:SigninPromoViewModeWarmState
@@ -253,9 +247,7 @@
   OpenRecentTabsPanel();
 
   id<GREYMatcher> recentTabsViewController =
-      grey_allOf(grey_accessibilityID(
-                     kRecentTabsTableViewControllerAccessibilityIdentifier),
-                 grey_sufficientlyVisible(), nil);
+      grey_allOf(RecentTabsTable(), grey_sufficientlyVisible(), nil);
 
   // Check that the TableView is presented.
   [[EarlGrey selectElementWithMatcher:recentTabsViewController]
@@ -298,7 +290,7 @@
   }
 }
 
-// Test that the Cold Mode Signin promo is visible in the Other Devices section
+// Tests that the Cold Mode Signin promo is visible in the Other Devices section
 // (and with illustrated-empty-states enabled, there is the illustrated cell)
 - (void)testOtherDevicesDefaultEmptyState {
   OpenRecentTabsPanel();
@@ -317,10 +309,8 @@
 
   // Scroll to sign-in promo, if applicable
   [[EarlGrey
-      selectElementWithMatcher:
-          grey_allOf(grey_accessibilityID(
-                         kRecentTabsTableViewControllerAccessibilityIdentifier),
-                     grey_sufficientlyVisible(), nil)]
+      selectElementWithMatcher:grey_allOf(RecentTabsTable(),
+                                          grey_sufficientlyVisible(), nil)]
       performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];
 
   [SigninEarlGreyUI
@@ -328,4 +318,82 @@
                            closeButton:NO];
 }
 
+// Tests the Copy Link action on a recent tab's context menu.
+- (void)testContextMenuCopyLink {
+  if (![ChromeEarlGrey isNativeContextMenusEnabled]) {
+    EARL_GREY_TEST_SKIPPED(@"Test disabled on when feature flag is off.");
+  }
+
+  [self loadTestURL];
+  OpenRecentTabsPanel();
+  [self longPressTestURLTab];
+
+  GURL testURL = TestPageURL();
+  [ChromeEarlGrey
+      verifyCopyLinkActionWithText:[NSString stringWithUTF8String:testURL.spec()
+                                                                      .c_str()]
+                      useNewString:YES];
+}
+
+// Tests the Open in New Tab action on a recent tab's context menu.
+- (void)testContextMenuOpenInNewTab {
+  if (![ChromeEarlGrey isNativeContextMenusEnabled]) {
+    EARL_GREY_TEST_SKIPPED(@"Test disabled on when feature flag is off.");
+  }
+
+  [self loadTestURL];
+  OpenRecentTabsPanel();
+  [self longPressTestURLTab];
+
+  [ChromeEarlGrey verifyOpenInNewTabActionWithURL:TestPageURL().GetContent()];
+
+  // Verify that Recent Tabs closed.
+  [[EarlGrey selectElementWithMatcher:RecentTabsTable()]
+      assertWithMatcher:grey_notVisible()];
+}
+
+// Tests the Share action on a recent tab's context menu.
+- (void)testContextMenuShare {
+  if (![ChromeEarlGrey isNativeContextMenusEnabled]) {
+    EARL_GREY_TEST_SKIPPED(@"Test disabled on when feature flag is off.");
+  }
+
+  [self loadTestURL];
+  OpenRecentTabsPanel();
+  [self longPressTestURLTab];
+
+  [ChromeEarlGrey verifyShareActionWithPageTitle:kTitleOfTestPage];
+}
+
+#pragma mark Helper Methods
+
+// Opens a new tab and closes it, to make sure it appears as a recently closed
+// tab.
+- (void)loadTestURL {
+  const GURL testPageURL = web::test::HttpServer::MakeUrl(kURLOfTestPage);
+
+  // Open the test page in a new tab.
+  [ChromeEarlGrey loadURL:testPageURL];
+  [ChromeEarlGrey waitForWebStateContainingText:"hello"];
+
+  // Close the tab, making it appear in Recent Tabs.
+  [ChromeEarlGrey closeCurrentTab];
+}
+
+// Long-presses on a recent tab entry.
+- (void)longPressTestURLTab {
+  // The test page may be there multiple times.
+  [[[EarlGrey selectElementWithMatcher:TitleOfTestPage()] atIndex:0]
+      performAction:grey_longPress()];
+}
+
+// Closes the recent tabs panel.
+- (void)closeRecentTabs {
+  id<GREYMatcher> exitMatcher =
+      grey_accessibilityID(kTableViewNavigationDismissButtonId);
+  [[EarlGrey selectElementWithMatcher:exitMatcher] performAction:grey_tap()];
+  // Wait until the recent tabs panel is dismissed.
+  [ChromeEarlGreyUI waitForAppToIdle];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
index dcde65f5..feb72aa9 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
@@ -16,6 +16,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "components/sync_sessions/open_tabs_ui_delegate.h"
 #include "components/sync_sessions/session_sync_service.h"
+#import "ios/chrome/app/tests_hook.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/drag_and_drop/drag_and_drop_flag.h"
 #import "ios/chrome/browser/drag_and_drop/drag_item_util.h"
@@ -182,7 +183,11 @@
     self.dragDropHandler.origin = WindowActivityRecentTabsOrigin;
     self.dragDropHandler.dragDataSource = self;
     self.tableView.dragDelegate = self.dragDropHandler;
-    self.tableView.dragInteractionEnabled = YES;
+
+    // TODO(crbug.com/1129058): Clean this up when EarlGrey allows interacting
+    // with context menus that can be dragged.
+    self.tableView.dragInteractionEnabled =
+        !tests_hook::DisableTableDragAndDrop();
   }
 }
 
diff --git a/ios/chrome/browser/ui/settings/elements/BUILD.gn b/ios/chrome/browser/ui/settings/elements/BUILD.gn
index 68c7e18..e084bd0 100644
--- a/ios/chrome/browser/ui/settings/elements/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/elements/BUILD.gn
@@ -15,6 +15,7 @@
   frameworks = [ "UIKit.framework" ]
 
   deps = [
+    ":constants",
     "//base:base",
     "//ios/chrome/app/strings",
     "//ios/chrome/common:common",
@@ -24,3 +25,11 @@
     "//ui/base",
   ]
 }
+
+source_set("constants") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "elements_constants.h",
+    "elements_constants.mm",
+  ]
+}
diff --git a/ios/chrome/browser/ui/settings/elements/elements_constants.h b/ios/chrome/browser/ui/settings/elements/elements_constants.h
new file mode 100644
index 0000000..f256b1e90
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/elements/elements_constants.h
@@ -0,0 +1,13 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_ELEMENTS_ELEMENTS_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_ELEMENTS_ELEMENTS_CONSTANTS_H_
+
+#import <Foundation/Foundation.h>
+
+// The accessibility identifier of the enterpise info bubble view.
+extern NSString* const kEnterpriseInfoBubbleViewId;
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_ELEMENTS_ELEMENTS_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/settings/elements/elements_constants.mm b/ios/chrome/browser/ui/settings/elements/elements_constants.mm
new file mode 100644
index 0000000..1f84e8308
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/elements/elements_constants.mm
@@ -0,0 +1,11 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/settings/elements/elements_constants.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+NSString* const kEnterpriseInfoBubbleViewId = @"kEnterpriseInfoBubbleViewId";
diff --git a/ios/chrome/browser/ui/settings/elements/enterprise_info_popover_view_controller.mm b/ios/chrome/browser/ui/settings/elements/enterprise_info_popover_view_controller.mm
index d25630bd..b3f2c7ad 100644
--- a/ios/chrome/browser/ui/settings/elements/enterprise_info_popover_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/elements/enterprise_info_popover_view_controller.mm
@@ -6,6 +6,7 @@
 
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/ui/settings/elements/elements_constants.h"
 #import "ios/chrome/common/string_util.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/elements/popover_label_view_controller.h"
@@ -102,6 +103,13 @@
                    secondaryAttributedString:SecondaryMessage(enterpriseName)];
 }
 
+#pragma mark - UIViewController
+
+- (void)viewDidLoad {
+  [super viewDidLoad];
+  self.view.accessibilityIdentifier = kEnterpriseInfoBubbleViewId;
+}
+
 #pragma mark - UIPopoverPresentationControllerDelegate
 
 - (void)popoverPresentationControllerDidDismissPopover:
diff --git a/ios/chrome/browser/ui/settings/safety_check/BUILD.gn b/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
index 94b12d64..a6a4aa4 100644
--- a/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
@@ -80,9 +80,9 @@
 source_set("unit_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
-  sources = [ "safety_check_table_view_controller_unittest.mm" ]
+  sources = [ "safety_check_mediator_unittest.mm" ]
   deps = [
-    ":safety_check_ui",
+    ":safety_check",
     "//base/test:test_support",
     "//components/strings",
     "//ios/chrome/app/strings",
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm b/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm
index 3cf2a31..74218e01 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm
@@ -88,7 +88,7 @@
 - (void)start {
   SafetyCheckTableViewController* viewController =
       [[SafetyCheckTableViewController alloc]
-          initWithStyle:UITableViewStylePlain];
+          initWithStyle:UITableViewStyleGrouped];
   self.viewController = viewController;
 
   scoped_refptr<IOSChromePasswordCheckManager> passwordCheckManager =
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm
index c17baa1..4ea058f 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm
@@ -795,8 +795,6 @@
       self.safeBrowsingPreferenceManaged) {
     self.safeBrowsingCheckRowState = SafeBrowsingCheckRowStateManaged;
   }
-  // See if this was the last test.
-  [self resetsCheckStartItemIfNeeded];
 
   [self reconfigureSafeBrowsingCheckItem];
 }
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_unittest.mm
similarity index 64%
rename from ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller_unittest.mm
rename to ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_unittest.mm
index 07bf7af..d37ae494 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.h"
 
 #include <memory>
 
@@ -25,23 +25,6 @@
 
 namespace {
 
-class SafetyCheckTableViewControllerTest
-    : public ChromeTableViewControllerTest {
- protected:
-  ChromeTableViewController* InstantiateController() override {
-    return [[SafetyCheckTableViewController alloc]
-        initWithStyle:UITableViewStylePlain];
-  }
-};
-
-// Tests PrivacyTableViewController is set up with all appropriate items
-// and sections.
-TEST_F(SafetyCheckTableViewControllerTest, TestModel) {
-  CreateController();
-  CheckController();
-  EXPECT_EQ(1, NumberOfSections());
-
-  EXPECT_EQ(3, NumberOfItemsInSection(0));
-}
+// TODO(crbug.com/1078782): Add tests.
 
 }  // namespace
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm
index d5aa705..6373e03d 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm
@@ -16,6 +16,8 @@
 #error "This file requires ARC support."
 #endif
 
+using chrome_test_util::TabGridOtherDevicesPanelButton;
+
 namespace {
 char kURL1[] = "http://firstURL";
 char kURL2[] = "http://secondURL";
@@ -134,6 +136,73 @@
       assertWithMatcher:grey_sufficientlyVisible()];
 }
 
+// Tests that Clear Browsing Data can be successfully done from tab grid.
+- (void)DISABLED_testClearBrowsingData {
+  // Load history
+  [self loadTestURLs];
+
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::ShowTabsButton()]
+      performAction:grey_tap()];
+
+  // Switch over to Recent Tabs.
+  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
+      performAction:grey_tap()];
+
+  // Tap on "Show History"
+  // Undo is available after close all action.
+  [[EarlGrey
+      selectElementWithMatcher:chrome_test_util::TabGridSelectShowHistoryCell()]
+      performAction:grey_tap()];
+  [ChromeEarlGreyUI openAndClearBrowsingDataFromHistory];
+  [ChromeEarlGreyUI assertHistoryHasNoEntries];
+}
+
+// Tests the Copy Link action on a recent tab's context menu.
+- (void)testRecentTabsContextMenuCopyLink {
+  if (![ChromeEarlGrey isNativeContextMenusEnabled]) {
+    EARL_GREY_TEST_SKIPPED(@"Test disabled on when feature flag is off.");
+  }
+
+  [self prepareRecentTabWithURL:_URL1 response:kResponse1];
+  [self longPressRecentTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
+
+  [ChromeEarlGrey
+      verifyCopyLinkActionWithText:[NSString stringWithUTF8String:_URL1.spec()
+                                                                      .c_str()]
+                      useNewString:YES];
+}
+
+// Tests the Open in New Tab action on a recent tab's context menu.
+- (void)testRecentTabsContextMenuOpenInNewTab {
+  if (![ChromeEarlGrey isNativeContextMenusEnabled]) {
+    EARL_GREY_TEST_SKIPPED(@"Test disabled on when feature flag is off.");
+  }
+
+  [self prepareRecentTabWithURL:_URL1 response:kResponse1];
+  [self longPressRecentTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
+
+  [ChromeEarlGrey verifyOpenInNewTabActionWithURL:_URL1.GetContent()];
+
+  // Verify that the Tab Grid is closed.
+  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
+      assertWithMatcher:grey_notVisible()];
+}
+
+// Tests the Share action on a recent tab's context menu.
+- (void)testRecentTabsContextMenuShare {
+  if (![ChromeEarlGrey isNativeContextMenusEnabled]) {
+    EARL_GREY_TEST_SKIPPED(@"Test disabled on when feature flag is off.");
+  }
+
+  [self prepareRecentTabWithURL:_URL1 response:kResponse1];
+  [self longPressRecentTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
+
+  [ChromeEarlGrey
+      verifyShareActionWithPageTitle:[NSString stringWithUTF8String:kTitle1]];
+}
+
+#pragma mark - Helper Methods
+
 - (void)loadTestURLs {
   [ChromeEarlGrey loadURL:_URL1];
   [ChromeEarlGrey waitForWebStateContainingText:kResponse1];
@@ -145,27 +214,25 @@
   [ChromeEarlGrey waitForWebStateContainingText:kResponse3];
 }
 
-// Test that Clear Browsing Data can be successfully done from tab grid.
-- (void)DISABLED_testClearBrowsingData {
-  // Load history
-  [self loadTestURLs];
+// Loads a URL in a new tab and deletes it to populate Recent Tabs. Then,
+// navigates to the Recent tabs via tab grid.
+- (void)prepareRecentTabWithURL:(const GURL&)URL
+                       response:(const char*)response {
+  [ChromeEarlGrey loadURL:URL];
+  [ChromeEarlGrey waitForWebStateContainingText:response];
 
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::ShowTabsButton()]
-      performAction:grey_tap()];
+  // Close the tab, making it appear in Recent Tabs.
+  [ChromeEarlGrey closeCurrentTab];
 
   // Switch over to Recent Tabs.
-  [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(
-                                   kTabGridRemoteTabsPageButtonIdentifier)]
+  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
       performAction:grey_tap()];
+}
 
-  // Tap on "Show History"
-  // Undo is available after close all action.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::TabGridSelectShowHistoryCell()]
-      performAction:grey_tap()];
-  [ChromeEarlGreyUI openAndClearBrowsingDataFromHistory];
-  [ChromeEarlGreyUI assertHistoryHasNoEntries];
+- (void)longPressRecentTabWithTitle:(NSString*)title {
+  // The test page may be there multiple times.
+  [[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(title)]
+      atIndex:0] performAction:grey_longPress()];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h b/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h
index 086d6911..0849dbc 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h
@@ -50,4 +50,8 @@
 // A masked password string(e.g. "••••••••").
 extern NSString* const kMaskedPassword;
 
+// The accessibility identifier of the info button of the
+// TableViewInfoButtonCell.
+extern NSString* const kTableViewCellInfoButtonViewId;
+
 #endif  // IOS_CHROME_BROWSER_UI_TABLE_VIEW_CELLS_TABLE_VIEW_CELLS_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.mm b/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.mm
index f0cefab..98f9d9c 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.mm
@@ -23,3 +23,5 @@
 const CGFloat kTableViewAccessoryWidth = 40;
 
 NSString* const kMaskedPassword = @"••••••••";
+NSString* const kTableViewCellInfoButtonViewId =
+    @"kTableViewCellInfoButtonViewId";
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_info_button_cell.mm b/ios/chrome/browser/ui/table_view/cells/table_view_info_button_cell.mm
index e5859bdf..c58ceb3e 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_info_button_cell.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_info_button_cell.mm
@@ -6,6 +6,7 @@
 
 #import "ios/chrome/browser/ui/settings/cells/settings_cells_constants.h"
 #include "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
@@ -108,6 +109,7 @@
         setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh + 1
                                         forAxis:
                                             UILayoutConstraintAxisHorizontal];
+    _trailingButton.accessibilityIdentifier = kTableViewCellInfoButtonViewId;
     [self.contentView addSubview:_trailingButton];
 
     // Set up the constraints assuming that the icon image is hidden.
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn
index 0b1df89..2a2ff175 100644
--- a/ios/chrome/browser/web/BUILD.gn
+++ b/ios/chrome/browser/web/BUILD.gn
@@ -57,6 +57,7 @@
     "//ios/components/ui_util",
     "//ios/components/webui:url_constants",
     "//ios/net",
+    "//ios/public/provider/chrome/browser:browser",
     "//ios/web",
     "//ios/web/common",
     "//ios/web/public/js_messaging",
diff --git a/ios/chrome/browser/web/features.cc b/ios/chrome/browser/web/features.cc
index 0b4fe52c..62c08a9 100644
--- a/ios/chrome/browser/web/features.cc
+++ b/ios/chrome/browser/web/features.cc
@@ -12,6 +12,9 @@
 const base::Feature kWebPageDefaultZoomFromDynamicType{
     "WebPageDefaultZoomFromDynamicType", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kWebPageAlternativeTextZoom{
+    "WebPageAlternativeTextZoom", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kMobileGoogleSRP{"MobileGoogleSRP",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/ios/chrome/browser/web/features.h b/ios/chrome/browser/web/features.h
index 8511d2b..ef218dd 100644
--- a/ios/chrome/browser/web/features.h
+++ b/ios/chrome/browser/web/features.h
@@ -17,6 +17,9 @@
 // dynamic type setting.
 extern const base::Feature kWebPageDefaultZoomFromDynamicType;
 
+// Used to enable a different method of zooming web pages.
+extern const base::Feature kWebPageAlternativeTextZoom;
+
 // Feature flag to keep the mobile version for Google SRP. Should be used when
 // the desktop version is requested by default.
 extern const base::Feature kMobileGoogleSRP;
diff --git a/ios/chrome/browser/web/font_size_tab_helper.mm b/ios/chrome/browser/web/font_size_tab_helper.mm
index 3c3b162..59a56ca3 100644
--- a/ios/chrome/browser/web/font_size_tab_helper.mm
+++ b/ios/chrome/browser/web/font_size_tab_helper.mm
@@ -22,6 +22,8 @@
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/web/features.h"
 #include "ios/components/ui_util/dynamic_type_util.h"
+#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#import "ios/public/provider/chrome/browser/text_zoom_provider.h"
 #include "ios/web/public/js_messaging/web_frame.h"
 #include "ios/web/public/js_messaging/web_frame_util.h"
 #include "ios/web/public/js_messaging/web_frames_manager.h"
@@ -138,12 +140,9 @@
     return;
   }
   tab_helper_has_zoomed_ = true;
-  std::vector<base::Value> parameters;
-  parameters.push_back(base::Value(size));
-  for (web::WebFrame* frame :
-       web_state_->GetWebFramesManager()->GetAllWebFrames()) {
-    frame->CallJavaScriptFunction("accessibility.adjustFontSize", parameters);
-  }
+
+  ios::GetChromeBrowserProvider()->GetTextZoomProvider()->SetPageFontSize(
+      web_state_, size);
 }
 
 void FontSizeTabHelper::UserZoom(Zoom zoom) {
diff --git a/ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.h b/ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.h
index 60877b6a..8ac7309 100644
--- a/ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.h
+++ b/ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.h
@@ -67,10 +67,10 @@
   WebStateList* web_state_list_;  // weak
 
   // Counters for metrics.
-  int inserted_web_state_counter_;
-  int detached_web_state_counter_;
-  int activated_web_state_counter_;
-  bool metric_collection_paused_;
+  int inserted_web_state_counter_ = 0;
+  int detached_web_state_counter_ = 0;
+  int activated_web_state_counter_ = 0;
+  bool metric_collection_paused_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(WebStateListMetricsBrowserAgent);
 };
diff --git a/ios/chrome/common/credential_provider/constants.h b/ios/chrome/common/credential_provider/constants.h
index 40e1098..a9f1b21 100644
--- a/ios/chrome/common/credential_provider/constants.h
+++ b/ios/chrome/common/credential_provider/constants.h
@@ -14,6 +14,9 @@
 // validated in the extension.
 NSString* AppGroupUserDefaultsCredentialProviderManagedUserID();
 
+// An array of deprecated keys to be removed if present.
+NSArray<NSString*>* UnusedUserDefaultsCredentialProviderKeys();
+
 // Key for the app group user defaults indicating if the credentials have been
 // synced with iOS via AuthenticationServices.
 extern NSString* const
@@ -21,7 +24,8 @@
 
 // Key for the app group user defaults indicating if the credentials have been
 // sync for the first time. The defaults contain a Bool indicating if the first
-// time sync have been completed.
+// time sync have been completed. This value might change to force credentials
+// to be sync once Chrome is updated.
 extern NSString* const kUserDefaultsCredentialProviderFirstTimeSyncCompleted;
 
 // Key for the app group user defaults indicating if the user has enabled and
diff --git a/ios/chrome/common/credential_provider/constants.mm b/ios/chrome/common/credential_provider/constants.mm
index 1db2cb7..0ff0f851 100644
--- a/ios/chrome/common/credential_provider/constants.mm
+++ b/ios/chrome/common/credential_provider/constants.mm
@@ -57,11 +57,18 @@
       stringByAppendingString:kUserDefaultsCredentialProviderManagedUserID];
 }
 
+NSArray<NSString*>* UnusedUserDefaultsCredentialProviderKeys() {
+  return @[
+    @"UserDefaultsCredentialProviderASIdentityStoreSyncCompleted.V0",
+    @"UserDefaultsCredentialProviderFirstTimeSyncCompleted.V0"
+  ];
+}
+
 NSString* const kUserDefaultsCredentialProviderASIdentityStoreSyncCompleted =
-    @"UserDefaultsCredentialProviderASIdentityStoreSyncCompleted.V0";
+    @"UserDefaultsCredentialProviderASIdentityStoreSyncCompleted.V1";
 
 NSString* const kUserDefaultsCredentialProviderFirstTimeSyncCompleted =
-    @"UserDefaultsCredentialProviderFirstTimeSyncCompleted.V0";
+    @"UserDefaultsCredentialProviderFirstTimeSyncCompleted.V1";
 
 NSString* const kUserDefaultsCredentialProviderConsentVerified =
     @"UserDefaultsCredentialProviderConsentVerified";
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index b645541..a822e5a59 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -305,7 +305,9 @@
     "//ios/chrome/browser/ui/settings/password:unit_tests",
     "//ios/chrome/browser/ui/settings/password/password_details:unit_tests",
     "//ios/chrome/browser/ui/settings/privacy:unit_tests",
+    "//ios/chrome/browser/ui/settings/safety_check:unit_tests",
     "//ios/chrome/browser/ui/settings/sync:unit_tests",
+    "//ios/chrome/browser/ui/settings/utils:unit_tests",
     "//ios/chrome/browser/ui/sharing:unit_tests",
     "//ios/chrome/browser/ui/side_swipe:unit_tests",
     "//ios/chrome/browser/ui/tab_grid:unit_tests",
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index f12a63c..ac287ae 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -565,6 +565,27 @@
 // clearing Browsing data.
 - (void)resetBrowsingDataPrefs;
 
+#pragma mark - Pasteboard Utilities (EG2)
+
+// Verifies that |text| was copied to the pasteboard.
+- (void)verifyStringCopied:(NSString*)text;
+
+#pragma mark - Context Menus Utilities (EG2)
+
+// Taps on the Copy Link context menu action and verifies that the |text| has
+// been copied to the pasteboard. |useNewString| determines which action string
+// to use.
+- (void)verifyCopyLinkActionWithText:(NSString*)text
+                        useNewString:(BOOL)useNewString;
+
+// Taps on the Open in New Tab context menu action and waits for the |URL| to be
+// present in the omnibox.
+- (void)verifyOpenInNewTabActionWithURL:(const std::string&)URL;
+
+// Taps on the Share context menu action and validates that the ActivityView
+// was brought up with |pageTitle| in its header.
+- (void)verifyShareActionWithPageTitle:(NSString*)pageTitle;
+
 #pragma mark - Unified Consent utilities
 
 // Enables or disables URL-keyed anonymized data collection.
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index c574b44..bc5fe2d 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -38,6 +38,10 @@
 using base::test::ios::kWaitForPageLoadTimeout;
 using base::test::ios::kWaitForUIElementTimeout;
 using base::test::ios::WaitUntilConditionOrTimeout;
+using chrome_test_util::ActivityViewHeader;
+using chrome_test_util::CopyLinkButton;
+using chrome_test_util::OpenLinkInNewTabButton;
+using chrome_test_util::ShareButton;
 
 namespace {
 NSString* const kWaitForPageToStartLoadingError = @"Page did not start to load";
@@ -1038,6 +1042,47 @@
   return [ChromeEarlGreyAppInterface resetBrowsingDataPrefs];
 }
 
+#pragma mark - Pasteboard Utilities (EG2)
+
+- (void)verifyStringCopied:(NSString*)text {
+  ConditionBlock condition = ^{
+    return !![[ChromeEarlGreyAppInterface pasteboardString]
+        containsString:text];
+  };
+  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(kWaitForActionTimeout,
+                                                          condition),
+             @"Waiting for '%@' to be copied to pasteboard.", text);
+}
+
+#pragma mark - Context Menus Utilities (EG2)
+
+- (void)verifyCopyLinkActionWithText:(NSString*)text
+                        useNewString:(BOOL)useNewString {
+  [[EarlGrey selectElementWithMatcher:CopyLinkButton(useNewString)]
+      performAction:grey_tap()];
+  [self verifyStringCopied:text];
+}
+
+- (void)verifyOpenInNewTabActionWithURL:(const std::string&)URL {
+  // Check tab count prior to execution.
+  NSUInteger oldTabCount = [ChromeEarlGreyAppInterface mainTabCount];
+
+  [[EarlGrey selectElementWithMatcher:OpenLinkInNewTabButton()]
+      performAction:grey_tap()];
+
+  [self waitForMainTabCount:oldTabCount + 1];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText(URL)]
+      assertWithMatcher:grey_notNil()];
+}
+
+- (void)verifyShareActionWithPageTitle:(NSString*)pageTitle {
+  [[EarlGrey selectElementWithMatcher:ShareButton()] performAction:grey_tap()];
+
+  // Page title is added asynchronously, so wait for its appearance.
+  [self waitForMatcher:grey_allOf(ActivityViewHeader(pageTitle),
+                                  grey_sufficientlyVisible(), nil)];
+}
+
 #pragma mark - Unified consent utilities
 
 - (void)setURLKeyedAnonymizedDataCollectionEnabled:(BOOL)enabled {
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
index 16d2be5..a5b18cceb 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
@@ -480,6 +480,12 @@
 + (void)simulatePhysicalKeyboardEvent:(NSString*)input
                                 flags:(UIKeyModifierFlags)flags;
 
+#pragma mark - Pasteboard utilities
+
+// Retrieves the currently stored string on the pasteboard from the tested app's
+// perspective.
++ (NSString*)pasteboardString;
+
 @end
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_EARL_GREY_APP_INTERFACE_H_
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
index f385e891..bcaff7e6 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -838,4 +838,10 @@
   chrome_test_util::SimulatePhysicalKeyboardEvent(flags, input);
 }
 
+#pragma mark - Pasteboard utilities
+
++ (NSString*)pasteboardString {
+  return [UIPasteboard generalPasteboard].string;
+}
+
 @end
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h
index 0a85203..fa940ae4d 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -498,6 +498,9 @@
 // Returns a matcher for the CreditCardTableView window.
 id<GREYMatcher> ManualFallbackCreditCardTableViewWindowMatcher();
 
+// Returns the matcher for the iOS 13+ Activity View header.
+id<GREYMatcher> ActivityViewHeader(NSString* page_title);
+
 }  // namespace chrome_test_util
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_MATCHERS_H_
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index e8d550f..0f17e038e 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -624,4 +624,8 @@
       manualFallbackCreditCardTableViewWindowMatcher];
 }
 
+id<GREYMatcher> ActivityViewHeader(NSString* page_title) {
+  return [ChromeMatchersAppInterface activityViewHeaderWithTitle:page_title];
+}
+
 }  // namespace chrome_test_util
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
index 42e5bff..a804b4a 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
@@ -500,6 +500,9 @@
 // Returns a matcher for the CreditCardTableView window.
 + (id<GREYMatcher>)manualFallbackCreditCardTableViewWindowMatcher;
 
+// Returns the matcher for the Activity View header.
++ (id<GREYMatcher>)activityViewHeaderWithTitle:(NSString*)pageTitle;
+
 @end
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_MATCHERS_APP_INTERFACE_H_
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index 87da69ae..8467c3c1 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -972,4 +972,12 @@
   return grey_allOf(classMatcher, parentMatcher, nil);
 }
 
++ (id<GREYMatcher>)activityViewHeaderWithTitle:(NSString*)pageTitle {
+  return grey_allOf(grey_accessibilityLabel(pageTitle),
+                    grey_ancestor(grey_allOf(
+                        grey_accessibilityTrait(UIAccessibilityTraitHeader),
+                        grey_kindOfClassName(@"LPLinkView"), nil)),
+                    nil);
+}
+
 @end
diff --git a/ios/public/provider/chrome/browser/BUILD.gn b/ios/public/provider/chrome/browser/BUILD.gn
index 2b49688..42b968aa 100644
--- a/ios/public/provider/chrome/browser/BUILD.gn
+++ b/ios/public/provider/chrome/browser/BUILD.gn
@@ -15,12 +15,15 @@
     "geolocation_updater_provider.mm",
     "overrides_provider.h",
     "overrides_provider.mm",
+    "text_zoom_provider.h",
+    "text_zoom_provider.mm",
   ]
   deps = [
     "//base",
     "//components/metrics",
     "//ios/public/provider/chrome/browser/mailto",
     "//ios/web/public",
+    "//ios/web/public/js_messaging",
     "//url",
   ]
   frameworks = [ "CoreLocation.framework" ]
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.h b/ios/public/provider/chrome/browser/chrome_browser_provider.h
index 4778310..7137e10 100644
--- a/ios/public/provider/chrome/browser/chrome_browser_provider.h
+++ b/ios/public/provider/chrome/browser/chrome_browser_provider.h
@@ -26,6 +26,7 @@
 class OmahaServiceProvider;
 class OverridesProvider;
 class SpotlightProvider;
+class TextZoomProvider;
 class UserFeedbackProvider;
 class VoiceSearchProvider;
 
@@ -180,6 +181,8 @@
   // Returns an instance of the DiscoverFeed provider;
   virtual DiscoverFeedProvider* GetDiscoverFeedProvider() const;
 
+  virtual TextZoomProvider* GetTextZoomProvider() const;
+
   // Adds and removes observers.
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
@@ -191,6 +194,7 @@
  private:
   base::ObserverList<Observer, true>::Unchecked observer_list_;
   std::unique_ptr<MailtoHandlerProvider> mailto_handler_provider_;
+  std::unique_ptr<TextZoomProvider> text_zoom_provider_;
 };
 
 }  // namespace ios
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.mm b/ios/public/provider/chrome/browser/chrome_browser_provider.mm
index f93900e..66f537f5 100644
--- a/ios/public/provider/chrome/browser/chrome_browser_provider.mm
+++ b/ios/public/provider/chrome/browser/chrome_browser_provider.mm
@@ -8,6 +8,7 @@
 
 #include "components/metrics/metrics_provider.h"
 #import "ios/public/provider/chrome/browser/mailto/mailto_handler_provider.h"
+#import "ios/public/provider/chrome/browser/text_zoom_provider.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -30,7 +31,8 @@
 // A dummy implementation of ChromeBrowserProvider.
 
 ChromeBrowserProvider::ChromeBrowserProvider()
-    : mailto_handler_provider_(std::make_unique<MailtoHandlerProvider>()) {}
+    : mailto_handler_provider_(std::make_unique<MailtoHandlerProvider>()),
+      text_zoom_provider_(std::make_unique<TextZoomProvider>()) {}
 
 ChromeBrowserProvider::~ChromeBrowserProvider() {
   for (auto& observer : observer_list_)
@@ -149,6 +151,10 @@
   return nullptr;
 }
 
+TextZoomProvider* ChromeBrowserProvider::GetTextZoomProvider() const {
+  return text_zoom_provider_.get();
+}
+
 void ChromeBrowserProvider::HideModalViewStack() const {}
 
 void ChromeBrowserProvider::LogIfModalViewsArePresented() const {}
diff --git a/ios/public/provider/chrome/browser/text_zoom_provider.h b/ios/public/provider/chrome/browser/text_zoom_provider.h
new file mode 100644
index 0000000..35318ee
--- /dev/null
+++ b/ios/public/provider/chrome/browser/text_zoom_provider.h
@@ -0,0 +1,27 @@
+// Copyright 2020 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_PUBLIC_PROVIDER_CHROME_BROWSER_TEXT_ZOOM_PROVIDER_H_
+#define IOS_PUBLIC_PROVIDER_CHROME_BROWSER_TEXT_ZOOM_PROVIDER_H_
+
+namespace web {
+class WebState;
+}
+
+class TextZoomProvider {
+ public:
+  TextZoomProvider();
+  virtual ~TextZoomProvider();
+
+  // Zooms the given web_state to the provided size as a percentage. I.e. a size
+  // of 100 corresponds to a zoom of 100%.
+  virtual void SetPageFontSize(web::WebState* web_state, int size);
+
+ protected:
+  // Uses injected javascript to change to zoom the page font size to the given
+  // |size| as a percentage (size = 100 -> 100% zoom).
+  void SetPageFontSizeJavascript(web::WebState* web_state, int size);
+};
+
+#endif  // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_TEXT_ZOOM_PROVIDER_H_
diff --git a/ios/public/provider/chrome/browser/text_zoom_provider.mm b/ios/public/provider/chrome/browser/text_zoom_provider.mm
new file mode 100644
index 0000000..5294947
--- /dev/null
+++ b/ios/public/provider/chrome/browser/text_zoom_provider.mm
@@ -0,0 +1,32 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/public/provider/chrome/browser/text_zoom_provider.h"
+
+#import "base/values.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frames_manager.h"
+#import "ios/web/public/web_state.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+TextZoomProvider::TextZoomProvider() = default;
+
+TextZoomProvider::~TextZoomProvider() = default;
+
+void TextZoomProvider::SetPageFontSize(web::WebState* web_state, int size) {
+  SetPageFontSizeJavascript(web_state, size);
+}
+
+void TextZoomProvider::SetPageFontSizeJavascript(web::WebState* web_state,
+                                                 int size) {
+  std::vector<base::Value> parameters;
+  parameters.push_back(base::Value(size));
+  for (web::WebFrame* frame :
+       web_state->GetWebFramesManager()->GetAllWebFrames()) {
+    frame->CallJavaScriptFunction("accessibility.adjustFontSize", parameters);
+  }
+}
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index b3cc57d07..52fd59c 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -411,10 +411,6 @@
 const base::Feature kSuspendMutedAudio{"SuspendMutedAudio",
                                        base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Use shared block-based buffering for media.
-const base::Feature kUseNewMediaCache{"use-new-media-cache",
-                                      base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables using the media history store to store media engagement metrics.
 const base::Feature kUseMediaHistoryStore{"UseMediaHistoryStore",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index 06cf7223..be28353 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -175,7 +175,6 @@
 MEDIA_EXPORT extern const base::Feature kUseAndroidOverlayAggressively;
 MEDIA_EXPORT extern const base::Feature kUseFakeDeviceForMediaStream;
 MEDIA_EXPORT extern const base::Feature kUseMediaHistoryStore;
-MEDIA_EXPORT extern const base::Feature kUseNewMediaCache;
 MEDIA_EXPORT extern const base::Feature kUseR16Texture;
 MEDIA_EXPORT extern const base::Feature kUseSodaForLiveCaption;
 MEDIA_EXPORT extern const base::Feature kVaapiLowPowerEncoderGen9x;
diff --git a/media/cast/receiver/audio_decoder_unittest.cc b/media/cast/receiver/audio_decoder_unittest.cc
index 5a39507..6b4ed20 100644
--- a/media/cast/receiver/audio_decoder_unittest.cc
+++ b/media/cast/receiver/audio_decoder_unittest.cc
@@ -238,7 +238,6 @@
   WaitForAllAudioToBeDecoded();
 }
 
-#if !defined(OS_ANDROID)  // https://crbug.com/831999
 INSTANTIATE_TEST_SUITE_P(
     AudioDecoderTestScenarios,
     AudioDecoderTest,
@@ -246,7 +245,6 @@
                       TestScenario(CODEC_AUDIO_PCM16, 2, 48000),
                       TestScenario(CODEC_AUDIO_OPUS, 1, 8000),
                       TestScenario(CODEC_AUDIO_OPUS, 2, 48000)));
-#endif
 
 }  // namespace cast
 }  // namespace media
diff --git a/media/gpu/command_buffer_helper.cc b/media/gpu/command_buffer_helper.cc
index a15549c0..d4b91d05 100644
--- a/media/gpu/command_buffer_helper.cc
+++ b/media/gpu/command_buffer_helper.cc
@@ -199,15 +199,6 @@
         ->is_passthrough_cmd_decoder();
   }
 
-  bool SupportsTextureRectangle() const override {
-    if (!stub_)
-      return false;
-    return stub_->decoder_context()
-        ->GetFeatureInfo()
-        ->feature_flags()
-        .arb_texture_rectangle;
-  }
-
  private:
   ~CommandBufferHelperImpl() override {
     DVLOG(1) << __func__;
diff --git a/media/gpu/command_buffer_helper.h b/media/gpu/command_buffer_helper.h
index c022f42..11fd1da 100644
--- a/media/gpu/command_buffer_helper.h
+++ b/media/gpu/command_buffer_helper.h
@@ -137,9 +137,6 @@
   // Is the backing command buffer passthrough (versus validating).
   virtual bool IsPassthrough() const = 0;
 
-  // Does this command buffer support ARB_texture_rectangle.
-  virtual bool SupportsTextureRectangle() const = 0;
-
  protected:
   explicit CommandBufferHelper(
       scoped_refptr<base::SequencedTaskRunner> task_runner);
diff --git a/media/gpu/gpu_video_decode_accelerator_helpers.h b/media/gpu/gpu_video_decode_accelerator_helpers.h
index 6f7bfb8..18cedb0 100644
--- a/media/gpu/gpu_video_decode_accelerator_helpers.h
+++ b/media/gpu/gpu_video_decode_accelerator_helpers.h
@@ -112,9 +112,6 @@
 
   // Whether or not the command buffer is passthrough.
   bool is_passthrough = false;
-
-  // Whether or not ARB_texture_rectangle is present.
-  bool supports_arb_texture_rectangle = false;
 };
 
 // Convert vector of VDA::SupportedProfile to vector of
diff --git a/media/gpu/gpu_video_encode_accelerator_factory.cc b/media/gpu/gpu_video_encode_accelerator_factory.cc
index fe9f3c72..8d665bb 100644
--- a/media/gpu/gpu_video_encode_accelerator_factory.cc
+++ b/media/gpu/gpu_video_encode_accelerator_factory.cc
@@ -81,7 +81,8 @@
     base::RepeatingCallback<std::unique_ptr<VideoEncodeAccelerator>()>;
 
 std::vector<VEAFactoryFunction> GetVEAFactoryFunctions(
-    const gpu::GpuPreferences& gpu_preferences) {
+    const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds) {
   // Array of VEAFactoryFunctions potentially usable on the current platform.
   // This list is ordered by priority, from most to least preferred, if
   // applicable. This list is composed once and then reused.
@@ -108,19 +109,20 @@
       &CreateMediaFoundationVEA,
       gpu_preferences.enable_media_foundation_vea_on_windows7,
       base::FeatureList::IsEnabled(kMediaFoundationAsyncH264Encoding) &&
-          !gpu::GpuDriverBugWorkarounds()
-               .disable_mediafoundation_async_h264_encoding));
+          !gpu_workarounds.disable_mediafoundation_async_h264_encoding));
 #endif
   return vea_factory_functions;
 }
 
 VideoEncodeAccelerator::SupportedProfiles GetSupportedProfilesInternal(
-    const gpu::GpuPreferences& gpu_preferences) {
+    const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds) {
   if (gpu_preferences.disable_accelerated_video_encode)
     return VideoEncodeAccelerator::SupportedProfiles();
 
   VideoEncodeAccelerator::SupportedProfiles profiles;
-  for (const auto& create_vea : GetVEAFactoryFunctions(gpu_preferences)) {
+  for (const auto& create_vea :
+       GetVEAFactoryFunctions(gpu_preferences, gpu_workarounds)) {
     auto vea = std::move(create_vea).Run();
     if (!vea)
       continue;
@@ -137,8 +139,10 @@
 GpuVideoEncodeAcceleratorFactory::CreateVEA(
     const VideoEncodeAccelerator::Config& config,
     VideoEncodeAccelerator::Client* client,
-    const gpu::GpuPreferences& gpu_preferences) {
-  for (const auto& create_vea : GetVEAFactoryFunctions(gpu_preferences)) {
+    const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds) {
+  for (const auto& create_vea :
+       GetVEAFactoryFunctions(gpu_preferences, gpu_workarounds)) {
     std::unique_ptr<VideoEncodeAccelerator> vea = create_vea.Run();
     if (!vea)
       continue;
@@ -155,12 +159,13 @@
 // static
 MEDIA_GPU_EXPORT VideoEncodeAccelerator::SupportedProfiles
 GpuVideoEncodeAcceleratorFactory::GetSupportedProfiles(
-    const gpu::GpuPreferences& gpu_preferences) {
+    const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds) {
   // Cache the supported profiles so that they will not be computed more than
   // once per GPU process. It is assumed that |gpu_preferences| do not change
   // between calls.
   static VideoEncodeAccelerator::SupportedProfiles profiles =
-      GetSupportedProfilesInternal(gpu_preferences);
+      GetSupportedProfilesInternal(gpu_preferences, gpu_workarounds);
 
 #if BUILDFLAG(USE_V4L2_CODEC)
   // V4L2-only: the encoder devices may not be visible at the time the GPU
@@ -170,26 +175,17 @@
   // (e.g. via udev) has happened instead.
   if (profiles.empty()) {
     VLOGF(1) << "Supported profiles empty, querying again...";
-    profiles = GetSupportedProfilesInternal(gpu_preferences);
+    profiles = GetSupportedProfilesInternal(gpu_preferences, gpu_workarounds);
   }
 #endif
-  return profiles;
-}
-
-// static
-MEDIA_GPU_EXPORT VideoEncodeAccelerator::SupportedProfiles
-GpuVideoEncodeAcceleratorFactory::GetSupportedProfiles(
-    const gpu::GpuPreferences& gpu_preferences,
-    const gpu::GpuDriverBugWorkarounds& gpu_workarounds) {
-  VideoEncodeAccelerator::SupportedProfiles vea_profiles =
-      GpuVideoEncodeAcceleratorFactory::GetSupportedProfiles(gpu_preferences);
 
   if (gpu_workarounds.disable_accelerated_vp8_encode) {
-    base::EraseIf(vea_profiles, [](const auto& vea_profile) {
+    base::EraseIf(profiles, [](const auto& vea_profile) {
       return vea_profile.profile == VP8PROFILE_ANY;
     });
   }
-  return vea_profiles;
+
+  return profiles;
 }
 
 }  // namespace media
diff --git a/media/gpu/gpu_video_encode_accelerator_factory.h b/media/gpu/gpu_video_encode_accelerator_factory.h
index b2dc77d..a9e5981 100644
--- a/media/gpu/gpu_video_encode_accelerator_factory.h
+++ b/media/gpu/gpu_video_encode_accelerator_factory.h
@@ -25,12 +25,11 @@
   static std::unique_ptr<VideoEncodeAccelerator> CreateVEA(
       const VideoEncodeAccelerator::Config& config,
       VideoEncodeAccelerator::Client* client,
-      const gpu::GpuPreferences& gpu_perferences);
+      const gpu::GpuPreferences& gpu_perferences,
+      const gpu::GpuDriverBugWorkarounds& gpu_workarounds);
 
   // Gets the supported codec profiles for video encoding on the platform.
   static VideoEncodeAccelerator::SupportedProfiles GetSupportedProfiles(
-      const gpu::GpuPreferences& gpu_preferences);
-  static VideoEncodeAccelerator::SupportedProfiles GetSupportedProfiles(
       const gpu::GpuPreferences& gpu_preferences,
       const gpu::GpuDriverBugWorkarounds& gpu_workarounds);
 
diff --git a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
index 34432ca..76f22dcd 100644
--- a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
+++ b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
@@ -191,10 +191,6 @@
       base::BindRepeating(&CreateAbstractTexture, stub_->AsWeakPtr());
   gl_client_.is_passthrough =
       stub_->decoder_context()->GetFeatureInfo()->is_passthrough_cmd_decoder();
-  gl_client_.supports_arb_texture_rectangle = stub_->decoder_context()
-                                                  ->GetFeatureInfo()
-                                                  ->feature_flags()
-                                                  .arb_texture_rectangle;
 }
 
 GpuVideoDecodeAccelerator::~GpuVideoDecodeAccelerator() {
diff --git a/media/gpu/ipc/service/picture_buffer_manager.cc b/media/gpu/ipc/service/picture_buffer_manager.cc
index bc096635..92cb6751 100644
--- a/media/gpu/ipc/service/picture_buffer_manager.cc
+++ b/media/gpu/ipc/service/picture_buffer_manager.cc
@@ -13,7 +13,6 @@
 #include "base/logging.h"
 #include "base/synchronization/lock.h"
 #include "base/thread_annotations.h"
-#include "build/build_config.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
 #include "media/base/video_util.h"
 
@@ -28,6 +27,11 @@
   return value;
 }
 
+bool UseSharedImage() {
+  // TODO(https://crbug.com/1108909): Enable shared image use on macOS.
+  return false;
+}
+
 class PictureBufferManagerImpl : public PictureBufferManager {
  public:
   explicit PictureBufferManagerImpl(
@@ -75,8 +79,7 @@
       VideoPixelFormat pixel_format,
       uint32_t planes,
       gfx::Size texture_size,
-      uint32_t texture_target,
-      bool use_shared_image) override {
+      uint32_t texture_target) override {
     DVLOG(2) << __func__;
     DCHECK(gpu_task_runner_);
     DCHECK(gpu_task_runner_->BelongsToCurrentThread());
@@ -84,7 +87,7 @@
     DCHECK(planes);
     DCHECK_LE(planes, static_cast<uint32_t>(VideoFrame::kMaxPlanes));
 
-    if (!use_shared_image) {
+    if (!UseSharedImage()) {
       // TODO(sandersd): Consider requiring that CreatePictureBuffers() is
       // called with the context current.
       if (!command_buffer_helper_->MakeContextCurrent()) {
@@ -95,10 +98,9 @@
 
     std::vector<PictureBuffer> picture_buffers;
     for (uint32_t i = 0; i < count; i++) {
-      PictureBufferData picture_data = {pixel_format, texture_size,
-                                        use_shared_image};
+      PictureBufferData picture_data = {pixel_format, texture_size};
 
-      if (!use_shared_image) {
+      if (!UseSharedImage()) {
         for (uint32_t j = 0; j < planes; j++) {
           // Create a texture for this plane.
           GLuint service_id = command_buffer_helper_->CreateTexture(
@@ -217,8 +219,7 @@
 
     // If this |picture| has a SharedImage, then keep a reference to the
     // SharedImage in |picture_buffer_data| and update the gpu::MailboxHolder.
-    DCHECK_EQ(picture_buffer_data.use_shared_image,
-              !!picture.scoped_shared_image());
+    DCHECK_EQ(UseSharedImage(), !!picture.scoped_shared_image());
     if (auto scoped_shared_image = picture.scoped_shared_image()) {
       picture_buffer_data.scoped_shared_image = scoped_shared_image;
       picture_buffer_data.mailbox_holders[0] =
@@ -349,7 +350,6 @@
   struct PictureBufferData {
     VideoPixelFormat pixel_format;
     gfx::Size texture_size;
-    bool use_shared_image = false;
     std::vector<GLuint> service_ids;
     gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
     scoped_refptr<Picture::ScopedSharedImage> scoped_shared_image;
diff --git a/media/gpu/ipc/service/picture_buffer_manager.h b/media/gpu/ipc/service/picture_buffer_manager.h
index 2c9b76c..fe03190 100644
--- a/media/gpu/ipc/service/picture_buffer_manager.h
+++ b/media/gpu/ipc/service/picture_buffer_manager.h
@@ -62,7 +62,6 @@
   // |planes|: Number of image planes (textures) in the picture.
   // |texture_size|: Size of textures to create.
   // |texture_target|: Type of textures to create.
-  // |use_shared_image|: True if the created buffers should use shared images.
   //
   // Must be called on the GPU thread.
   //
@@ -78,8 +77,7 @@
       VideoPixelFormat pixel_format,
       uint32_t planes,
       gfx::Size texture_size,
-      uint32_t texture_target,
-      bool use_shared_image) = 0;
+      uint32_t texture_target) = 0;
 
   // Dismisses a picture buffer from the pool.
   //
diff --git a/media/gpu/ipc/service/picture_buffer_manager_unittest.cc b/media/gpu/ipc/service/picture_buffer_manager_unittest.cc
index d74bc708..38b0482 100644
--- a/media/gpu/ipc/service/picture_buffer_manager_unittest.cc
+++ b/media/gpu/ipc/service/picture_buffer_manager_unittest.cc
@@ -41,8 +41,7 @@
 
   std::vector<PictureBuffer> CreateARGBPictureBuffers(uint32_t count) {
     return pbm_->CreatePictureBuffers(count, PIXEL_FORMAT_ARGB, 1,
-                                      gfx::Size(320, 240), GL_TEXTURE_2D,
-                                      false /* use_shared_image */);
+                                      gfx::Size(320, 240), GL_TEXTURE_2D);
   }
 
   PictureBuffer CreateARGBPictureBuffer() {
diff --git a/media/gpu/ipc/service/vda_video_decoder.cc b/media/gpu/ipc/service/vda_video_decoder.cc
index 576d1b9..3d0e5be 100644
--- a/media/gpu/ipc/service/vda_video_decoder.cc
+++ b/media/gpu/ipc/service/vda_video_decoder.cc
@@ -76,8 +76,6 @@
       &CommandBufferHelper::MakeContextCurrent, command_buffer_helper);
   gl_client.bind_image = base::BindRepeating(&BindImage, command_buffer_helper);
   gl_client.is_passthrough = command_buffer_helper->IsPassthrough();
-  gl_client.supports_arb_texture_rectangle =
-      command_buffer_helper->SupportsTextureRectangle();
 
   std::unique_ptr<GpuVideoDecodeAcceleratorFactory> factory =
       GpuVideoDecodeAcceleratorFactory::Create(gl_client);
@@ -541,8 +539,7 @@
 
   std::vector<PictureBuffer> picture_buffers =
       picture_buffer_manager_->CreatePictureBuffers(
-          count, pixel_format, planes, texture_size, texture_target,
-          vda_->SupportsSharedImagePictureBuffers());
+          count, pixel_format, planes, texture_size, texture_target);
   if (picture_buffers.empty()) {
     parent_task_runner_->PostTask(
         FROM_HERE,
diff --git a/media/gpu/mac/vt_video_decode_accelerator_mac.cc b/media/gpu/mac/vt_video_decode_accelerator_mac.cc
index 541f00d..a688e9c6 100644
--- a/media/gpu/mac/vt_video_decode_accelerator_mac.cc
+++ b/media/gpu/mac/vt_video_decode_accelerator_mac.cc
@@ -1533,10 +1533,7 @@
     gpu::Mailbox mailbox = gpu::Mailbox::GenerateForSharedImage();
 
     gpu::SharedImageBackingGLCommon::InitializeGLTextureParams gl_params;
-    // ANGLE-on-Metal exposes IOSurfaces via GL_TEXTURE_2D. Be robust to that.
-    gl_params.target = gl_client_.supports_arb_texture_rectangle
-                           ? GL_TEXTURE_RECTANGLE_ARB
-                           : GL_TEXTURE_2D;
+    gl_params.target = GL_TEXTURE_RECTANGLE_ARB;
     gl_params.internal_format = gl_format;
     gl_params.format = gl_format;
     gl_params.type = GL_UNSIGNED_BYTE;
@@ -1570,7 +1567,7 @@
         gpu_task_runner_);
     scoped_shared_image = scoped_refptr<Picture::ScopedSharedImage>(
         new Picture::ScopedSharedImage(
-            mailbox, gl_params.target,
+            mailbox, GL_TEXTURE_RECTANGLE_ARB,
             std::move(destroy_shared_image_callback)));
   } else {
     if (!gl_client_.bind_image.Run(picture_info->client_texture_id,
@@ -1690,10 +1687,6 @@
   return false;
 }
 
-bool VTVideoDecodeAccelerator::SupportsSharedImagePictureBuffers() const {
-  return true;
-}
-
 // static
 VideoDecodeAccelerator::SupportedProfiles
 VTVideoDecodeAccelerator::GetSupportedProfiles() {
diff --git a/media/gpu/mac/vt_video_decode_accelerator_mac.h b/media/gpu/mac/vt_video_decode_accelerator_mac.h
index 5ec5bed..aada203 100644
--- a/media/gpu/mac/vt_video_decode_accelerator_mac.h
+++ b/media/gpu/mac/vt_video_decode_accelerator_mac.h
@@ -63,7 +63,6 @@
       const base::WeakPtr<Client>& decode_client,
       const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner)
       override;
-  bool SupportsSharedImagePictureBuffers() const override;
 
   // MemoryDumpProvider implementation.
   bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
diff --git a/media/gpu/test/fake_command_buffer_helper.cc b/media/gpu/test/fake_command_buffer_helper.cc
index cd0ec218..f31ec7c 100644
--- a/media/gpu/test/fake_command_buffer_helper.cc
+++ b/media/gpu/test/fake_command_buffer_helper.cc
@@ -168,8 +168,4 @@
   return false;
 }
 
-bool FakeCommandBufferHelper::SupportsTextureRectangle() const {
-  return false;
-}
-
 }  // namespace media
diff --git a/media/gpu/test/fake_command_buffer_helper.h b/media/gpu/test/fake_command_buffer_helper.h
index 5474c0b..4a61372 100644
--- a/media/gpu/test/fake_command_buffer_helper.h
+++ b/media/gpu/test/fake_command_buffer_helper.h
@@ -62,7 +62,6 @@
                         base::OnceClosure done_cb) override;
   void SetWillDestroyStubCB(WillDestroyStubCB will_destroy_stub_cb) override;
   bool IsPassthrough() const override;
-  bool SupportsTextureRectangle() const override;
 
  private:
   ~FakeCommandBufferHelper() override;
diff --git a/media/gpu/test/video_encoder/video_encoder_client.cc b/media/gpu/test/video_encoder/video_encoder_client.cc
index 7594d7f..3ce7068 100644
--- a/media/gpu/test/video_encoder/video_encoder_client.cc
+++ b/media/gpu/test/video_encoder/video_encoder_client.cc
@@ -11,6 +11,7 @@
 
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
+#include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_preferences.h"
 #include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "media/base/bind_to_current_loop.h"
@@ -416,8 +417,8 @@
       VideoEncodeAccelerator::Config::ContentType::kCamera,
       CreateSpatialLayersConfig(video_->Resolution(), encoder_client_config_));
 
-  encoder_ = GpuVideoEncodeAcceleratorFactory::CreateVEA(config, this,
-                                                         gpu::GpuPreferences());
+  encoder_ = GpuVideoEncodeAcceleratorFactory::CreateVEA(
+      config, this, gpu::GpuPreferences(), gpu::GpuDriverBugWorkarounds());
   *success = (encoder_ != nullptr);
 
   // Initialization is continued once the encoder notifies us of the coded size
diff --git a/media/gpu/video_encode_accelerator_unittest.cc b/media/gpu/video_encode_accelerator_unittest.cc
index da750bc6..8b9f416 100644
--- a/media/gpu/video_encode_accelerator_unittest.cc
+++ b/media/gpu/video_encode_accelerator_unittest.cc
@@ -44,6 +44,7 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "build/build_config.h"
+#include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_preferences.h"
 #include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "media/base/bind_to_current_loop.h"
@@ -1828,7 +1829,8 @@
 static std::unique_ptr<VideoEncodeAccelerator> CreateVideoEncodeAccelerator(
     const VideoEncodeAccelerator::Config& config,
     VideoEncodeAccelerator::Client* client,
-    const gpu::GpuPreferences& gpu_preferences) {
+    const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds) {
   if (g_fake_encoder) {
     std::unique_ptr<VideoEncodeAccelerator> encoder(
         new FakeVideoEncodeAccelerator(
@@ -1838,8 +1840,8 @@
       return encoder;
     return nullptr;
   } else {
-    return GpuVideoEncodeAcceleratorFactory::CreateVEA(config, client,
-                                                       gpu_preferences);
+    return GpuVideoEncodeAcceleratorFactory::CreateVEA(
+        config, client, gpu_preferences, gpu_workarounds);
   }
 }
 
@@ -1855,7 +1857,8 @@
       test_stream_->pixel_format, encoded_visible_size_,
       test_stream_->requested_profile, requested_bitrate_, requested_framerate_,
       keyframe_period_, test_stream_->requested_level, false, storage_type);
-  encoder_ = CreateVideoEncodeAccelerator(config, this, gpu::GpuPreferences());
+  encoder_ = CreateVideoEncodeAccelerator(config, this, gpu::GpuPreferences(),
+                                          gpu::GpuDriverBugWorkarounds());
   if (!encoder_) {
     LOG(ERROR) << "Failed creating a VideoEncodeAccelerator.";
     SetState(CS_ERROR);
@@ -2531,7 +2534,8 @@
   const VideoEncodeAccelerator::Config config(
       g_env->test_streams_[0]->pixel_format, visible_size,
       g_env->test_streams_[0]->requested_profile, bitrate_, fps_);
-  encoder_ = CreateVideoEncodeAccelerator(config, this, gpu::GpuPreferences());
+  encoder_ = CreateVideoEncodeAccelerator(config, this, gpu::GpuPreferences(),
+                                          gpu::GpuDriverBugWorkarounds());
   if (!encoder_) {
     LOG(ERROR) << "Failed creating a VideoEncodeAccelerator.";
     SetState(CS_ERROR);
diff --git a/media/mojo/services/mojo_video_encode_accelerator_provider.h b/media/mojo/services/mojo_video_encode_accelerator_provider.h
index 1e5572a..01b64d7 100644
--- a/media/mojo/services/mojo_video_encode_accelerator_provider.h
+++ b/media/mojo/services/mojo_video_encode_accelerator_provider.h
@@ -29,7 +29,8 @@
       base::RepeatingCallback<std::unique_ptr<::media::VideoEncodeAccelerator>(
           const ::media::VideoEncodeAccelerator::Config& config,
           VideoEncodeAccelerator::Client* client,
-          const gpu::GpuPreferences& gpu_preferences)>;
+          const gpu::GpuPreferences& gpu_preferences,
+          const gpu::GpuDriverBugWorkarounds& gpu_workarounds)>;
 
   static void Create(
       mojo::PendingReceiver<mojom::VideoEncodeAcceleratorProvider> receiver,
diff --git a/media/mojo/services/mojo_video_encode_accelerator_service.cc b/media/mojo/services/mojo_video_encode_accelerator_service.cc
index dddf2fb5..b573cae 100644
--- a/media/mojo/services/mojo_video_encode_accelerator_service.cc
+++ b/media/mojo/services/mojo_video_encode_accelerator_service.cc
@@ -85,8 +85,8 @@
     return;
   }
 
-  encoder_ =
-      std::move(create_vea_callback_).Run(config, this, gpu_preferences_);
+  encoder_ = std::move(create_vea_callback_)
+                 .Run(config, this, gpu_preferences_, gpu_workarounds_);
   if (!encoder_) {
     DLOG(ERROR) << __func__ << " Error creating or initializing VEA";
     std::move(success_callback).Run(false);
diff --git a/media/mojo/services/mojo_video_encode_accelerator_service.h b/media/mojo/services/mojo_video_encode_accelerator_service.h
index 4a9ceca5..9c780c0 100644
--- a/media/mojo/services/mojo_video_encode_accelerator_service.h
+++ b/media/mojo/services/mojo_video_encode_accelerator_service.h
@@ -39,7 +39,8 @@
       base::OnceCallback<std::unique_ptr<::media::VideoEncodeAccelerator>(
           const ::media::VideoEncodeAccelerator::Config& config,
           Client* client,
-          const gpu::GpuPreferences& gpu_preferences)>;
+          const gpu::GpuPreferences& gpu_preferences,
+          const gpu::GpuDriverBugWorkarounds& gpu_workarounds)>;
 
   static void Create(
       mojo::PendingReceiver<mojom::VideoEncodeAccelerator> receiver,
diff --git a/media/mojo/services/mojo_video_encode_accelerator_service_unittest.cc b/media/mojo/services/mojo_video_encode_accelerator_service_unittest.cc
index a339331..3d4ead7 100644
--- a/media/mojo/services/mojo_video_encode_accelerator_service_unittest.cc
+++ b/media/mojo/services/mojo_video_encode_accelerator_service_unittest.cc
@@ -34,7 +34,8 @@
     bool will_initialization_succeed,
     const VideoEncodeAccelerator::Config& config,
     VideoEncodeAccelerator::Client* client,
-    const gpu::GpuPreferences& gpu_preferences) {
+    const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds) {
   // Use FakeVEA as scoped_ptr to guarantee proper destruction via Destroy().
   auto vea = std::make_unique<FakeVideoEncodeAccelerator>(
       base::ThreadTaskRunnerHandle::Get());
diff --git a/media/mojo/test/mojo_video_encode_accelerator_integration_test.cc b/media/mojo/test/mojo_video_encode_accelerator_integration_test.cc
index 785fce3..ce8d29f 100644
--- a/media/mojo/test/mojo_video_encode_accelerator_integration_test.cc
+++ b/media/mojo/test/mojo_video_encode_accelerator_integration_test.cc
@@ -35,7 +35,8 @@
 extern std::unique_ptr<VideoEncodeAccelerator> CreateAndInitializeFakeVEA(
     const VideoEncodeAccelerator::Config& config,
     VideoEncodeAccelerator::Client* client,
-    const gpu::GpuPreferences& gpu_preferences) {
+    const gpu::GpuPreferences& gpu_preferences,
+    const gpu::GpuDriverBugWorkarounds& gpu_workarounds) {
   // Use FakeVEA as scoped_ptr to guarantee proper destruction via Destroy().
   auto vea = std::make_unique<FakeVideoEncodeAccelerator>(
       base::ThreadTaskRunnerHandle::Get());
diff --git a/media/video/video_decode_accelerator.cc b/media/video/video_decode_accelerator.cc
index b15a8aa..aee2a763 100644
--- a/media/video/video_decode_accelerator.cc
+++ b/media/video/video_decode_accelerator.cc
@@ -101,10 +101,6 @@
   return GL_RGBA;
 }
 
-bool VideoDecodeAccelerator::SupportsSharedImagePictureBuffers() const {
-  return false;
-}
-
 VideoDecodeAccelerator::SupportedProfile::SupportedProfile()
     : profile(media::VIDEO_CODEC_PROFILE_UNKNOWN), encrypted_only(false) {}
 
diff --git a/media/video/video_decode_accelerator.h b/media/video/video_decode_accelerator.h
index 22ba252..c4ad432 100644
--- a/media/video/video_decode_accelerator.h
+++ b/media/video/video_decode_accelerator.h
@@ -414,10 +414,6 @@
   // TODO(dshwang): after moving to D3D11, remove this. crbug.com/438691
   virtual GLenum GetSurfaceInternalFormat() const;
 
-  // Returns true if the decoder supports SharedImage backed picture buffers.
-  // May be called on any thread at any time.
-  virtual bool SupportsSharedImagePictureBuffers() const;
-
  protected:
   // Do not delete directly; use Destroy() or own it with a scoped_ptr, which
   // will Destroy() it properly by default.
diff --git a/net/dns/BUILD.gn b/net/dns/BUILD.gn
index f243877..cf884bbc 100644
--- a/net/dns/BUILD.gn
+++ b/net/dns/BUILD.gn
@@ -177,11 +177,6 @@
   # Restricted access so we can keep track of all usage external to the
   # network stack and network service.
   friend = [
-    # DNS Config and Overrides for Chrome browser.
-    # TODO (ericorth): Make DnsConfig(Overrides) public so //chrome/browser does
-    # not need to depend on host_resolver.
-    "//chrome/browser",
-
     # chromecast/browser/url_request_context_factory.cc
     # URLRequestContext creation for chromecast.
     "//chromecast/browser",
diff --git a/remoting/codec/DEPS b/remoting/codec/DEPS
index 67d9c12..9e88118 100644
--- a/remoting/codec/DEPS
+++ b/remoting/codec/DEPS
@@ -5,6 +5,7 @@
   "+google/protobuf",
   "+third_party/opus",
   "+third_party/webrtc",
+  "+gpu/config/gpu_driver_bug_workarounds.h",
   "+gpu/config/gpu_preferences.h",
   "+media/cast",
   "+media/video",
diff --git a/remoting/codec/webrtc_video_encoder_gpu.cc b/remoting/codec/webrtc_video_encoder_gpu.cc
index 64c0274..34c846d 100644
--- a/remoting/codec/webrtc_video_encoder_gpu.cc
+++ b/remoting/codec/webrtc_video_encoder_gpu.cc
@@ -13,6 +13,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/unsafe_shared_memory_region.h"
 #include "build/build_config.h"
+#include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_preferences.h"
 #include "media/gpu/gpu_video_encode_accelerator_factory.h"
 #include "remoting/base/constants.h"
@@ -53,6 +54,11 @@
   return gpu_preferences;
 }
 
+gpu::GpuDriverBugWorkarounds CreateGpuWorkarounds() {
+  gpu::GpuDriverBugWorkarounds gpu_workarounds;
+  return gpu_workarounds;
+}
+
 }  // namespace
 
 namespace remoting {
@@ -230,7 +236,7 @@
       input_format, input_visible_size_, codec_profile_, initial_bitrate);
   video_encode_accelerator_ =
       media::GpuVideoEncodeAcceleratorFactory::CreateVEA(
-          config, this, CreateGpuPreferences());
+          config, this, CreateGpuPreferences(), CreateGpuWorkarounds());
 
   if (!video_encode_accelerator_) {
     LOG(ERROR) << "Could not create VideoEncodeAccelerator";
@@ -271,7 +277,7 @@
     const WebrtcVideoEncoderSelector::Profile& profile) {
   media::VideoEncodeAccelerator::SupportedProfiles profiles =
       media::GpuVideoEncodeAcceleratorFactory::GetSupportedProfiles(
-          CreateGpuPreferences());
+          CreateGpuPreferences(), CreateGpuWorkarounds());
   for (const auto& supported_profile : profiles) {
     if (supported_profile.profile != kH264Profile) {
       continue;
diff --git a/services/network/public/cpp/BUILD.gn b/services/network/public/cpp/BUILD.gn
index d66d36e..3e240ba3 100644
--- a/services/network/public/cpp/BUILD.gn
+++ b/services/network/public/cpp/BUILD.gn
@@ -180,16 +180,31 @@
   defines = [ "IS_NETWORK_CPP_BASE_IMPL" ]
 }
 
+# This component is separate from cpp_base as it is a dependency of
+# //services/network/public/mojom:url_loader_base.
+component("cross_origin_embedder_policy") {
+  sources = [
+    "cross_origin_embedder_policy.cc",
+    "cross_origin_embedder_policy.h",
+    "cross_origin_embedder_policy_mojom_traits.cc",
+    "cross_origin_embedder_policy_mojom_traits.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/public/mojom/base",
+    "//services/network/public/mojom:url_loader_base_shared",
+  ]
+
+  defines = [ "IS_NETWORK_CPP_BASE_IMPL" ]
+}
+
 component("cpp_base") {
   output_name = "network_cpp_base"
 
   sources = [
     "cors/cors_error_status.cc",
     "cors/cors_error_status.h",
-    "cross_origin_embedder_policy.cc",
-    "cross_origin_embedder_policy.h",
-    "cross_origin_embedder_policy_mojom_traits.cc",
-    "cross_origin_embedder_policy_mojom_traits.h",
     "cross_origin_opener_policy.cc",
     "cross_origin_opener_policy.h",
     "cross_origin_opener_policy_mojom_traits.cc",
@@ -243,6 +258,7 @@
   public_deps = [
     ":cookies_mojom_support",
     ":crash_keys",
+    ":cross_origin_embedder_policy",
     ":ip_address_mojom_support",
     "//services/network/public/mojom:url_loader_base",
     "//third_party/webrtc_overrides:webrtc_component",
diff --git a/services/network/public/cpp/resource_request.cc b/services/network/public/cpp/resource_request.cc
index f105994..8f1b36b 100644
--- a/services/network/public/cpp/resource_request.cc
+++ b/services/network/public/cpp/resource_request.cc
@@ -23,6 +23,17 @@
   return new_remote;
 }
 
+// Returns true iff either holds true:
+//
+//  - both |lhs| and |rhs| are nullopt, or
+//  - neither is nullopt and they both contain equal values
+//
+bool OptionalTrustedParamsEqualsForTesting(
+    const base::Optional<ResourceRequest::TrustedParams>& lhs,
+    const base::Optional<ResourceRequest::TrustedParams>& rhs) {
+  return (!lhs && !rhs) || (lhs && rhs && lhs->EqualsForTesting(*rhs));
+}
+
 }  // namespace
 
 ResourceRequest::TrustedParams::TrustedParams() = default;
@@ -40,6 +51,7 @@
   cookie_observer =
       Clone(&const_cast<mojo::PendingRemote<mojom::CookieAccessObserver>&>(
           other.cookie_observer));
+  client_security_state = other.client_security_state.Clone();
   return *this;
 }
 
@@ -47,7 +59,8 @@
     const TrustedParams& trusted_params) const {
   return isolation_info.IsEqualForTesting(trusted_params.isolation_info) &&
          disable_secure_dns == trusted_params.disable_secure_dns &&
-         has_user_activation == trusted_params.has_user_activation;
+         has_user_activation == trusted_params.has_user_activation &&
+         client_security_state == trusted_params.client_security_state;
 }
 
 ResourceRequest::ResourceRequest() {}
@@ -55,14 +68,6 @@
 ResourceRequest::~ResourceRequest() {}
 
 bool ResourceRequest::EqualsForTesting(const ResourceRequest& request) const {
-  if ((trusted_params && !request.trusted_params) ||
-      (!trusted_params && request.trusted_params)) {
-    return false;
-  }
-  if (trusted_params && request.trusted_params) {
-    if (!trusted_params->EqualsForTesting(*request.trusted_params))
-      return false;
-  }
   return method == request.method && url == request.url &&
          site_for_cookies.IsEquivalent(request.site_for_cookies) &&
          force_ignore_site_for_cookies ==
@@ -115,6 +120,8 @@
              request.is_signed_exchange_prefetch_cache_enabled &&
          obey_origin_policy == request.obey_origin_policy &&
          recursive_prefetch_token == request.recursive_prefetch_token &&
+         OptionalTrustedParamsEqualsForTesting(trusted_params,
+                                               request.trusted_params) &&
          trust_token_params == request.trust_token_params;
 }
 
diff --git a/services/network/public/cpp/resource_request.h b/services/network/public/cpp/resource_request.h
index d911066..30d02b1 100644
--- a/services/network/public/cpp/resource_request.h
+++ b/services/network/public/cpp/resource_request.h
@@ -20,6 +20,7 @@
 #include "net/url_request/referrer_policy.h"
 #include "services/network/public/cpp/optional_trust_token_params.h"
 #include "services/network/public/cpp/resource_request_body.h"
+#include "services/network/public/mojom/client_security_state.mojom.h"
 #include "services/network/public/mojom/cookie_access_observer.mojom.h"
 #include "services/network/public/mojom/cors.mojom-shared.h"
 #include "services/network/public/mojom/fetch_api.mojom-shared.h"
@@ -53,6 +54,7 @@
     bool disable_secure_dns = false;
     bool has_user_activation = false;
     mojo::PendingRemote<mojom::CookieAccessObserver> cookie_observer;
+    mojom::ClientSecurityStatePtr client_security_state;
   };
 
   ResourceRequest();
diff --git a/services/network/public/cpp/url_request_mojom_traits.cc b/services/network/public/cpp/url_request_mojom_traits.cc
index 34f6223..21fca61 100644
--- a/services/network/public/cpp/url_request_mojom_traits.cc
+++ b/services/network/public/cpp/url_request_mojom_traits.cc
@@ -149,12 +149,16 @@
                   network::ResourceRequest::TrustedParams>::
     Read(network::mojom::TrustedUrlRequestParamsDataView data,
          network::ResourceRequest::TrustedParams* out) {
-  if (!data.ReadIsolationInfo(&out->isolation_info))
+  if (!data.ReadIsolationInfo(&out->isolation_info)) {
     return false;
+  }
   out->disable_secure_dns = data.disable_secure_dns();
   out->has_user_activation = data.has_user_activation();
   out->cookie_observer = data.TakeCookieObserver<
       mojo::PendingRemote<network::mojom::CookieAccessObserver>>();
+  if (!data.ReadClientSecurityState(&out->client_security_state)) {
+    return false;
+  }
   return true;
 }
 
diff --git a/services/network/public/cpp/url_request_mojom_traits.h b/services/network/public/cpp/url_request_mojom_traits.h
index f3daedb..4c746753 100644
--- a/services/network/public/cpp/url_request_mojom_traits.h
+++ b/services/network/public/cpp/url_request_mojom_traits.h
@@ -24,6 +24,7 @@
 #include "services/network/public/cpp/resource_request_body.h"
 #include "services/network/public/cpp/site_for_cookies_mojom_traits.h"
 #include "services/network/public/mojom/chunked_data_pipe_getter.mojom.h"
+#include "services/network/public/mojom/client_security_state.mojom-forward.h"
 #include "services/network/public/mojom/cookie_access_observer.mojom.h"
 #include "services/network/public/mojom/data_pipe_getter.mojom.h"
 #include "services/network/public/mojom/trust_tokens.mojom.h"
@@ -74,6 +75,10 @@
         const_cast<network::ResourceRequest::TrustedParams&>(trusted_params)
             .cookie_observer);
   }
+  static const network::mojom::ClientSecurityStatePtr& client_security_state(
+      const network::ResourceRequest::TrustedParams& trusted_params) {
+    return trusted_params.client_security_state;
+  }
 
   static bool Read(network::mojom::TrustedUrlRequestParamsDataView data,
                    network::ResourceRequest::TrustedParams* out);
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn
index 9a260665..81aecbe52 100644
--- a/services/network/public/mojom/BUILD.gn
+++ b/services/network/public/mojom/BUILD.gn
@@ -153,7 +153,11 @@
   generate_java = true
   sources = [
     "chunked_data_pipe_getter.mojom",
+    "client_security_state.mojom",
+    "cross_origin_embedder_policy.mojom",
     "data_pipe_getter.mojom",
+    "fetch_api.mojom",
+    "ip_address_space.mojom",
     "mutable_network_traffic_annotation_tag.mojom",
     "mutable_partial_network_traffic_annotation_tag.mojom",
     "trust_tokens.mojom",
@@ -161,6 +165,7 @@
 
   public_deps = [
     "//mojo/public/mojom/base",
+    "//url/mojom:url_mojom_gurl",
     "//url/mojom:url_mojom_origin",
   ]
 
@@ -175,6 +180,17 @@
     {
       types = [
         {
+          mojom = "network.mojom.CrossOriginEmbedderPolicy"
+          cpp = "::network::CrossOriginEmbedderPolicy"
+        },
+      ]
+      traits_headers = [ "//services/network/public/cpp/cross_origin_embedder_policy_mojom_traits.h" ]
+      traits_deps =
+          [ "//services/network/public/cpp:cross_origin_embedder_policy" ]
+    },
+    {
+      types = [
+        {
           mojom = "network.mojom.MutableNetworkTrafficAnnotationTag"
           cpp = "::net::MutableNetworkTrafficAnnotationTag"
         },
@@ -424,17 +440,14 @@
     "content_security_policy.mojom",
     "cors.mojom",
     "cors_origin_pattern.mojom",
-    "cross_origin_embedder_policy.mojom",
     "cross_origin_opener_policy.mojom",
     "default_credentials.mojom",
     "dhcp_wpad_url_client.mojom",
     "digitally_signed.mojom",
-    "fetch_api.mojom",
     "host_resolver.mojom",
     "http_raw_headers.mojom",
     "http_raw_request_response_info.mojom",
     "http_request_headers.mojom",
-    "ip_address_space.mojom",
     "isolation_info.mojom",
     "load_timing_info.mojom",
     "mdns_responder.mojom",
@@ -521,15 +534,6 @@
     {
       types = [
         {
-          mojom = "network.mojom.CrossOriginEmbedderPolicy"
-          cpp = "::network::CrossOriginEmbedderPolicy"
-        },
-      ]
-      traits_headers = [ "//services/network/public/cpp/cross_origin_embedder_policy_mojom_traits.h" ]
-    },
-    {
-      types = [
-        {
           mojom = "network.mojom.CrossOriginOpenerPolicy"
           cpp = "::network::CrossOriginOpenerPolicy"
         },
diff --git a/services/network/public/mojom/client_security_state.mojom b/services/network/public/mojom/client_security_state.mojom
new file mode 100644
index 0000000..283446a
--- /dev/null
+++ b/services/network/public/mojom/client_security_state.mojom
@@ -0,0 +1,39 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module network.mojom;
+
+import "services/network/public/mojom/cross_origin_embedder_policy.mojom";
+import "services/network/public/mojom/ip_address_space.mojom";
+
+// How to treat private network requests.
+//
+// Private network requests are any requests to a resource served by a
+// non-public IP address.
+//
+// See the CORS-RFC1918 spec for details: https://wicg.github.io/cors-rfc1918.
+enum PrivateNetworkRequestPolicy {
+  // Allow all requests.
+  kAllow,
+
+  // Forbid requests to more-private address spaces than that of the initiator,
+  // when the initiator is not in a secure context.
+  kBlockFromInsecureToMorePrivate,
+};
+
+struct ClientSecurityState {
+  // See: https://html.spec.whatwg.org/multipage/origin.html#coep
+  CrossOriginEmbedderPolicy cross_origin_embedder_policy;
+
+  // Whether the initiator of the requests is in a web secure context.
+  // See: https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts
+  bool is_web_secure_context = false;
+
+  // The initiator's IP AddressSpace.
+  IPAddressSpace ip_address_space = kUnknown;
+
+  // The policy to apply to private network requests.
+  PrivateNetworkRequestPolicy private_network_request_policy = kAllow;
+};
+
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 82fdee56..184e48c 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -12,11 +12,12 @@
 import "mojo/public/mojom/base/values.mojom";
 import "services/network/public/mojom/address_list.mojom";
 import "services/network/public/mojom/cert_verifier_service.mojom";
+import "services/network/public/mojom/client_security_state.mojom";
 import "services/network/public/mojom/cookie_access_observer.mojom";
 import "services/network/public/mojom/cookie_manager.mojom";
-import "services/network/public/mojom/default_credentials.mojom";
 import "services/network/public/mojom/cors_origin_pattern.mojom";
 import "services/network/public/mojom/cross_origin_embedder_policy.mojom";
+import "services/network/public/mojom/default_credentials.mojom";
 import "services/network/public/mojom/host_resolver.mojom";
 import "services/network/public/mojom/http_request_headers.mojom";
 import "services/network/public/mojom/ip_address.mojom";
@@ -572,37 +573,6 @@
   bool skip_cors_enabled_scheme_check = false;
 };
 
-// How to treat private network requests.
-//
-// Private network requests are any requests to a resource served by a
-// non-public IP address.
-//
-// See the CORS-RFC1918 spec for details: https://wicg.github.io/cors-rfc1918.
-enum PrivateNetworkRequestPolicy {
-  // Allow all requests.
-  kAllow,
-
-  // Forbid requests to more-private address spaces than that of the initiator,
-  // when the initiator is not in a secure context.
-  kBlockFromInsecureToMorePrivate,
-};
-
-struct ClientSecurityState {
-  // https://mikewest.github.io/corpp/#integration-html
-  // https://mikewest.github.io/corpp/#initialize-embedder-policy-for-global
-  CrossOriginEmbedderPolicy cross_origin_embedder_policy;
-
-  // Whether the initiator of the requests is in a web secure context.
-  // See: https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts
-  bool is_web_secure_context = false;
-
-  // The initiator's IP AddressSpace.
-  IPAddressSpace ip_address_space = kUnknown;
-
-  // The policy to apply to private network requests.
-  PrivateNetworkRequestPolicy private_network_request_policy = kAllow;
-};
-
 // Whether to forbid all Trust Tokens redemption and signing operations
 // (https://github.com/wicg/trust-token-api).
 enum TrustTokenRedemptionPolicy {
diff --git a/services/network/public/mojom/url_loader.mojom b/services/network/public/mojom/url_loader.mojom
index e0e694a4..8c1c4a71 100644
--- a/services/network/public/mojom/url_loader.mojom
+++ b/services/network/public/mojom/url_loader.mojom
@@ -9,6 +9,7 @@
 import "mojo/public/mojom/base/file_path.mojom";
 import "mojo/public/mojom/base/time.mojom";
 import "mojo/public/mojom/base/unguessable_token.mojom";
+import "services/network/public/mojom/client_security_state.mojom";
 import "services/network/public/mojom/cors.mojom";
 import "services/network/public/mojom/cookie_access_observer.mojom";
 import "services/network/public/mojom/chunked_data_pipe_getter.mojom";
@@ -92,6 +93,14 @@
   // a cookie. If this is set to non-null, the observer passed to
   // URLLoaderFactory will be ignored.
   pending_remote<CookieAccessObserver>? cookie_observer;
+
+  // Specifies the security state of the client, for cases when the
+  // URLLoaderFactory is shared among multiple clients.
+  //
+  // This field is only used if no ClientSecurityState was specified in the
+  // URLLoaderFactoryParams passed to the factory. Otherwise, the value from
+  // the factory params is used unconditionally.
+  ClientSecurityState? client_security_state;
 };
 
 // Typemapped to network::ResourceRequest.
diff --git a/services/video_capture/BUILD.gn b/services/video_capture/BUILD.gn
index b61adf35..ab42cb1 100644
--- a/services/video_capture/BUILD.gn
+++ b/services/video_capture/BUILD.gn
@@ -12,6 +12,8 @@
     "device_factory_media_to_mojo_adapter.h",
     "device_media_to_mojo_adapter.cc",
     "device_media_to_mojo_adapter.h",
+    "gpu_memory_buffer_virtual_device_mojo_adapter.cc",
+    "gpu_memory_buffer_virtual_device_mojo_adapter.h",
     "push_video_stream_subscription_impl.cc",
     "push_video_stream_subscription_impl.h",
     "receiver_mojo_to_media_adapter.cc",
@@ -63,6 +65,7 @@
   sources = [
     "broadcasting_receiver_unittest.cc",
     "device_media_to_mojo_adapter_unittest.cc",
+    "gpu_memory_buffer_virtual_device_mojo_adapter_unittest.cc",
     "test/fake_device_descriptor_test.cc",
     "test/fake_device_descriptor_test.h",
     "test/fake_device_descriptor_unittest.cc",
diff --git a/services/video_capture/device_factory_media_to_mojo_adapter.cc b/services/video_capture/device_factory_media_to_mojo_adapter.cc
index 5ede6a0..0cf87aa 100644
--- a/services/video_capture/device_factory_media_to_mojo_adapter.cc
+++ b/services/video_capture/device_factory_media_to_mojo_adapter.cc
@@ -156,6 +156,13 @@
   NOTIMPLEMENTED();
 }
 
+void DeviceFactoryMediaToMojoAdapter::AddGpuMemoryBufferVirtualDevice(
+    const media::VideoCaptureDeviceInfo& device_info,
+    mojo::PendingReceiver<mojom::GpuMemoryBufferVirtualDevice>
+        virtual_device_receiver) {
+  NOTIMPLEMENTED();
+}
+
 void DeviceFactoryMediaToMojoAdapter::RegisterVirtualDevicesChangedObserver(
     mojo::PendingRemote<mojom::DevicesChangedObserver> observer,
     bool raise_event_if_virtual_devices_already_present) {
diff --git a/services/video_capture/device_factory_media_to_mojo_adapter.h b/services/video_capture/device_factory_media_to_mojo_adapter.h
index 5459cb29..e66bf25 100644
--- a/services/video_capture/device_factory_media_to_mojo_adapter.h
+++ b/services/video_capture/device_factory_media_to_mojo_adapter.h
@@ -55,6 +55,10 @@
       const media::VideoCaptureDeviceInfo& device_info,
       mojo::PendingReceiver<mojom::TextureVirtualDevice>
           virtual_device_receiver) override;
+  void AddGpuMemoryBufferVirtualDevice(
+      const media::VideoCaptureDeviceInfo& device_info,
+      mojo::PendingReceiver<mojom::GpuMemoryBufferVirtualDevice>
+          virtual_device_receiver) override;
   void RegisterVirtualDevicesChangedObserver(
       mojo::PendingRemote<mojom::DevicesChangedObserver> observer,
       bool raise_event_if_virtual_devices_already_present) override;
diff --git a/services/video_capture/gpu_memory_buffer_virtual_device_mojo_adapter.cc b/services/video_capture/gpu_memory_buffer_virtual_device_mojo_adapter.cc
new file mode 100644
index 0000000..347502f
--- /dev/null
+++ b/services/video_capture/gpu_memory_buffer_virtual_device_mojo_adapter.cc
@@ -0,0 +1,136 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/video_capture/gpu_memory_buffer_virtual_device_mojo_adapter.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/check_op.h"
+#include "base/memory/ptr_util.h"
+#include "media/base/bind_to_current_loop.h"
+#include "mojo/public/cpp/bindings/callback_helpers.h"
+#include "services/video_capture/public/mojom/constants.mojom.h"
+#include "services/video_capture/public/mojom/scoped_access_permission.mojom.h"
+
+namespace video_capture {
+
+GpuMemoryBufferVirtualDeviceMojoAdapter::
+    GpuMemoryBufferVirtualDeviceMojoAdapter() = default;
+
+GpuMemoryBufferVirtualDeviceMojoAdapter::
+    ~GpuMemoryBufferVirtualDeviceMojoAdapter() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void GpuMemoryBufferVirtualDeviceMojoAdapter::SetReceiverDisconnectedCallback(
+    base::OnceClosure callback) {
+  optional_receiver_disconnected_callback_ = std::move(callback);
+}
+
+void GpuMemoryBufferVirtualDeviceMojoAdapter::OnNewGpuMemoryBufferHandle(
+    int32_t buffer_id,
+    gfx::GpuMemoryBufferHandle gmb_handle) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Keep track of the buffer handles in order to be able to forward them to
+  // the Receiver when it connects. This includes cases where a new Receiver
+  // connects after a previous one has disconnected.
+  known_buffer_handles_.insert(std::make_pair(buffer_id, gmb_handle.Clone()));
+
+  if (!video_frame_handler_.is_bound())
+    return;
+  media::mojom::VideoBufferHandlePtr buffer_handle =
+      media::mojom::VideoBufferHandle::New();
+  buffer_handle->set_gpu_memory_buffer_handle(std::move(gmb_handle));
+  video_frame_handler_->OnNewBuffer(buffer_id, std::move(buffer_handle));
+}
+
+void GpuMemoryBufferVirtualDeviceMojoAdapter::OnFrameReadyInBuffer(
+    int32_t buffer_id,
+    mojo::PendingRemote<mojom::ScopedAccessPermission> access_permission,
+    media::mojom::VideoFrameInfoPtr frame_info) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!video_frame_handler_.is_bound())
+    return;
+  video_frame_handler_->OnFrameReadyInBuffer(
+      buffer_id, 0 /* frame_feedback_id */, std::move(access_permission),
+      std::move(frame_info));
+}
+
+void GpuMemoryBufferVirtualDeviceMojoAdapter::OnBufferRetired(int buffer_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  known_buffer_handles_.erase(buffer_id);
+  if (!video_frame_handler_.is_bound())
+    return;
+  video_frame_handler_->OnBufferRetired(buffer_id);
+}
+
+void GpuMemoryBufferVirtualDeviceMojoAdapter::Start(
+    const media::VideoCaptureParams& requested_settings,
+    mojo::PendingRemote<mojom::VideoFrameHandler> handler) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  video_frame_handler_.Bind(std::move(handler));
+  video_frame_handler_.set_disconnect_handler(
+      base::BindOnce(&GpuMemoryBufferVirtualDeviceMojoAdapter::
+                         OnReceiverConnectionErrorOrClose,
+                     base::Unretained(this)));
+  video_frame_handler_->OnStarted();
+
+  // Notify receiver of known buffer handles */
+  for (auto& entry : known_buffer_handles_) {
+    media::mojom::VideoBufferHandlePtr buffer_handle =
+        media::mojom::VideoBufferHandle::New();
+    buffer_handle->set_gpu_memory_buffer_handle(entry.second.Clone());
+    video_frame_handler_->OnNewBuffer(entry.first, std::move(buffer_handle));
+  }
+}
+
+void GpuMemoryBufferVirtualDeviceMojoAdapter::MaybeSuspend() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void GpuMemoryBufferVirtualDeviceMojoAdapter::Resume() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void GpuMemoryBufferVirtualDeviceMojoAdapter::GetPhotoState(
+    GetPhotoStateCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  std::move(callback).Run(nullptr);
+}
+
+void GpuMemoryBufferVirtualDeviceMojoAdapter::SetPhotoOptions(
+    media::mojom::PhotoSettingsPtr settings,
+    SetPhotoOptionsCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void GpuMemoryBufferVirtualDeviceMojoAdapter::TakePhoto(
+    TakePhotoCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void GpuMemoryBufferVirtualDeviceMojoAdapter::Stop() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!video_frame_handler_.is_bound())
+    return;
+  // Unsubscribe from connection error callbacks.
+  video_frame_handler_.set_disconnect_handler(base::OnceClosure());
+  // Send out OnBufferRetired events and OnStopped.
+  for (const auto& entry : known_buffer_handles_)
+    video_frame_handler_->OnBufferRetired(entry.first);
+  video_frame_handler_->OnStopped();
+  video_frame_handler_.reset();
+}
+
+void GpuMemoryBufferVirtualDeviceMojoAdapter::
+    OnReceiverConnectionErrorOrClose() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  Stop();
+  if (optional_receiver_disconnected_callback_)
+    std::move(optional_receiver_disconnected_callback_).Run();
+}
+
+}  // namespace video_capture
diff --git a/services/video_capture/gpu_memory_buffer_virtual_device_mojo_adapter.h b/services/video_capture/gpu_memory_buffer_virtual_device_mojo_adapter.h
new file mode 100644
index 0000000..f3fd492
--- /dev/null
+++ b/services/video_capture/gpu_memory_buffer_virtual_device_mojo_adapter.h
@@ -0,0 +1,71 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_VIDEO_CAPTURE_GPU_MEMORY_BUFFER_VIRTUAL_DEVICE_MOJO_ADAPTER_H_
+#define SERVICES_VIDEO_CAPTURE_GPU_MEMORY_BUFFER_VIRTUAL_DEVICE_MOJO_ADAPTER_H_
+
+#include <map>
+
+#include "base/callback.h"
+#include "base/sequence_checker.h"
+#include "media/capture/video/video_capture_buffer_pool.h"
+#include "media/capture/video_capture_types.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/video_capture/public/mojom/device.mojom.h"
+#include "services/video_capture/public/mojom/producer.mojom.h"
+#include "services/video_capture/public/mojom/video_frame_handler.mojom.h"
+#include "services/video_capture/public/mojom/virtual_device.mojom.h"
+
+namespace video_capture {
+
+class GpuMemoryBufferVirtualDeviceMojoAdapter
+    : public mojom::GpuMemoryBufferVirtualDevice,
+      public mojom::Device {
+ public:
+  GpuMemoryBufferVirtualDeviceMojoAdapter();
+  GpuMemoryBufferVirtualDeviceMojoAdapter(
+      GpuMemoryBufferVirtualDeviceMojoAdapter&) = delete;
+  GpuMemoryBufferVirtualDeviceMojoAdapter& operator=(
+      GpuMemoryBufferVirtualDeviceMojoAdapter&) = delete;
+  ~GpuMemoryBufferVirtualDeviceMojoAdapter() override;
+
+  void SetReceiverDisconnectedCallback(base::OnceClosure callback);
+
+  // mojom::GpuMemoryBufferVirtualDevice implementation.
+  void OnNewGpuMemoryBufferHandle(
+      int32_t buffer_id,
+      gfx::GpuMemoryBufferHandle gmb_handle) override;
+  void OnFrameReadyInBuffer(
+      int32_t buffer_id,
+      mojo::PendingRemote<mojom::ScopedAccessPermission> access_permission,
+      media::mojom::VideoFrameInfoPtr frame_info) override;
+  void OnBufferRetired(int buffer_id) override;
+
+  // mojom::Device implementation.
+  void Start(const media::VideoCaptureParams& requested_settings,
+             mojo::PendingRemote<mojom::VideoFrameHandler> handler) override;
+  void MaybeSuspend() override;
+  void Resume() override;
+  void GetPhotoState(GetPhotoStateCallback callback) override;
+  void SetPhotoOptions(media::mojom::PhotoSettingsPtr settings,
+                       SetPhotoOptionsCallback callback) override;
+  void TakePhoto(TakePhotoCallback callback) override;
+
+  void Stop();
+
+ private:
+  void OnReceiverConnectionErrorOrClose();
+
+  base::OnceClosure optional_receiver_disconnected_callback_;
+  mojo::Remote<mojom::VideoFrameHandler> video_frame_handler_;
+  std::map<int32_t, gfx::GpuMemoryBufferHandle> known_buffer_handles_;
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(GpuMemoryBufferVirtualDeviceMojoAdapter);
+};
+
+}  // namespace video_capture
+
+#endif  // SERVICES_VIDEO_CAPTURE_GPU_MEMORY_BUFFER_VIRTUAL_DEVICE_MOJO_ADAPTER_H_
diff --git a/services/video_capture/gpu_memory_buffer_virtual_device_mojo_adapter_unittest.cc b/services/video_capture/gpu_memory_buffer_virtual_device_mojo_adapter_unittest.cc
new file mode 100644
index 0000000..2da525c
--- /dev/null
+++ b/services/video_capture/gpu_memory_buffer_virtual_device_mojo_adapter_unittest.cc
@@ -0,0 +1,124 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/video_capture/gpu_memory_buffer_virtual_device_mojo_adapter.h"
+
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "services/video_capture/public/cpp/mock_video_frame_handler.h"
+#include "services/video_capture/public/mojom/video_frame_handler.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::InvokeWithoutArgs;
+
+namespace video_capture {
+
+class GpuMemoryBufferVirtualDeviceMojoAdapterTest : public ::testing::Test {
+ public:
+  GpuMemoryBufferVirtualDeviceMojoAdapterTest() = default;
+
+  void SetUp() override {
+    mock_video_frame_handler_1_ = std::make_unique<MockVideoFrameHandler>(
+        video_frame_handler_1_.InitWithNewPipeAndPassReceiver());
+    mock_video_frame_handler_2_ = std::make_unique<MockVideoFrameHandler>(
+        video_frame_handler_2_.InitWithNewPipeAndPassReceiver());
+    adapter_ = std::make_unique<GpuMemoryBufferVirtualDeviceMojoAdapter>();
+  }
+
+ protected:
+  void ProducerSharesBufferHandle(int32_t buffer_id) {
+    gfx::GpuMemoryBufferHandle dummy_buffer_handle;
+    adapter_->OnNewGpuMemoryBufferHandle(buffer_id,
+                                         std::move(dummy_buffer_handle));
+  }
+
+  void ProducerRetiresBufferHandle(int32_t buffer_id) {
+    adapter_->OnBufferRetired(buffer_id);
+  }
+
+  void Receiver1Connects() {
+    const media::VideoCaptureParams kArbitraryRequestedSettings;
+    adapter_->Start(kArbitraryRequestedSettings,
+                    std::move(video_frame_handler_1_));
+  }
+
+  void Receiver2Connects() {
+    const media::VideoCaptureParams kArbitraryRequestedSettings;
+    adapter_->Start(kArbitraryRequestedSettings,
+                    std::move(video_frame_handler_2_));
+  }
+
+  void Receiver1Disconnects() {
+    base::RunLoop wait_loop;
+    adapter_->SetReceiverDisconnectedCallback(wait_loop.QuitClosure());
+    mock_video_frame_handler_1_.reset();
+    wait_loop.Run();
+  }
+
+  std::unique_ptr<MockVideoFrameHandler> mock_video_frame_handler_1_;
+  std::unique_ptr<MockVideoFrameHandler> mock_video_frame_handler_2_;
+
+ private:
+  base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<GpuMemoryBufferVirtualDeviceMojoAdapter> adapter_;
+  mojo::PendingRemote<mojom::VideoFrameHandler> video_frame_handler_1_;
+  mojo::PendingRemote<mojom::VideoFrameHandler> video_frame_handler_2_;
+};
+
+// Tests that when buffer handles are shared by the producer before a receiver
+// has connected, these buffer handles get shared with the receiver as soon as
+// it connects.
+TEST_F(GpuMemoryBufferVirtualDeviceMojoAdapterTest,
+       BufferHandlesAreSharedWithReceiverConnectingLate) {
+  const int kArbitraryBufferId1 = 1;
+  const int kArbitraryBufferId2 = 2;
+  ProducerSharesBufferHandle(kArbitraryBufferId1);
+  ProducerSharesBufferHandle(kArbitraryBufferId2);
+
+  base::RunLoop wait_loop;
+  int buffer_received_count = 0;
+  EXPECT_CALL(*mock_video_frame_handler_1_,
+              DoOnNewBuffer(kArbitraryBufferId1, _))
+      .WillOnce(InvokeWithoutArgs([&wait_loop, &buffer_received_count]() {
+        buffer_received_count++;
+        if (buffer_received_count == 2)
+          wait_loop.Quit();
+      }));
+  EXPECT_CALL(*mock_video_frame_handler_1_,
+              DoOnNewBuffer(kArbitraryBufferId2, _))
+      .WillOnce(InvokeWithoutArgs([&wait_loop, &buffer_received_count]() {
+        buffer_received_count++;
+        if (buffer_received_count == 2)
+          wait_loop.Quit();
+      }));
+  Receiver1Connects();
+  wait_loop.Run();
+}
+
+// Tests that when a receiver disconnects and a new receiver connects, the
+// virtual device adapter shares all valid buffer handles with it.
+TEST_F(GpuMemoryBufferVirtualDeviceMojoAdapterTest,
+       BufferHandlesAreSharedWithSecondReceiver) {
+  const int kArbitraryBufferId1 = 1;
+  const int kArbitraryBufferId2 = 2;
+
+  Receiver1Connects();
+  ProducerSharesBufferHandle(kArbitraryBufferId1);
+  ProducerSharesBufferHandle(kArbitraryBufferId2);
+  Receiver1Disconnects();
+
+  ProducerRetiresBufferHandle(kArbitraryBufferId1);
+
+  base::RunLoop wait_loop;
+  EXPECT_CALL(*mock_video_frame_handler_2_,
+              DoOnNewBuffer(kArbitraryBufferId2, _))
+      .WillOnce(InvokeWithoutArgs([&wait_loop]() { wait_loop.Quit(); }));
+  Receiver2Connects();
+  wait_loop.Run();
+}
+
+}  // namespace video_capture
diff --git a/services/video_capture/public/mojom/device_factory.mojom b/services/video_capture/public/mojom/device_factory.mojom
index 35a4ca4..69ffd6f8 100644
--- a/services/video_capture/public/mojom/device_factory.mojom
+++ b/services/video_capture/public/mojom/device_factory.mojom
@@ -64,6 +64,12 @@
       media.mojom.VideoCaptureDeviceInfo device_info,
       pending_receiver<TextureVirtualDevice> virtual_device_receiver);
 
+  // Add a virtual device with video frames backed by GpuMemoryBufferHandle.
+  // The buffers are allocated and managed by the caller.
+  AddGpuMemoryBufferVirtualDevice(
+      media.mojom.VideoCaptureDeviceInfo device_info,
+      pending_receiver<GpuMemoryBufferVirtualDevice> virtual_device_receiver);
+
   // Registered observers will get notified whenever a virtual device is added
   // or removed. Note: Changes to non-virtual devices are currently being
   // monitored outside the video capture service, and therefore the service
diff --git a/services/video_capture/public/mojom/virtual_device.mojom b/services/video_capture/public/mojom/virtual_device.mojom
index fbafc5f..98faf16 100644
--- a/services/video_capture/public/mojom/virtual_device.mojom
+++ b/services/video_capture/public/mojom/virtual_device.mojom
@@ -8,6 +8,7 @@
 import "services/video_capture/public/mojom/producer.mojom";
 import "services/video_capture/public/mojom/scoped_access_permission.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
+import "ui/gfx/mojom/buffer_types.mojom";
 
 // Interface for a producer to feed video frames into a virtual
 // device. These frames will appear to the consumer of the device
@@ -66,3 +67,24 @@
   // while the corresponding buffer is still in use via OnFrameReadyInBuffer().
   OnBufferRetired(int32 buffer_id);
 };
+
+// Virtual capture device with video frames backed by GpuMemoryBuffer.
+interface GpuMemoryBufferVirtualDevice {
+  // Registers a GpuMemoryBufferHandle for subsequent transport of frames.
+  OnNewGpuMemoryBufferHandle(
+      int32 buffer_id, gfx.mojom.GpuMemoryBufferHandle gmb_handle);
+
+  // The invoker must guarantee that the GpuMemoryBufferHandle with |buffer_id|
+  // stay valid until |access_permission| is released by the invocation target.
+  // In |frame_info|, |visible_rect| must be equivalent to the full |coded_size|
+  // of the frame, i.e. using |visible_rect| to crop to subregions of the frame
+  // is not supported.
+  OnFrameReadyInBuffer(int32 buffer_id,
+                       pending_remote<ScopedAccessPermission> access_permission,
+                       media.mojom.VideoFrameInfo frame_info);
+
+  // Unregisters a GpuMemoryBufferHandle previously registered via
+  // OnNewGpuMemoryBufferHandle(). Note, that this should not be called while
+  // the corresponding buffer is still in use via OnFrameReadyInBuffer().
+  OnBufferRetired(int32 buffer_id);
+};
diff --git a/services/video_capture/virtual_device_enabled_device_factory.cc b/services/video_capture/virtual_device_enabled_device_factory.cc
index 9ac2301..57fc67f 100644
--- a/services/video_capture/virtual_device_enabled_device_factory.cc
+++ b/services/video_capture/virtual_device_enabled_device_factory.cc
@@ -13,6 +13,7 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/video_capture/device_factory.h"
+#include "services/video_capture/gpu_memory_buffer_virtual_device_mojo_adapter.h"
 #include "services/video_capture/shared_memory_virtual_device_mojo_adapter.h"
 #include "services/video_capture/texture_virtual_device_mojo_adapter.h"
 
@@ -40,6 +41,16 @@
         texture_device_(std::move(device)),
         texture_producer_receiver_(std::move(producer_receiver)) {}
 
+  VirtualDeviceEntry(
+      const media::VideoCaptureDeviceInfo& device_info,
+      std::unique_ptr<GpuMemoryBufferVirtualDeviceMojoAdapter> device,
+      std::unique_ptr<mojo::Receiver<mojom::GpuMemoryBufferVirtualDevice>>
+          producer_receiver)
+      : device_info_(device_info),
+        device_type_(DeviceType::kGpuMemoryBuffer),
+        gmb_device_(std::move(device)),
+        gmb_producer_receiver_(std::move(producer_receiver)) {}
+
   VirtualDeviceEntry(VirtualDeviceEntry&& other) = default;
   VirtualDeviceEntry& operator=(VirtualDeviceEntry&& other) = default;
 
@@ -57,6 +68,10 @@
         consumer_receiver_ = std::make_unique<mojo::Receiver<mojom::Device>>(
             texture_device_.get(), std::move(device_receiver));
         break;
+      case DeviceType::kGpuMemoryBuffer:
+        consumer_receiver_ = std::make_unique<mojo::Receiver<mojom::Device>>(
+            gmb_device_.get(), std::move(device_receiver));
+        break;
     }
     consumer_receiver_->set_disconnect_handler(
         std::move(connection_error_handler));
@@ -67,14 +82,16 @@
   void StopDevice() {
     if (shared_memory_device_)
       shared_memory_device_->Stop();
-    else
+    else if (texture_device_)
       texture_device_->Stop();
+    else
+      gmb_device_->Stop();
   }
 
   media::VideoCaptureDeviceInfo device_info() const { return device_info_; }
 
  private:
-  enum class DeviceType { kSharedMemory, kTexture };
+  enum class DeviceType { kSharedMemory, kTexture, kGpuMemoryBuffer };
 
   media::VideoCaptureDeviceInfo device_info_;
   DeviceType device_type_;
@@ -89,6 +106,11 @@
   std::unique_ptr<mojo::Receiver<mojom::TextureVirtualDevice>>
       texture_producer_receiver_;
 
+  // Only valid for |device_type_ == kGpuMemoryBuffer|
+  std::unique_ptr<GpuMemoryBufferVirtualDeviceMojoAdapter> gmb_device_;
+  std::unique_ptr<mojo::Receiver<mojom::GpuMemoryBufferVirtualDevice>>
+      gmb_producer_receiver_;
+
   std::unique_ptr<mojo::Receiver<mojom::Device>> consumer_receiver_;
 };
 
@@ -196,6 +218,33 @@
   EmitDevicesChangedEvent();
 }
 
+void VirtualDeviceEnabledDeviceFactory::AddGpuMemoryBufferVirtualDevice(
+    const media::VideoCaptureDeviceInfo& device_info,
+    mojo::PendingReceiver<mojom::GpuMemoryBufferVirtualDevice>
+        virtual_device_receiver) {
+  auto device_id = device_info.descriptor.device_id;
+  auto virtual_device_iter = virtual_devices_by_id_.find(device_id);
+  if (virtual_device_iter != virtual_devices_by_id_.end()) {
+    // Revoke the access for the current producer and consumer by
+    // removing it from the list.
+    virtual_devices_by_id_.erase(virtual_device_iter);
+  }
+
+  auto device = std::make_unique<GpuMemoryBufferVirtualDeviceMojoAdapter>();
+  auto producer_receiver =
+      std::make_unique<mojo::Receiver<mojom::GpuMemoryBufferVirtualDevice>>(
+          device.get(), std::move(virtual_device_receiver));
+  producer_receiver->set_disconnect_handler(
+      base::BindOnce(&VirtualDeviceEnabledDeviceFactory::
+                         OnVirtualDeviceProducerConnectionErrorOrClose,
+                     base::Unretained(this), device_id));
+  VirtualDeviceEntry device_entry(device_info, std::move(device),
+                                  std::move(producer_receiver));
+  virtual_devices_by_id_.insert(
+      std::make_pair(device_id, std::move(device_entry)));
+  EmitDevicesChangedEvent();
+}
+
 void VirtualDeviceEnabledDeviceFactory::RegisterVirtualDevicesChangedObserver(
     mojo::PendingRemote<mojom::DevicesChangedObserver> observer_pending_remote,
     bool raise_event_if_virtual_devices_already_present) {
diff --git a/services/video_capture/virtual_device_enabled_device_factory.h b/services/video_capture/virtual_device_enabled_device_factory.h
index 94dce4a..09bb142 100644
--- a/services/video_capture/virtual_device_enabled_device_factory.h
+++ b/services/video_capture/virtual_device_enabled_device_factory.h
@@ -40,6 +40,10 @@
       const media::VideoCaptureDeviceInfo& device_info,
       mojo::PendingReceiver<mojom::TextureVirtualDevice>
           virtual_device_receiver) override;
+  void AddGpuMemoryBufferVirtualDevice(
+      const media::VideoCaptureDeviceInfo& device_info,
+      mojo::PendingReceiver<mojom::GpuMemoryBufferVirtualDevice>
+          virtual_device_receiver) override;
   void RegisterVirtualDevicesChangedObserver(
       mojo::PendingRemote<mojom::DevicesChangedObserver> observer,
       bool raise_event_if_virtual_devices_already_present) override;
diff --git a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter
index 44c7a42..74a3aa6 100644
--- a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter
+++ b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter
@@ -59,6 +59,9 @@
 -EnterpriseEnrollmentTestBase.*
 -EulaOfflineTest.*
 -EulaTest.*
+-FamilyLinkNoticeScreenChildTest.*
+-FamilyLinkNoticeScreenManagedTest.*
+-FamilyLinkNoticeScreenTest.*
 -FingerprintSetupTest.*
 -ForceMaximizeOnFirstRunTest.*
 -ForceMaximizePolicyFalseTest.*
@@ -124,6 +127,9 @@
 -UpdateRequiredScreenTest.*
 -UpdateScreenTest.*
 -UserCloudExternalDataManagerTest.*
+-UserCreationScreenEnrolledTest.*
+-UserCreationScreenLoginTest.*
+-UserCreationScreenTest.*
 -WebKioskTest.*
 -WebviewClientCertsLoginTest.*
 -WebviewClientCertsLoginTestBase.*
diff --git a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter
index bf0a76a..b8e3c0c 100644
--- a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter
+++ b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter
@@ -59,6 +59,9 @@
 EnterpriseEnrollmentTestBase.*
 EulaOfflineTest.*
 EulaTest.*
+FamilyLinkNoticeScreenChildTest.*
+FamilyLinkNoticeScreenManagedTest.*
+FamilyLinkNoticeScreenTest.*
 FingerprintSetupTest.*
 ForceMaximizeOnFirstRunTest.*
 ForceMaximizePolicyFalseTest.*
@@ -124,6 +127,9 @@
 UpdateRequiredScreenTest.*
 UpdateScreenTest.*
 UserCloudExternalDataManagerTest.*
+UserCreationScreenEnrolledTest.*
+UserCreationScreenLoginTest.*
+UserCreationScreenTest.*
 WebKioskTest.*
 WebviewClientCertsLoginTest.*
 WebviewClientCertsLoginTestBase.*
diff --git a/testing/scripts/check_static_initializers.py b/testing/scripts/check_static_initializers.py
index a697900..8e1859f 100755
--- a/testing/scripts/check_static_initializers.py
+++ b/testing/scripts/check_static_initializers.py
@@ -42,7 +42,6 @@
         'native_message_host_chromeos.cc',  # TODO(crbug.com/537099): Remove.
         'protobuf_http_status.cc',  # TODO(crbug.com/537099): Remove.
         'rpc.pb.cc',  # TODO(crbug.com/537099): Remove.
-        'switch_access_menu_view.cc',  # TODO(crbug.com/537099): Remove.
     ],
     'nacl_helper_bootstrap': [],
 }
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 78274588..31e8ab6 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -8064,25 +8064,5 @@
                 }
             ]
         }
-    ],
-    "use-new-media-cache": [
-        {
-            "platforms": [
-                "android",
-                "android_weblayer",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "use-new-media-cache"
-                    ]
-                }
-            ]
-        }
     ]
 }
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index ab0e96f..c8447d2 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -836,6 +836,7 @@
       "//cc/mojom",
       "//services/data_decoder/public/mojom:mojom_resource_snapshot_for_web_bundle",
       "//services/network/public/mojom:cookies_mojom",
+      "//services/network/public/mojom:url_loader_base",
       "//third_party/blink/public/mojom/frame",
       "//third_party/blink/public/mojom/tokens",
       "//ui/events/mojom",
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 7d17b98..0fdd2b8 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2868,29 +2868,29 @@
   kIdentifiabilityStudyReserved3540 = 3540,
   kV8WheelEvent_DeltaMode_AttributeGetter = 3541,
   kIdentifiabilityStudyReserved3542 = 3542,
-  kV8HIDDevice_ProductId_AttributeGetter = 3543,
-  kV8HIDDevice_ProductName_AttributeGetter = 3544,
-  kV8HIDDevice_VendorId_AttributeGetter = 3545,
+  kIdentifiabilityStudyReserved3543 = 3543,
+  kIdentifiabilityStudyReserved3544 = 3544,
+  kIdentifiabilityStudyReserved3545 = 3545,
   kV8BeforeInstallPromptEvent_Platforms_AttributeGetter = 3546,
-  kV8HIDReportItem_HasNull_AttributeGetter = 3547,
-  kV8HIDReportItem_IsAbsolute_AttributeGetter = 3548,
-  kV8HIDReportItem_IsArray_AttributeGetter = 3549,
-  kV8HIDReportItem_IsRange_AttributeGetter = 3550,
-  kV8HIDReportItem_LogicalMaximum_AttributeGetter = 3551,
-  kV8HIDReportItem_LogicalMinimum_AttributeGetter = 3552,
-  kV8HIDReportItem_PhysicalMaximum_AttributeGetter = 3553,
-  kV8HIDReportItem_PhysicalMinimum_AttributeGetter = 3554,
-  kV8HIDReportItem_ReportCount_AttributeGetter = 3555,
-  kV8HIDReportItem_ReportSize_AttributeGetter = 3556,
-  kV8HIDReportItem_UnitExponent_AttributeGetter = 3557,
-  kV8HIDReportItem_UnitFactorCurrentExponent_AttributeGetter = 3558,
-  kV8HIDReportItem_UnitFactorLengthExponent_AttributeGetter = 3559,
-  kV8HIDReportItem_UnitFactorLuminousIntensityExponent_AttributeGetter = 3560,
-  kV8HIDReportItem_UnitFactorMassExponent_AttributeGetter = 3561,
-  kV8HIDReportItem_UnitFactorTemperatureExponent_AttributeGetter = 3562,
-  kV8HIDReportItem_UnitFactorTimeExponent_AttributeGetter = 3563,
-  kV8HIDReportItem_UsageMaximum_AttributeGetter = 3564,
-  kV8HIDReportItem_UsageMinimum_AttributeGetter = 3565,
+  kIdentifiabilityStudyReserved3547 = 3547,
+  kIdentifiabilityStudyReserved3548 = 3548,
+  kIdentifiabilityStudyReserved3549 = 3549,
+  kIdentifiabilityStudyReserved3550 = 3550,
+  kIdentifiabilityStudyReserved3551 = 3551,
+  kIdentifiabilityStudyReserved3552 = 3552,
+  kIdentifiabilityStudyReserved3553 = 3553,
+  kIdentifiabilityStudyReserved3554 = 3554,
+  kIdentifiabilityStudyReserved3555 = 3555,
+  kIdentifiabilityStudyReserved3556 = 3556,
+  kIdentifiabilityStudyReserved3557 = 3557,
+  kIdentifiabilityStudyReserved3558 = 3558,
+  kIdentifiabilityStudyReserved3559 = 3559,
+  kIdentifiabilityStudyReserved3560 = 3560,
+  kIdentifiabilityStudyReserved3561 = 3561,
+  kIdentifiabilityStudyReserved3562 = 3562,
+  kIdentifiabilityStudyReserved3563 = 3563,
+  kIdentifiabilityStudyReserved3564 = 3564,
+  kIdentifiabilityStudyReserved3565 = 3565,
   kV8BaseAudioContext_SampleRate_AttributeGetter = 3566,
   kIdentifiabilityStudyReserved3567 = 3567,
   kIdentifiabilityStudyReserved3568 = 3568,
diff --git a/third_party/blink/public/platform/TaskTypes.md b/third_party/blink/public/platform/TaskTypes.md
index 316b68d..9caf6c1e 100644
--- a/third_party/blink/public/platform/TaskTypes.md
+++ b/third_party/blink/public/platform/TaskTypes.md
@@ -7,56 +7,65 @@
 frozen or deferred. All specified (in W3C, HTML, DOM, etc) task types are
 pausable. Some internal task queues are not.
 
-| Queue Type                        | Throttlable | Deferrable | Freezable | Pausable | Virtual time |
-|-----------------------------------|-------------|------------|-----------|----------|--------------|
-| DOMManipulation                   | No          | Yes        | Yes       | Yes      | Yes          |
-| UserInteraction                   | No          | No         | Yes       | Yes      | Yes          |
-| Networking                        | No          | Yes        | Yes       | Yes      | No           |
-| NetworkingWithURLLoaderAnnotation | No          | Yes        | Yes       | Yes      | No           |
-| NetworkingControl                 | No          | Yes        | Yes       | Yes      | No           |
-| HistoryTraversal                  | No          | Yes        | Yes       | Yes      | Yes          |
-| Embed                             | No          | Yes        | Yes       | Yes      | Yes          |
-| MediaElementEvent                 | No          | No         | Yes       | Yes      | Yes          |
-| CanvasBlobSerialization           | No          | Yes        | Yes       | Yes      | Yes          |
-| Microtask                         | No          | Yes        | Yes       | Yes      | Yes          |
-| JavascriptTimerDelayedLowNesting  | Yes         | Yes        | Yes       | Yes      | Yes          |
-| JavascriptTimerDelayedHighNesting | Yes         | Yes        | Yes       | Yes      | Yes          |
-| JavascriptTimerImmediate [1]      | No          | Yes        | Yes       | Yes      | Yes          |
-| RemoteEvent                       | No          | Yes        | Yes       | Yes      | Yes          |
-| WebSocket                         | No          | Yes        | Yes       | Yes      | Yes          |
-| PostedMessage                     | No          | No         | Yes       | Yes      | Yes          |
-| UnshippedPortMessage              | No          | Yes        | Yes       | Yes      | Yes          |
-| FileReading                       | No          | Yes        | Yes       | Yes      | Yes          |
-| DatabaseAccess                    | No          | No         | Yes       | Yes      | Yes          |
-| Presentation                      | No          | Yes        | Yes       | Yes      | Yes          |
-| Sensor                            | No          | Yes        | Yes       | Yes      | Yes          |
-| PerformanceTimeline               | No          | Yes        | Yes       | Yes      | Yes          |
-| WebGL                             | No          | Yes        | Yes       | Yes      | Yes          |
-| IdleTask                          | No          | Yes        | Yes       | Yes      | Yes          |
-| MiscPlatformAPI                   | No          | Yes        | Yes       | Yes      | Yes          |
-| WorkerAnimation                   | No          | No         | Yes       | Yes      | Yes          |
-| FontLoading                       | No          | Yes        | Yes       | Yes      | Yes          |
-| ApplicationLifeCycle              | No          | Yes        | Yes       | Yes      | Yes          |
-| BackgroundFetch                   | No          | Yes        | Yes       | Yes      | Yes          |
-| Permission                        | No          | Yes        | Yes       | Yes      | Yes          |
-| ServiceWorkerClientMessage        | No          | No         | Yes       | Yes      | Yes          |
-| WebLocks                          | No          | No         | No        | No       | Yes          |
-| InternalDefault                   | No          | Yes        | Yes       | Yes      | Yes          |
-| InternalLoading                   | No          | Yes        | Yes       | Yes      | No           |
-| InternalTest                      | No          | No         | No        | No       | Yes          |
-| InternalWebCrypto                 | No          | No         | Yes       | Yes      | Yes          |
-| InternalMedia                     | No          | No         | Yes       | Yes      | Yes          |
-| InternalMediaRealTime             | No          | No         | Yes       | Yes      | Yes          |
-| InternalIPC                       | No          | No         | No        | No       | Yes          |
-| InternalUserInteraction           | No          | No         | Yes       | Yes      | Yes          |
-| InternalInspector                 | No          | No         | No        | No       | No           |
-| InternalTranslation               | Yes         | Yes        | Yes       | Yes      | Yes          |
-| InternalIntersectionObserver      | No          | No         | Yes       | Yes      | Yes          |
-| InternalContentCapture            | Yes         | Yes        | Yes       | Yes      | Yes          |
-| InternalNavigationAssociated      | No          | No         | No        | No       | No           |
-| InternalFreezableIPC              | No          | No         | Yes       | No       | No           |
-| InternalContinueScriptLoading     | No          | No         | Yes       | Yes      | Yes          |
+| Queue Type                        | Throttlable | Throttlable (intensive) | Deferrable | Freezable | Pausable | Virtual time |
+|-----------------------------------|-------------|-------------------------|------------|-----------|----------|--------------|
+| DOMManipulation                   | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| UserInteraction                   | No          | No                      |  No        | Yes       | Yes      | Yes          |
+| Networking                        | No          | No                      |  Yes       | Yes       | Yes      | No           |
+| NetworkingWithURLLoaderAnnotation | No          | No                      |  Yes       | Yes       | Yes      | No           |
+| NetworkingControl                 | No          | No                      |  Yes       | Yes       | Yes      | No           |
+| HistoryTraversal                  | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| Embed                             | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| MediaElementEvent                 | No          | No                      |  No        | Yes       | Yes      | Yes          |
+| CanvasBlobSerialization           | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| Microtask                         | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| JavascriptTimerDelayedLowNesting  | Yes         | No [^2]                 |  Yes       | Yes       | Yes      | Yes          |
+| JavascriptTimerDelayedHighNesting | Yes         | Yes [^3]                |  Yes       | Yes       | Yes      | Yes          |
+| JavascriptTimerImmediate          | No [^1]     | No                      |  Yes       | Yes       | Yes      | Yes          |
+| RemoteEvent                       | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| WebSocket                         | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| PostedMessage                     | No          | No                      |  No        | Yes       | Yes      | Yes          |
+| UnshippedPortMessage              | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| FileReading                       | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| DatabaseAccess                    | No          | No                      |  No        | Yes       | Yes      | Yes          |
+| Presentation                      | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| Sensor                            | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| PerformanceTimeline               | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| WebGL                             | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| IdleTask                          | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| MiscPlatformAPI                   | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| WorkerAnimation                   | No          | No                      |  No        | Yes       | Yes      | Yes          |
+| FontLoading                       | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| ApplicationLifeCycle              | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| BackgroundFetch                   | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| Permission                        | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| ServiceWorkerClientMessage        | No          | No                      |  No        | Yes       | Yes      | Yes          |
+| WebLocks                          | No          | No                      |  No        | No        | No       | Yes          |
+| InternalDefault                   | No          | No                      |  Yes       | Yes       | Yes      | Yes          |
+| InternalLoading                   | No          | No                      |  Yes       | Yes       | Yes      | No           |
+| InternalTest                      | No          | No                      |  No        | No        | No       | Yes          |
+| InternalWebCrypto                 | No          | No                      |  No        | Yes       | Yes      | Yes          |
+| InternalMedia                     | No          | No                      |  No        | Yes       | Yes      | Yes          |
+| InternalMediaRealTime             | No          | No                      |  No        | Yes       | Yes      | Yes          |
+| InternalIPC                       | No          | No                      |  No        | No        | No       | Yes          |
+| InternalUserInteraction           | No          | No                      |  No        | Yes       | Yes      | Yes          |
+| InternalInspector                 | No          | No                      |  No        | No        | No       | No           |
+| InternalTranslation               | Yes         | No                      |  Yes       | Yes       | Yes      | Yes          |
+| InternalIntersectionObserver      | No          | No                      |  No        | Yes       | Yes      | Yes          |
+| InternalContentCapture            | Yes         | No                      |  Yes       | Yes       | Yes      | Yes          |
+| InternalNavigationAssociated      | No          | No                      |  No        | No        | No       | No           |
+| InternalFreezableIPC              | No          | No                      |  No        | Yes       | No       | No           |
+| InternalContinueScriptLoading     | No          | No                      |  No        | Yes       | Yes      | Yes          |
 
 Internal Translation queue supports concept of it running only in the foreground. It is disabled if the page that owns it goes in background.
 
-[1] Assuming that the "OptOutZeroTimeoutTimersFromThrottling" feature is enabled.
+"Throttlable (Intensive)": Wake ups are limited to 1 per minute when the page
+has been backgrounded for 5 minutes. See
+[Chrome Platform Status entry](https://www.chromestatus.com/feature/4718288976216064).
+
+[^1] "Yes" if the "OptOutZeroTimeoutTimersFromThrottling" feature is disabled.
+
+[^2] "Yes" if the "IntensiveWakeUpThrottling" feature is enabled and the
+"can_intensively_throttle_low_nesting_level" param is "true".
+
+[^3] "No" if the "IntensiveWakeUpThrottling" feature is disabled.
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc
index 7c714037..b5178ce 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -593,7 +593,7 @@
                                              const ComputedStyle& style,
                                              const ComputedStyle* parent_style,
                                              StyleResolver* resolver) {
-  const ElementAnimations* element_animations =
+  ElementAnimations* element_animations =
       animating_element ? animating_element->GetElementAnimations() : nullptr;
 
   bool is_animation_style_change =
@@ -614,8 +614,22 @@
   const ComputedStyle* old_style =
       animating_element ? animating_element->GetComputedStyle() : nullptr;
   bool logical_property_mapping_change =
-      old_style && (old_style->Direction() != style.Direction() ||
-                    old_style->GetWritingMode() != style.GetWritingMode());
+      !old_style || old_style->Direction() != style.Direction() ||
+      old_style->GetWritingMode() != style.GetWritingMode();
+
+  if (logical_property_mapping_change && element_animations) {
+    // Update computed keyframes for any running animations that depend on
+    // logical properties.
+    for (auto& entry : element_animations->Animations()) {
+      Animation* animation = entry.key;
+      if (auto* keyframe_effect =
+              DynamicTo<KeyframeEffect>(animation->effect())) {
+        keyframe_effect->SetLogicalPropertyResolutionContext(
+            style.Direction(), style.GetWritingMode());
+        animation->UpdateIfNecessary();
+      }
+    }
+  }
 
   const CSSAnimationData* animation_data = style.Animations();
   const CSSAnimations* css_animations =
diff --git a/third_party/blink/renderer/core/animation/effect_input.cc b/third_party/blink/renderer/core/animation/effect_input.cc
index a3bbed98..ad548951 100644
--- a/third_party/blink/renderer/core/animation/effect_input.cc
+++ b/third_party/blink/renderer/core/animation/effect_input.cc
@@ -46,6 +46,7 @@
 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/frame/frame_console.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -707,6 +708,13 @@
                            ? element->GetDocument()
                            : *LocalDOMWindow::From(script_state)->document();
 
+  // Map logical to physical properties.
+  const ComputedStyle* style = element ? element->GetComputedStyle() : nullptr;
+  TextDirection text_direction =
+      style ? style->Direction() : TextDirection::kLtr;
+  WritingMode writing_mode =
+      style ? style->GetWritingMode() : WritingMode::kHorizontalTb;
+
   StringKeyframeVector parsed_keyframes;
   if (script_iterator.IsNull()) {
     parsed_keyframes = ConvertObjectForm(element, document, keyframes_obj,
@@ -717,6 +725,11 @@
                          script_state, exception_state);
   }
 
+  for (wtf_size_t i = 0; i < parsed_keyframes.size(); i++) {
+    StringKeyframe* keyframe = parsed_keyframes[i];
+    keyframe->SetLogicalPropertyResolutionContext(text_direction, writing_mode);
+  }
+
   if (!ValidatePartialKeyframes(parsed_keyframes)) {
     exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
                                       "Partial keyframes are not supported.");
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect.cc b/third_party/blink/renderer/core/animation/keyframe_effect.cc
index 2a4d80c..3744835 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect.cc
+++ b/third_party/blink/renderer/core/animation/keyframe_effect.cc
@@ -689,6 +689,18 @@
   return results;
 }
 
+void KeyframeEffect::SetLogicalPropertyResolutionContext(
+    TextDirection text_direction,
+    WritingMode writing_mode) {
+  if (auto* model = DynamicTo<StringKeyframeEffectModel>(Model())) {
+    if (model->SetLogicalPropertyResolutionContext(text_direction,
+                                                   writing_mode)) {
+      ClearEffects();
+      InvalidateAndNotifyOwner();
+    }
+  }
+}
+
 void KeyframeEffect::CountAnimatedProperties() const {
   if (target_element_) {
     Document& document = target_element_->GetDocument();
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect.h b/third_party/blink/renderer/core/animation/keyframe_effect.h
index 00731f4..f8f74217 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect.h
+++ b/third_party/blink/renderer/core/animation/keyframe_effect.h
@@ -142,6 +142,9 @@
   bool GetIgnoreCSSKeyframes() { return ignore_css_keyframes_; }
   void SetIgnoreCSSKeyframes() { ignore_css_keyframes_ = true; }
 
+  void SetLogicalPropertyResolutionContext(TextDirection text_direction,
+                                           WritingMode writing_mode);
+
  private:
   EffectModel::CompositeOperation CompositeInternal() const;
 
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect_model.cc b/third_party/blink/renderer/core/animation/keyframe_effect_model.cc
index 1a386fb..dd622ec 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect_model.cc
+++ b/third_party/blink/renderer/core/animation/keyframe_effect_model.cc
@@ -287,6 +287,24 @@
          Affects(PropertyHandle(GetCSSPropertyTranslate()));
 }
 
+bool KeyframeEffectModelBase::SetLogicalPropertyResolutionContext(
+    TextDirection text_direction,
+    WritingMode writing_mode) {
+  bool changed = false;
+  for (wtf_size_t i = 0; i < keyframes_.size(); i++) {
+    if (auto* string_keyframe = DynamicTo<StringKeyframe>(*keyframes_[i])) {
+      if (string_keyframe->HasLogicalProperty()) {
+        string_keyframe->SetLogicalPropertyResolutionContext(text_direction,
+                                                             writing_mode);
+        changed = true;
+      }
+    }
+  }
+  if (changed)
+    ClearCachedData();
+  return changed;
+}
+
 void KeyframeEffectModelBase::Trace(Visitor* visitor) const {
   visitor->Trace(keyframes_);
   visitor->Trace(keyframe_groups_);
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect_model.h b/third_party/blink/renderer/core/animation/keyframe_effect_model.h
index 49699926..d6267f72 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect_model.h
+++ b/third_party/blink/renderer/core/animation/keyframe_effect_model.h
@@ -159,6 +159,11 @@
 
   bool IsTransformRelatedEffect() const override;
 
+  // Update properties used in resolving logical properties. Returns true if
+  // one or more keyframes changed as a result of the update.
+  bool SetLogicalPropertyResolutionContext(TextDirection text_direction,
+                                           WritingMode writing_mode);
+
   virtual KeyframeEffectModelBase* Clone() = 0;
 
   void Trace(Visitor*) const override;
diff --git a/third_party/blink/renderer/core/animation/string_keyframe.cc b/third_party/blink/renderer/core/animation/string_keyframe.cc
index 3727ec5c..b247c2b 100644
--- a/third_party/blink/renderer/core/animation/string_keyframe.cc
+++ b/third_party/blink/renderer/core/animation/string_keyframe.cc
@@ -30,47 +30,54 @@
   }
 }
 
-const CSSValue* GetOrCreateCSSValueFrom(
-    const CSSProperty& property,
-    const MutableCSSPropertyValueSet& property_value_set) {
-  DCHECK_NE(property.PropertyID(), CSSPropertyID::kInvalid);
-  DCHECK_NE(property.PropertyID(), CSSPropertyID::kVariable);
-  if (!property.IsShorthand())
-    return property_value_set.GetPropertyCSSValue(property.PropertyID());
+bool IsLogicalProperty(CSSPropertyID property_id) {
+  const CSSProperty& property = CSSProperty::Get(property_id);
+  const CSSProperty& resolved_property = property.ResolveDirectionAwareProperty(
+      TextDirection::kLtr, WritingMode::kHorizontalTb);
+  return resolved_property.PropertyID() != property_id;
+}
 
-  // For shorthands create a special wrapper value, |CSSKeyframeShorthandValue|,
-  // which can be used to correctly serialize it given longhands that are
-  // present in this set.
-  return MakeGarbageCollected<CSSKeyframeShorthandValue>(
-      property.PropertyID(), property_value_set.ImmutableCopyIfNeeded());
+MutableCSSPropertyValueSet* CreateCssPropertyValueSet() {
+  return MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
 }
 
 }  // namespace
 
+using PropertyResolver = StringKeyframe::PropertyResolver;
+
 StringKeyframe::StringKeyframe(const StringKeyframe& copy_from)
     : Keyframe(copy_from.offset_, copy_from.composite_, copy_from.easing_),
       input_properties_(copy_from.input_properties_),
-      css_property_map_(copy_from.css_property_map_->MutableCopy()),
       presentation_attribute_map_(
           copy_from.presentation_attribute_map_->MutableCopy()),
-      svg_attribute_map_(copy_from.svg_attribute_map_) {}
+      svg_attribute_map_(copy_from.svg_attribute_map_),
+      has_logical_property_(copy_from.has_logical_property_),
+      text_direction_(copy_from.text_direction_),
+      writing_mode_(copy_from.writing_mode_) {
+  if (copy_from.css_property_map_)
+    css_property_map_ = copy_from.css_property_map_->MutableCopy();
+}
 
 MutableCSSPropertyValueSet::SetResult StringKeyframe::SetCSSPropertyValue(
-    const AtomicString& property_name,
+    const AtomicString& custom_property_name,
     const String& value,
     SecureContextMode secure_context_mode,
     StyleSheetContents* style_sheet_contents) {
   bool is_animation_tainted = true;
-  MutableCSSPropertyValueSet::SetResult result = css_property_map_->SetProperty(
-      property_name, value, false, secure_context_mode, style_sheet_contents,
-      is_animation_tainted);
+
+  auto* property_map = CreateCssPropertyValueSet();
+  MutableCSSPropertyValueSet::SetResult result = property_map->SetProperty(
+      custom_property_name, value, false, secure_context_mode,
+      style_sheet_contents, is_animation_tainted);
 
   const CSSValue* parsed_value =
-      css_property_map_->GetPropertyCSSValue(property_name);
+      property_map->GetPropertyCSSValue(custom_property_name);
 
   if (result.did_parse && parsed_value) {
     // Per specification we only keep properties around which are parsable.
-    input_properties_.Set(PropertyHandle(property_name), *parsed_value);
+    input_properties_.Set(PropertyHandle(custom_property_name),
+                          MakeGarbageCollected<PropertyResolver>(
+                              CSSPropertyID::kVariable, *parsed_value));
   }
 
   return result;
@@ -91,38 +98,51 @@
     return MutableCSSPropertyValueSet::SetResult{did_parse, did_change};
   }
 
-  // Use a temporary set for shorthands so that its longhands are stored
-  // separately and can later be used to construct a special shorthand value.
-  bool use_temporary_set = property.IsShorthand();
-
-  auto* property_value_set =
-      use_temporary_set ? MakeGarbageCollected<MutableCSSPropertyValueSet>(
-                              css_property_map_->CssParserMode())
-                        : css_property_map_.Get();
-
+  auto* property_value_set = CreateCssPropertyValueSet();
   MutableCSSPropertyValueSet::SetResult result =
       property_value_set->SetProperty(
           property_id, value, false, secure_context_mode, style_sheet_contents);
 
-  const CSSValue* parsed_value =
-      GetOrCreateCSSValueFrom(property, *property_value_set);
-  if (result.did_parse && parsed_value) {
-    // Per specification we only keep properties around which are parsable.
-    input_properties_.Set(PropertyHandle(property), parsed_value);
+  // TODO(crbug.com/1132078): Add flag to CSSProperty to track if it is for a
+  // logical style.
+  bool is_logical = false;
+  if (property.IsShorthand()) {
+    // Logical shorthands to not directly map to physical shorthands. Determine
+    // if the shorthand is for a logical property by checking the first
+    // longhand.
+    if (property_value_set->PropertyCount()) {
+      CSSPropertyValueSet::PropertyReference reference =
+          property_value_set->PropertyAt(0);
+      if (IsLogicalProperty(reference.Id()))
+        is_logical = true;
+    }
+  } else {
+    is_logical = IsLogicalProperty(property_id);
   }
+  if (is_logical)
+    has_logical_property_ = true;
 
-  if (use_temporary_set)
-    css_property_map_->MergeAndOverrideOnConflict(property_value_set);
+  if (result.did_parse) {
+    // Per specification we only keep properties around which are parsable.
+    auto* resolver = MakeGarbageCollected<PropertyResolver>(
+        property, property_value_set, is_logical);
+    input_properties_.Set(PropertyHandle(property), resolver);
+  }
 
   return result;
 }
 
 void StringKeyframe::SetCSSPropertyValue(const CSSProperty& property,
                                          const CSSValue& value) {
-  DCHECK_NE(property.PropertyID(), CSSPropertyID::kInvalid);
+  CSSPropertyID property_id = property.PropertyID();
+  DCHECK_NE(property_id, CSSPropertyID::kInvalid);
   DCHECK(!CSSAnimations::IsAnimationAffectingProperty(property));
-  input_properties_.Set(ToPropertyHandle(property, &value), value);
-  css_property_map_->SetProperty(property.PropertyID(), value, false);
+  DCHECK(!property.IsShorthand());
+  DCHECK(!IsLogicalProperty(property_id));
+  input_properties_.Set(
+      ToPropertyHandle(property, &value),
+      MakeGarbageCollected<PropertyResolver>(property_id, value));
+  InvalidateCssPropertyMap();
 }
 
 void StringKeyframe::SetPresentationAttributeValue(
@@ -146,6 +166,7 @@
 PropertyHandleSet StringKeyframe::Properties() const {
   // This is not used in time-critical code, so we probably don't need to
   // worry about caching this result.
+  EnsureCssPropertyMap();
   PropertyHandleSet properties;
   for (unsigned i = 0; i < css_property_map_->PropertyCount(); ++i) {
     CSSPropertyValueSet::PropertyReference property_reference =
@@ -185,7 +206,7 @@
   Keyframe::AddKeyframePropertiesToV8Object(object_builder, element);
   for (const auto& entry : input_properties_) {
     const PropertyHandle& property_handle = entry.key;
-    const CSSValue* property_value = entry.value;
+    const CSSValue* property_value = entry.value->CssValue();
     String property_name =
         AnimationInputHelpers::PropertyHandleToKeyframeAttribute(
             property_handle);
@@ -227,6 +248,57 @@
   return MakeGarbageCollected<StringKeyframe>(*this);
 }
 
+bool StringKeyframe::SetLogicalPropertyResolutionContext(
+    TextDirection text_direction,
+    WritingMode writing_mode) {
+  if (text_direction != text_direction_ || writing_mode != writing_mode_) {
+    text_direction_ = text_direction;
+    writing_mode_ = writing_mode;
+    if (has_logical_property_) {
+      // force a rebuild of the property map on the next property fetch.
+      InvalidateCssPropertyMap();
+      return true;
+    }
+  }
+  return false;
+}
+
+void StringKeyframe::EnsureCssPropertyMap() const {
+  if (css_property_map_)
+    return;
+
+  css_property_map_ =
+      MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
+
+  bool requires_sorting = false;
+  HeapVector<Member<PropertyResolver>> resolvers;
+  for (const auto& entry : input_properties_) {
+    const PropertyHandle& property_handle = entry.key;
+    if (!property_handle.IsCSSProperty())
+      continue;
+
+    if (property_handle.IsCSSCustomProperty()) {
+      CSSPropertyName property_name(property_handle.CustomPropertyName());
+      const CSSValue* value = entry.value->CssValue();
+      css_property_map_->SetProperty(CSSPropertyValue(property_name, *value));
+    } else {
+      PropertyResolver* resolver = entry.value;
+      if (resolver->IsLogical() || resolver->IsShorthand())
+        requires_sorting = true;
+      resolvers.push_back(resolver);
+    }
+  }
+
+  if (requires_sorting) {
+    std::stable_sort(resolvers.begin(), resolvers.end(),
+                     PropertyResolver::HasLowerPriority);
+  }
+
+  for (const auto& resolver : resolvers) {
+    resolver->AppendTo(css_property_map_, text_direction_, writing_mode_);
+  }
+}
+
 Keyframe::PropertySpecificKeyframe*
 StringKeyframe::CreatePropertySpecificKeyframe(
     const PropertyHandle& property,
@@ -304,4 +376,99 @@
       offset, std::move(easing), String(), EffectModel::kCompositeAdd);
 }
 
+// ----- Property Resolver -----
+
+PropertyResolver::PropertyResolver(CSSPropertyID property_id,
+                                   const CSSValue& css_value)
+    : property_id_(property_id), css_value_(css_value) {}
+
+PropertyResolver::PropertyResolver(
+    const CSSProperty& property,
+    const MutableCSSPropertyValueSet* property_value_set,
+    bool is_logical)
+    : property_id_(property.PropertyID()), is_logical_(is_logical) {
+  DCHECK_NE(property_id_, CSSPropertyID::kInvalid);
+  DCHECK_NE(property_id_, CSSPropertyID::kVariable);
+  if (!property.IsShorthand())
+    css_value_ = property_value_set->GetPropertyCSSValue(property_id_);
+  else
+    css_property_value_set_ = property_value_set->ImmutableCopyIfNeeded();
+}
+
+const CSSValue* PropertyResolver::CssValue() {
+  DCHECK(css_value_ || css_property_value_set_);
+
+  if (css_value_)
+    return css_value_;
+
+  // For shorthands create a special wrapper value, |CSSKeyframeShorthandValue|,
+  // which can be used to correctly serialize it given longhands that are
+  // present in this set.
+  css_value_ = MakeGarbageCollected<CSSKeyframeShorthandValue>(
+      property_id_, css_property_value_set_);
+  return css_value_;
+}
+
+void PropertyResolver::AppendTo(MutableCSSPropertyValueSet* property_value_set,
+                                TextDirection text_direction,
+                                WritingMode writing_mode) {
+  DCHECK(property_id_ != CSSPropertyID::kInvalid);
+  DCHECK(property_id_ != CSSPropertyID::kVariable);
+
+  if (css_property_value_set_) {
+    // Shorthand property. Extract longhands from css_property_value_set_.
+    if (is_logical_) {
+      // Walk set of properties converting each property name to its
+      // corresponding physical property.
+      for (unsigned i = 0; i < css_property_value_set_->PropertyCount(); i++) {
+        CSSPropertyValueSet::PropertyReference reference =
+            css_property_value_set_->PropertyAt(i);
+        SetProperty(property_value_set, reference.Id(), reference.Value(),
+                    text_direction, writing_mode);
+      }
+    } else {
+      property_value_set->MergeAndOverrideOnConflict(css_property_value_set_);
+    }
+  } else {
+    SetProperty(property_value_set, property_id_, *css_value_, text_direction,
+                writing_mode);
+  }
+}
+
+void PropertyResolver::SetProperty(
+    MutableCSSPropertyValueSet* property_value_set,
+    CSSPropertyID property_id,
+    const CSSValue& value,
+    TextDirection text_direction,
+    WritingMode writing_mode) {
+  const CSSProperty& physical_property =
+      CSSProperty::Get(property_id)
+          .ResolveDirectionAwareProperty(text_direction, writing_mode);
+  property_value_set->SetProperty(physical_property.PropertyID(), value);
+}
+
+void PropertyResolver::Trace(Visitor* visitor) const {
+  visitor->Trace(css_value_);
+  visitor->Trace(css_property_value_set_);
+}
+
+// static
+bool PropertyResolver::HasLowerPriority(PropertyResolver* first,
+                                        PropertyResolver* second) {
+  // Longhand properties take precedence over shorthand properties.
+  if (first->IsShorthand() != second->IsShorthand())
+    return first->IsShorthand();
+
+  // Physical properties take precedence over logical properties.
+  if (first->IsLogical() != second->IsLogical())
+    return first->IsLogical();
+
+  // Two shorthands with overlapping longhand properties are sorted based
+  // on the number of longhand properties in their expansions.
+  if (first->IsShorthand())
+    return first->ExpansionCount() > second->ExpansionCount();
+
+  return false;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/string_keyframe.h b/third_party/blink/renderer/core/animation/string_keyframe.h
index 87a7477d..b4f72d7c 100644
--- a/third_party/blink/renderer/core/animation/string_keyframe.h
+++ b/third_party/blink/renderer/core/animation/string_keyframe.h
@@ -23,20 +23,16 @@
 // StringKeyframe are expanded to shorthand and de-duplicated, with newer
 // properties replacing older ones. SVG attributes are similarly de-duplicated.
 //
-// TODO(smcgruer): By the spec, a StringKeyframe should not de-duplicate or
-// expand shorthand properties; that is done for computed keyframes.
 class CORE_EXPORT StringKeyframe : public Keyframe {
  public:
   StringKeyframe()
-      : css_property_map_(MakeGarbageCollected<MutableCSSPropertyValueSet>(
-            kHTMLStandardMode)),
-        presentation_attribute_map_(
+      : presentation_attribute_map_(
             MakeGarbageCollected<MutableCSSPropertyValueSet>(
                 kHTMLStandardMode)) {}
   StringKeyframe(const StringKeyframe& copy_from);
 
   MutableCSSPropertyValueSet::SetResult SetCSSPropertyValue(
-      const AtomicString& property_name,
+      const AtomicString& custom_property_name,
       const String& value,
       SecureContextMode,
       StyleSheetContents*);
@@ -46,6 +42,7 @@
       SecureContextMode,
       StyleSheetContents*);
   void SetCSSPropertyValue(const CSSProperty&, const CSSValue&);
+
   void SetPresentationAttributeValue(const CSSProperty&,
                                      const String& value,
                                      SecureContextMode,
@@ -53,13 +50,16 @@
   void SetSVGAttributeValue(const QualifiedName&, const String& value);
 
   const CSSValue& CssPropertyValue(const PropertyHandle& property) const {
+    EnsureCssPropertyMap();
     int index = -1;
-    if (property.IsCSSCustomProperty())
+    if (property.IsCSSCustomProperty()) {
       index =
           css_property_map_->FindPropertyIndex(property.CustomPropertyName());
-    else
+    } else {
+      DCHECK(!property.GetCSSProperty().IsShorthand());
       index = css_property_map_->FindPropertyIndex(
           property.GetCSSProperty().PropertyID());
+    }
     CHECK_GE(index, 0);
     return css_property_map_->PropertyAt(static_cast<unsigned>(index)).Value();
   }
@@ -86,6 +86,11 @@
 
   Keyframe* Clone() const override;
 
+  bool HasLogicalProperty() { return has_logical_property_; }
+
+  bool SetLogicalPropertyResolutionContext(TextDirection text_direction,
+                                           WritingMode writing_mode);
+
   void Trace(Visitor*) const override;
 
   class CSSPropertySpecificKeyframe
@@ -160,21 +165,75 @@
     String value_;
   };
 
+  class PropertyResolver : public GarbageCollected<PropertyResolver> {
+   public:
+    // Custom properties must use this version of the constructor.
+    PropertyResolver(CSSPropertyID property_id, const CSSValue& css_value);
+
+    // Shorthand and logical properties must use this version of the
+    // constructor.
+    PropertyResolver(const CSSProperty& property,
+                     const MutableCSSPropertyValueSet* property_value_set,
+                     bool is_logical);
+
+    static PropertyResolver* CreateCustomVariableResolver(
+        const CSSValue& css_value);
+
+    const CSSValue* CssValue();
+
+    void AppendTo(MutableCSSPropertyValueSet* property_value_set,
+                  TextDirection text_direction,
+                  WritingMode writing_mode);
+
+    void SetProperty(MutableCSSPropertyValueSet* property_value_set,
+                     CSSPropertyID property_id,
+                     const CSSValue& value,
+                     TextDirection text_direction,
+                     WritingMode writing_mode);
+
+    static bool HasLowerPriority(PropertyResolver* first,
+                                 PropertyResolver* second);
+
+    // Helper methods for resolving longhand name collisions.
+    // Longhands take priority over shorthands.
+    // Physical properties take priority over logical.
+    // Two shorthands with overlapping longhand properties are sorted based
+    // on the number of longhand properties in their expansions.
+    bool IsLogical() { return is_logical_; }
+    bool IsShorthand() { return css_property_value_set_; }
+    unsigned ExpansionCount() {
+      return css_property_value_set_ ? css_property_value_set_->PropertyCount()
+                                     : 1;
+    }
+
+    void Trace(Visitor* visitor) const;
+
+   private:
+    CSSPropertyID property_id_ = CSSPropertyID::kInvalid;
+    Member<const CSSValue> css_value_ = nullptr;
+    Member<ImmutableCSSPropertyValueSet> css_property_value_set_ = nullptr;
+    bool is_logical_ = false;
+  };
+
  private:
   Keyframe::PropertySpecificKeyframe* CreatePropertySpecificKeyframe(
       const PropertyHandle&,
       EffectModel::CompositeOperation effect_composite,
       double offset) const override;
 
+  void InvalidateCssPropertyMap() { css_property_map_ = nullptr; }
+  void EnsureCssPropertyMap() const;
+
   bool IsStringKeyframe() const override { return true; }
 
-  // The unresolved property and their values. This is needed for correct
-  // implementation of KeyframeEffect.getKeyframes(). We use a single list for
-  // CSS, SVG properties. The only requirement for a property value to be
-  // in this list is that it parses correctly.
-  //
+  // Mapping of unresolved properties to a their resolvers. A resolver knows
+  // how to expand shorthands to their corresponding longhand property names,
+  // convert logical to physical property names and compare precedence for
+  // resolving longhand name collisions.  The resolver also knows how to
+  // create serialized text for a shorthand, which is required for getKeyframes
+  // calls.
   // See: https://drafts.csswg.org/web-animations/#keyframes-section
-  HeapHashMap<PropertyHandle, Member<const CSSValue>> input_properties_;
+  HeapHashMap<PropertyHandle, Member<PropertyResolver>> input_properties_;
 
   // The resolved properties are computed from unresolved ones applying these
   // steps:
@@ -182,17 +241,21 @@
   //      one (e.g., margin, margin-top)
   //  2. Expand shorthands to longhands
   //  3. Expand logical properties to physical ones
-  //
-  // See:
-  // https://drafts.csswg.org/web-animations/#calculating-computed-keyframes
-  //
-  // TODO(816956): AFAICT we don't do (1) at the moment rather we parse and feed
-  // values into the MutableCSSPropertyValueSet which keeps replacing values as
-  // they come in. I am not sure if it leads to the same conflict resolution
-  // that web-animation expects. This needs more investigation.
-  Member<MutableCSSPropertyValueSet> css_property_map_;
+  mutable Member<MutableCSSPropertyValueSet> css_property_map_;
   Member<MutableCSSPropertyValueSet> presentation_attribute_map_;
   HashMap<const QualifiedName*, String> svg_attribute_map_;
+
+  // If the keyframes contain one or more logical properties, these need to be
+  // remapped to physical properties when the writing mode or text direction
+  // changes.
+  bool has_logical_property_ = false;
+
+  // The following properties are required for mapping logical to physical
+  // property names. Though the same for all keyframes within the same model,
+  // we store the value here to facilitate lazy evaluation of the CSS
+  // properties.
+  TextDirection text_direction_ = TextDirection::kLtr;
+  WritingMode writing_mode_ = WritingMode::kHorizontalTb;
 };
 
 using CSSPropertySpecificKeyframe = StringKeyframe::CSSPropertySpecificKeyframe;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
index c4f744a7..bc985e2 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
@@ -838,7 +838,7 @@
       item_result->shape_result->SnappedWidth().ClampNegativeToZero();
   item_result->can_break_after = true;
 
-  if (trailing_collapsible_space_.has_value() ||
+  if (trailing_collapsible_space_.has_value() &&
       trailing_collapsible_space_->item_result == item_result) {
     trailing_collapsible_space_.reset();
   }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc b/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc
index a4cffca1c..56b954b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc
@@ -154,10 +154,7 @@
   switch (type) {
     case kNotSet:
 #if DCHECK_IS_ON()
-      if (!read_unset_as_none_ &&
-          // TODO(crbug.com/1132619): Should compute all ink overflow when
-          // NGBlockFragmentation is enabled.
-          !RuntimeEnabledFeatures::LayoutNGBlockFragmentationEnabled())
+      if (!read_unset_as_none_)
         NOTREACHED();
       FALLTHROUGH;
 #endif
diff --git a/third_party/blink/renderer/core/scheduler_integration_tests/throttling_test.cc b/third_party/blink/renderer/core/scheduler_integration_tests/throttling_test.cc
index 602b4613..6c6bd298 100644
--- a/third_party/blink/renderer/core/scheduler_integration_tests/throttling_test.cc
+++ b/third_party/blink/renderer/core/scheduler_integration_tests/throttling_test.cc
@@ -303,33 +303,41 @@
         {features::kStopInBackground});
   }
 
-  void TestNoIntensiveThrotlingOnTitleOrFaviconUpdate(
+  // Expect a console message every second, for |num_1hz_messages| seconds.
+  // Then, expect a console messages every minute.
+  void ExpectRepeatingTimerConsoleMessages(int num_1hz_messages) {
+    for (int i = 0; i < num_1hz_messages; ++i) {
+      ConsoleMessages().clear();
+      platform_->RunForPeriod(base::TimeDelta::FromSeconds(1));
+      EXPECT_EQ(FilteredConsoleMessages().size(), 1U);
+    }
+
+    constexpr int kNumIterations = 3;
+    for (int i = 0; i < kNumIterations; ++i) {
+      ConsoleMessages().clear();
+      platform_->RunForPeriod(base::TimeDelta::FromSeconds(30));
+      // Task shouldn't execute earlier than expected.
+      EXPECT_EQ(FilteredConsoleMessages().size(), 0U);
+      platform_->RunForPeriod(base::TimeDelta::FromSeconds(30));
+      EXPECT_EQ(FilteredConsoleMessages().size(), 1U);
+    }
+  }
+
+  void TestNoIntensiveThrottlingOnTitleOrFaviconUpdate(
       const String& console_message) {
     // The page does not attempt to run onTimer in the first 5 minutes.
     platform_->RunForPeriod(base::TimeDelta::FromMinutes(5));
     EXPECT_THAT(FilteredConsoleMessages(), ElementsAre());
 
-    // At 5 minutes, a timer fires to run the afterFiveMinutes() function.
-    // This function does not communicate in the background, so the intensive
-    // throttling policy applies and onTimer() can only run after 1 minute.
-    platform_->RunForPeriod(base::TimeDelta::FromMinutes(1));
-    EXPECT_THAT(FilteredConsoleMessages(), ElementsAre(console_message));
-
-    ConsoleMessages().clear();
-
-    // Beyond this point intensive background throttling will not apply anymore
-    // since the page is communicating in the background from onTimer().
-
-    constexpr auto kTimeUntilNextCheck = base::TimeDelta::FromSeconds(30);
-    platform_->RunForPeriod(kTimeUntilNextCheck);
-
-    // Tasks are not throttled beyond the default background throttling behavior
-    // nor do they get to run more often.
-    Vector<String> expected_ouput(
-        base::ClampFloor<wtf_size_t>(kTimeUntilNextCheck /
-                                     kDefaultThrottledWakeUpInterval),
-        console_message);
-    EXPECT_THAT(FilteredConsoleMessages(), expected_ouput);
+    // onTimer() communicates in background and re-posts itself. The background
+    // communication inhibits intensive wake up throttling for 3 seconds, which
+    // allows the re-posted task to run after |kDefaultThrottledWakeUpInterval|.
+    constexpr int kNumIterations = 3;
+    for (int i = 0; i < kNumIterations; ++i) {
+      platform_->RunForPeriod(kDefaultThrottledWakeUpInterval);
+      EXPECT_THAT(FilteredConsoleMessages(), ElementsAre(console_message));
+      ConsoleMessages().clear();
+    }
   }
 
  private:
@@ -362,30 +370,35 @@
     "  }"
     "</script>";
 
-// A script that schedules a timer with a long delay that is not aligned on the
-// intensive throttling wake up interval.
+// A script that schedules a timer task which logs to the console. The timer
+// task has a high nesting level and its timeout is not aligned on the intensive
+// wake up throttling interval.
 constexpr char kLongUnalignedTimerScriptTemplate[] =
     "<script>"
-    "  function onTimer() {"
+    "  function onTimerWithHighNestingLevel() {"
     "     console.log('%s');"
     "  }"
-    "  setTimeout(onTimer, 342 * 1000);"
+    "  function onTimerWithLowNestingLevel(nesting_level) {"
+    "    if (nesting_level == 4) {"
+    "      setTimeout(onTimerWithHighNestingLevel, 338 * 1000);"
+    "    } else {"
+    "      setTimeout(onTimerWithLowNestingLevel, 1000, nesting_level + 1);"
+    "    }"
+    "  }"
+    "  setTimeout(onTimerWithLowNestingLevel, 1000, 1);"
     "</script>";
 
 // A time delta that matches the delay in the above script.
 constexpr base::TimeDelta kLongUnalignedTimerDelay =
     base::TimeDelta::FromSeconds(342);
 
-// Use to build a web-page ready to test intensive javascript throttling.
-// The page will differ in its definition of the maybeCommunicateInBackground()
-// function which has to be defined in a script passed in |communicate_script|.
+// Builds a page that waits 5 minutes and then creates a timer that reschedules
+// itself 50 times with 10 ms delay. The timer task logs |console_message| to
+// the console and invokes maybeCommunicateInBackground(). The caller must
+// provide the definition of maybeCommunicateInBackground() via
+// |communicate_script|.
 String BuildRepeatingTimerPage(const char* console_message,
                                const char* communicate_script) {
-  // A template for a page that waits 5 minutes on load then creates a timer
-  // that reschedules itself 50 times with 10 ms delay. Contains the minimimal
-  // page structure to simulate background communication with the user via title
-  // or favicon update. Needs to be augmented with a definition for
-  // maybeCommunicateInBackground;
   constexpr char kRepeatingTimerPageTemplate[] =
       "<html>"
       "<head>"
@@ -404,7 +417,7 @@
       "  }"
       "  setTimeout(afterFiveMinutes, 5 * 60 * 1000);"
       "</script>"
-      "%s"  // maybeCommunicateInBackground definition inserted here.
+      "%s"  // |communicate_script| inserted here
       "</body>"
       "</html>";
 
@@ -420,12 +433,10 @@
   SimRequest main_resource("https://example.com/", "text/html");
   LoadURL("https://example.com/");
 
-  const String console_message = BuildTimerConsoleMessage();
-
   // Page does not communicate with the user. Normal intensive throttling
   // applies.
-  main_resource.Complete(BuildRepeatingTimerPage(console_message.Utf8().c_str(),
-                                                 kCommunicationNop));
+  main_resource.Complete(BuildRepeatingTimerPage(
+      BuildTimerConsoleMessage().Utf8().c_str(), kCommunicationNop));
 
   GetDocument().GetPage()->GetPageScheduler()->SetPageVisible(false);
 
@@ -433,19 +444,20 @@
   platform_->RunForPeriod(base::TimeDelta::FromMinutes(5));
   EXPECT_THAT(FilteredConsoleMessages(), ElementsAre());
 
-  // After that, intensive throttling starts and there should be 1 wake up per
-  // minute.
-  platform_->RunForPeriod(base::TimeDelta::FromMinutes(1));
-  EXPECT_THAT(FilteredConsoleMessages(), ElementsAre(console_message));
-
-  // No tasks execute early.
-  platform_->RunForPeriod(base::TimeDelta::FromSeconds(30));
-  EXPECT_THAT(FilteredConsoleMessages(), ElementsAre(console_message));
-
-  // A minute after the last timer.
-  platform_->RunForPeriod(base::TimeDelta::FromSeconds(30));
-  EXPECT_THAT(FilteredConsoleMessages(),
-              ElementsAre(console_message, console_message));
+  // Expected execution:
+  //
+  // t = 5min 0s : afterFiveMinutes    nesting=1 (low)
+  // t = 5min 1s : onTimer             nesting=2 (low)     <
+  // t = 5min 2s : onTimer             nesting=3 (low)     < 4 seconds at 1 Hz
+  // t = 5min 3s : onTimer             nesting=4 (low)     <
+  // t = 5min 4s : onTimer             nesting=5 (high) ** <
+  // t = 6min    : onTimer             nesting=6 (high)
+  // t = 7min    : onTimer             nesting=7 (high)
+  // ...
+  //
+  // ** In a main frame, a task with high nesting level is 1-second aligned
+  //    when no task with high nesting level ran in the last minute.
+  ExpectRepeatingTimerConsoleMessages(4);
 }
 
 // Verify that a main frame timer that reposts itself with a 10 ms timeout runs
@@ -461,7 +473,7 @@
 
   GetDocument().GetPage()->GetPageScheduler()->SetPageVisible(false);
 
-  TestNoIntensiveThrotlingOnTitleOrFaviconUpdate(console_message);
+  TestNoIntensiveThrottlingOnTitleOrFaviconUpdate(console_message);
 }
 
 // Verify that a main frame timer that reposts itself with a 10 ms timeout runs
@@ -478,7 +490,7 @@
 
   GetDocument().GetPage()->GetPageScheduler()->SetPageVisible(false);
 
-  TestNoIntensiveThrotlingOnTitleOrFaviconUpdate(console_message);
+  TestNoIntensiveThrottlingOnTitleOrFaviconUpdate(console_message);
 }
 
 // Verify that a same-origin subframe timer that reposts itself with a 10 ms
@@ -492,9 +504,8 @@
   // possible to complete the iframe resource request before that.
   platform_->RunUntilIdle();
 
-  const String console_message = BuildTimerConsoleMessage();
   subframe_resource.Complete(BuildRepeatingTimerPage(
-      console_message.Utf8().c_str(), kCommunicationNop));
+      BuildTimerConsoleMessage().Utf8().c_str(), kCommunicationNop));
 
   GetDocument().GetPage()->GetPageScheduler()->SetPageVisible(false);
 
@@ -502,13 +513,20 @@
   platform_->RunForPeriod(base::TimeDelta::FromMinutes(5));
   EXPECT_THAT(FilteredConsoleMessages(), ElementsAre());
 
-  // After that, intensive throttling starts and there should be 1 wake up per
-  // minute.
-  platform_->RunForPeriod(base::TimeDelta::FromMinutes(1));
-  EXPECT_THAT(FilteredConsoleMessages(), ElementsAre(console_message));
-  platform_->RunForPeriod(base::TimeDelta::FromMinutes(1));
-  EXPECT_THAT(FilteredConsoleMessages(),
-              ElementsAre(console_message, console_message));
+  // Expected execution:
+  //
+  // t = 5min 0s : afterFiveMinutes    nesting=1 (low)
+  // t = 5min 1s : onTimer             nesting=2 (low)     <
+  // t = 5min 2s : onTimer             nesting=3 (low)     < 4 seconds at 1 Hz
+  // t = 5min 3s : onTimer             nesting=4 (low)     <
+  // t = 5min 4s : onTimer             nesting=5 (high) ** <
+  // t = 6min    : onTimer             nesting=6 (high)
+  // t = 7min    : onTimer             nesting=7 (high)
+  // ...
+  //
+  // ** In a same-origin frame, a task with high nesting level is 1-second
+  //    aligned when no task with high nesting level ran in the last minute.
+  ExpectRepeatingTimerConsoleMessages(4);
 }
 
 // Verify that a cross-origin subframe timer that reposts itself with a 10 ms
@@ -524,9 +542,8 @@
   // possible to complete the iframe resource request before that.
   platform_->RunUntilIdle();
 
-  const String console_message = BuildTimerConsoleMessage();
   subframe_resource.Complete(BuildRepeatingTimerPage(
-      console_message.Utf8().c_str(), kCommunicationNop));
+      BuildTimerConsoleMessage().Utf8().c_str(), kCommunicationNop));
 
   GetDocument().GetPage()->GetPageScheduler()->SetPageVisible(false);
 
@@ -534,13 +551,17 @@
   platform_->RunForPeriod(base::TimeDelta::FromMinutes(5));
   EXPECT_THAT(FilteredConsoleMessages(), ElementsAre());
 
-  // After that, intensive throttling starts and there should be 1 wake up per
-  // minute.
-  platform_->RunForPeriod(base::TimeDelta::FromMinutes(1));
-  EXPECT_THAT(FilteredConsoleMessages(), ElementsAre(console_message));
-  platform_->RunForPeriod(base::TimeDelta::FromMinutes(1));
-  EXPECT_THAT(FilteredConsoleMessages(),
-              ElementsAre(console_message, console_message));
+  // Expected execution:
+  //
+  // t = 5min 0s : afterFiveMinutes    nesting=1 (low)
+  // t = 5min 1s : onTimer             nesting=2 (low)  <
+  // t = 5min 2s : onTimer             nesting=3 (low)  < 3 seconds at 1 Hz
+  // t = 5min 3s : onTimer             nesting=4 (low)  <
+  // t = 6min    : onTimer             nesting=5 (high)
+  // t = 7min    : onTimer             nesting=6 (high)
+  // t = 8min    : onTimer             nesting=7 (high)
+  // ...
+  ExpectRepeatingTimerConsoleMessages(3);
 }
 
 // Verify that a main frame timer with a long timeout runs at the desired run
diff --git a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
index 09faa83..b1251b6 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
@@ -612,9 +612,9 @@
     auto* payment_credential_remote =
         CredentialManagerProxy::From(resolver->GetScriptState())
             ->PaymentCredential();
+    auto credential_id = credential->info->raw_id;
     payment_credential_remote->StorePaymentCredential(
-        std::move(payment_instrument), credential->info->raw_id,
-        options->rp()->id(),
+        std::move(payment_instrument), credential_id, options->rp()->id(),
         WTF::Bind(
             &OnPaymentCredentialCreationComplete,
             WTF::Passed(std::make_unique<ScopedPromiseResolver>(resolver)),
diff --git a/third_party/blink/renderer/modules/hid/hid_device.idl b/third_party/blink/renderer/modules/hid/hid_device.idl
index c2c7acd..9a5e09e0 100644
--- a/third_party/blink/renderer/modules/hid/hid_device.idl
+++ b/third_party/blink/renderer/modules/hid/hid_device.idl
@@ -17,11 +17,11 @@
     readonly attribute boolean opened;
 
     // The 16-bit vendor and product IDs reported by the device.
-    [HighEntropy=Direct, Measure] readonly attribute unsigned short vendorId;
-    [HighEntropy=Direct, Measure] readonly attribute unsigned short productId;
+    readonly attribute unsigned short vendorId;
+    readonly attribute unsigned short productId;
 
     // The device name, as reported by the device and shown in the chooser.
-    [HighEntropy=Direct, Measure] readonly attribute DOMString productName;
+    readonly attribute DOMString productName;
 
     // An array of the top-level collections within the HID report descriptor,
     // in the order they were encountered within the descriptor. The collections
diff --git a/third_party/blink/renderer/modules/hid/hid_report_item.idl b/third_party/blink/renderer/modules/hid/hid_report_item.idl
index f091fad..eed37327 100644
--- a/third_party/blink/renderer/modules/hid/hid_report_item.idl
+++ b/third_party/blink/renderer/modules/hid/hid_report_item.idl
@@ -34,21 +34,21 @@
 ] interface HIDReportItem {
     // True if the item represents an absolute measurement (e.g. joystick tilt)
     // or false if it represents a relative measurement (e.g. mouse movement).
-    [HighEntropy=Direct, Measure] readonly attribute boolean isAbsolute;
+    readonly attribute boolean isAbsolute;
 
     // True if the item is an Array or false if it is a Variable. Array items
     // are typically used when a device needs to represent a large number of
     // button-type inputs, but only a few inputs need to be active at once.
     // Variable items require space in the report for each input, but can report
     // all inputs simultaneously.
-    [HighEntropy=Direct, Measure] readonly attribute boolean isArray;
+    readonly attribute boolean isArray;
 
     // True if the usages for this item are defined by |usageMinimum| and
     // |usageMaximum| or false if the usages are defined by |usages|.
-    [HighEntropy=Direct, Measure] readonly attribute boolean isRange;
+    readonly attribute boolean isRange;
 
     // True if the item uses an out-of-bounds value when there is no input.
-    [HighEntropy=Direct, Measure] readonly attribute boolean hasNull;
+    readonly attribute boolean hasNull;
 
     // An ordered list of 32-bit usage values associated with this item. Unused
     // if |isRange| is true. If |reportCount| is two or more, usages are
@@ -58,19 +58,19 @@
     // The minimum and maximum usage values associated with this item. Unused if
     // |isRange| is false. If |reportCount| is two or more, usages are assigned
     // starting from |usageMinimum| and increment by one.
-    [HighEntropy=Direct, Measure] readonly attribute unsigned long usageMinimum;
-    [HighEntropy=Direct, Measure] readonly attribute unsigned long usageMaximum;
+    readonly attribute unsigned long usageMinimum;
+    readonly attribute unsigned long usageMaximum;
 
     // The size of a single field described by this item, in bits.
-    [HighEntropy=Direct, Measure] readonly attribute unsigned short reportSize;
+    readonly attribute unsigned short reportSize;
 
     // The number of similar fields described by this item. The total size of
     // the item described by this report is |reportSize| * |reportCount| bits.
-    [HighEntropy=Direct, Measure] readonly attribute unsigned short reportCount;
+    readonly attribute unsigned short reportCount;
 
     // The base 10 exponent of the units for this report item. For instance, for
     // kilograms |unitExponent| would be 3 and for micrograms it would be -6.
-    [HighEntropy=Direct, Measure] readonly attribute byte unitExponent;
+    readonly attribute byte unitExponent;
 
     // The unit system determines which units are used for length, mass, time,
     // temperature, current, and luminous intensity. May be "none" if the values
@@ -81,23 +81,23 @@
     // unit definition. For instance, for acceleration all factors would have
     // an exponent of 0 except |unitFactorLengthExponent| which would be 1 and
     // |unitFactorTimeExponent| which would be -2.
-    [HighEntropy=Direct, Measure] readonly attribute byte unitFactorLengthExponent;
-    [HighEntropy=Direct, Measure] readonly attribute byte unitFactorMassExponent;
-    [HighEntropy=Direct, Measure] readonly attribute byte unitFactorTimeExponent;
-    [HighEntropy=Direct, Measure] readonly attribute byte unitFactorTemperatureExponent;
-    [HighEntropy=Direct, Measure] readonly attribute byte unitFactorCurrentExponent;
-    [HighEntropy=Direct, Measure] readonly attribute byte unitFactorLuminousIntensityExponent;
+    readonly attribute byte unitFactorLengthExponent;
+    readonly attribute byte unitFactorMassExponent;
+    readonly attribute byte unitFactorTimeExponent;
+    readonly attribute byte unitFactorTemperatureExponent;
+    readonly attribute byte unitFactorCurrentExponent;
+    readonly attribute byte unitFactorLuminousIntensityExponent;
 
     // The minimum and maximum values that may be represented by this input. A
     // device with |hasNull| may report a value outside this range to indicate
     // no input.
-    [HighEntropy=Direct, Measure] readonly attribute long logicalMinimum;
-    [HighEntropy=Direct, Measure] readonly attribute long logicalMaximum;
+    readonly attribute long logicalMinimum;
+    readonly attribute long logicalMaximum;
 
     // The minimum and maximum values, scaled to the units described by |unit|
     // and |unitExponent|.
-    [HighEntropy=Direct, Measure] readonly attribute long physicalMinimum;
-    [HighEntropy=Direct, Measure] readonly attribute long physicalMaximum;
+    readonly attribute long physicalMinimum;
+    readonly attribute long physicalMaximum;
 
     // The strings associated with this item.
     readonly attribute FrozenArray<DOMString> strings;
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.cc b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.cc
index e327e97..eec2425 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.cc
@@ -457,19 +457,27 @@
     return pan_set_.IsEmpty() || tilt_set_.IsEmpty() || zoom_set_.IsEmpty();
   }
 
-  double Fitness(const MediaTrackConstraintSetPlatform& basic_set) const {
-    return NumericRangeFitness(basic_set.pan, pan_set_) +
+  double Fitness(const MediaTrackConstraintSetPlatform& basic_set,
+                 bool pan_tilt_zoom_supported) const {
+    double initial_fitness =
+        ((basic_set.pan.IsPresent() || basic_set.tilt.IsPresent() ||
+          basic_set.zoom.IsPresent()) &&
+         !pan_tilt_zoom_supported)
+            ? 1.0
+            : 0.0;
+
+    return initial_fitness + NumericRangeFitness(basic_set.pan, pan_set_) +
            NumericRangeFitness(basic_set.tilt, tilt_set_) +
            NumericRangeFitness(basic_set.zoom, zoom_set_);
   }
 
   const char* FailedConstraintName() const {
     MediaTrackConstraintSetPlatform dummy;
-    if (!pan_set_.IsEmpty())
+    if (pan_set_.IsEmpty())
       return dummy.pan.GetName();
-    if (!tilt_set_.IsEmpty())
+    if (tilt_set_.IsEmpty())
       return dummy.tilt.GetName();
-    if (!zoom_set_.IsEmpty())
+    if (zoom_set_.IsEmpty())
       return dummy.zoom.GetName();
 
     // No failed constraint.
@@ -557,17 +565,17 @@
     return false;
   }
 
-  if (constraint_set.pan.IsPresent() && !device.pan_tilt_zoom_supported) {
+  if (constraint_set.pan.HasMandatory() && !device.pan_tilt_zoom_supported) {
     UpdateFailedConstraintName(constraint_set.pan, failed_constraint_name);
     return false;
   }
 
-  if (constraint_set.tilt.IsPresent() && !device.pan_tilt_zoom_supported) {
+  if (constraint_set.tilt.HasMandatory() && !device.pan_tilt_zoom_supported) {
     UpdateFailedConstraintName(constraint_set.tilt, failed_constraint_name);
     return false;
   }
 
-  if (constraint_set.zoom.IsPresent() && !device.pan_tilt_zoom_supported) {
+  if (constraint_set.zoom.HasMandatory() && !device.pan_tilt_zoom_supported) {
     UpdateFailedConstraintName(constraint_set.zoom, failed_constraint_name);
     return false;
   }
@@ -614,7 +622,7 @@
                         const MediaTrackConstraintSetPlatform& constraint_set,
                         VideoTrackAdapterSettings* track_settings) {
   return DeviceFitness(device, constraint_set) +
-         ptz_state.Fitness(constraint_set) +
+         ptz_state.Fitness(constraint_set, device.pan_tilt_zoom_supported) +
          candidate_format.Fitness(constraint_set, track_settings) +
          OptionalBoolFitness(noise_reduction,
                              constraint_set.goog_noise_reduction);
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device_test.cc
index 1acb3c4..9995d69b 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device_test.cc
@@ -24,14 +24,12 @@
 const char kDeviceID3[] = "fake_device_3";
 const char kDeviceID4[] = "fake_device_4";
 const char kDeviceID5[] = "fake_device_5";
-const char kDeviceID6[] = "fake_device_6";
 
 const char kGroupID1[] = "fake_group_1";
 const char kGroupID2[] = "fake_group_2";
 const char kGroupID3[] = "fake_group_3";
 const char kGroupID4[] = "fake_group_4";
 const char kGroupID5[] = "fake_group_5";
-const char kGroupID6[] = "fake_group_6";
 
 const std::vector<DoubleConstraint MediaTrackConstraintSetPlatform::*>
     kPanTiltZoomConstraints = {
@@ -96,7 +94,7 @@
         media::VideoCaptureFormat(gfx::Size(1000, 1000), 20.0f,
                                   media::PIXEL_FORMAT_I420),
     };
-    device.pan_tilt_zoom_supported = true;
+    device.pan_tilt_zoom_supported = false;
     capabilities_.device_capabilities.push_back(std::move(device));
 
     // A low-resolution device.
@@ -158,6 +156,7 @@
     device.facing_mode = media::MEDIA_VIDEO_FACING_ENVIRONMENT;
     device.formats = {media::VideoCaptureFormat(gfx::Size(640, 480), 30.0f,
                                                 media::PIXEL_FORMAT_Y16)};
+    device.pan_tilt_zoom_supported = true;
     capabilities_.device_capabilities.push_back(std::move(device));
 
     // A device that reports invalid frame rates. These devices exist and should
@@ -176,15 +175,6 @@
     device.pan_tilt_zoom_supported = true;
     capabilities_.device_capabilities.push_back(std::move(device));
 
-    // A camera device without PTZ.
-    device.device_id = kDeviceID6;
-    device.group_id = kGroupID6;
-    device.facing_mode = media::MEDIA_VIDEO_FACING_NONE;
-    device.formats = {media::VideoCaptureFormat(gfx::Size(640, 480), 30.0f,
-                                                media::PIXEL_FORMAT_I420)};
-    device.pan_tilt_zoom_supported = false;
-    capabilities_.device_capabilities.push_back(std::move(device));
-
     capabilities_.noise_reduction_capabilities = {
         base::Optional<bool>(),
         base::Optional<bool>(true),
@@ -195,7 +185,6 @@
     low_res_device_ = &capabilities_.device_capabilities[1];
     high_res_device_ = &capabilities_.device_capabilities[2];
     invalid_frame_rate_device_ = &capabilities_.device_capabilities[4];
-    no_ptz_device_ = &capabilities_.device_capabilities[5];
     default_closest_format_ = &default_device_->formats[1];
     low_res_closest_format_ = &low_res_device_->formats[2];
     high_res_closest_format_ = &high_res_device_->formats[3];
@@ -213,7 +202,6 @@
   const VideoInputDeviceCapabilities* low_res_device_;
   const VideoInputDeviceCapabilities* high_res_device_;
   const VideoInputDeviceCapabilities* invalid_frame_rate_device_;
-  const VideoInputDeviceCapabilities* no_ptz_device_;
   // Closest formats to the default settings.
   const media::VideoCaptureFormat* default_closest_format_;
   const media::VideoCaptureFormat* low_res_closest_format_;
@@ -427,38 +415,30 @@
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
-       OverconstrainedOnPanTiltZoom) {
+       OverconstrainedOnMandatoryPanTiltZoom) {
   for (auto& constraint : kPanTiltZoomConstraints) {
     constraint_factory_.Reset();
-    constraint_factory_.basic().device_id.SetExact(no_ptz_device_->device_id);
-    (constraint_factory_.basic().*constraint).SetIdeal(1);
+    constraint_factory_.basic().device_id.SetExact(default_device_->device_id);
+    (constraint_factory_.basic().*constraint).SetMin(1);
     auto result = SelectSettings();
     EXPECT_FALSE(result.HasValue());
-    EXPECT_EQ((constraint_factory_.basic().*constraint).GetName(),
+    EXPECT_EQ(constraint_factory_.basic().device_id.GetName(),
               result.failed_constraint_name());
 
     constraint_factory_.Reset();
-    constraint_factory_.basic().device_id.SetExact(no_ptz_device_->device_id);
-    (constraint_factory_.basic().*constraint).SetMin(1);
-    result = SelectSettings();
-    EXPECT_FALSE(result.HasValue());
-    EXPECT_EQ((constraint_factory_.basic().*constraint).GetName(),
-              result.failed_constraint_name());
-
-    constraint_factory_.Reset();
-    constraint_factory_.basic().device_id.SetExact(no_ptz_device_->device_id);
+    constraint_factory_.basic().device_id.SetExact(default_device_->device_id);
     (constraint_factory_.basic().*constraint).SetMax(1);
     result = SelectSettings();
     EXPECT_FALSE(result.HasValue());
-    EXPECT_EQ((constraint_factory_.basic().*constraint).GetName(),
+    EXPECT_EQ(constraint_factory_.basic().device_id.GetName(),
               result.failed_constraint_name());
 
     constraint_factory_.Reset();
-    constraint_factory_.basic().device_id.SetExact(no_ptz_device_->device_id);
+    constraint_factory_.basic().device_id.SetExact(default_device_->device_id);
     (constraint_factory_.basic().*constraint).SetExact(1);
     result = SelectSettings();
     EXPECT_FALSE(result.HasValue());
-    EXPECT_EQ((constraint_factory_.basic().*constraint).GetName(),
+    EXPECT_EQ(constraint_factory_.basic().device_id.GetName(),
               result.failed_constraint_name());
   }
 }
@@ -1896,8 +1876,8 @@
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
     // The algorithm should prefer the first device that supports PTZ natively,
-    // which is the default device.
-    EXPECT_EQ(default_device_->device_id.Utf8(), result.device_id());
+    // which is the low-res device.
+    EXPECT_EQ(low_res_device_->device_id.Utf8(), result.device_id());
     if (constraint == &MediaTrackConstraintSetPlatform::pan)
       EXPECT_EQ(3, result.pan().value());
     else if (constraint == &MediaTrackConstraintSetPlatform::tilt)
@@ -1914,8 +1894,8 @@
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
     // The algorithm should prefer the first device that supports PTZ
-    // natively, which is the default device.
-    EXPECT_EQ(default_device_->device_id.Utf8(), result.device_id());
+    // natively, which is the low-res device.
+    EXPECT_EQ(low_res_device_->device_id.Utf8(), result.device_id());
     if (constraint == &MediaTrackConstraintSetPlatform::pan)
       EXPECT_EQ(2, result.pan().value());
     else if (constraint == &MediaTrackConstraintSetPlatform::tilt)
@@ -1932,8 +1912,8 @@
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
     // The algorithm should prefer the first device that supports PTZ
-    // natively, which is the default device.
-    EXPECT_EQ(default_device_->device_id.Utf8(), result.device_id());
+    // natively, which is the low-res device.
+    EXPECT_EQ(low_res_device_->device_id.Utf8(), result.device_id());
     if (constraint == &MediaTrackConstraintSetPlatform::pan)
       EXPECT_EQ(4, result.pan().value());
     else if (constraint == &MediaTrackConstraintSetPlatform::tilt)
@@ -1951,8 +1931,8 @@
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
     // The algorithm should prefer the first device that supports PTZ
-    // natively, which is the default device.
-    EXPECT_EQ(default_device_->device_id.Utf8(), result.device_id());
+    // natively, which is the low-res device.
+    EXPECT_EQ(low_res_device_->device_id.Utf8(), result.device_id());
     if (constraint == &MediaTrackConstraintSetPlatform::pan)
       EXPECT_EQ(2, result.pan().value());
     else if (constraint == &MediaTrackConstraintSetPlatform::tilt)
@@ -1969,8 +1949,8 @@
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
     // The algorithm should select the first device that supports the ideal PTZ
-    // constraint natively, which is the default device.
-    EXPECT_EQ(default_device_->device_id.Utf8(), result.device_id());
+    // constraint natively, which is the low-res device.
+    EXPECT_EQ(low_res_device_->device_id.Utf8(), result.device_id());
     if (constraint == &MediaTrackConstraintSetPlatform::pan)
       EXPECT_EQ(3, result.pan().value());
     else if (constraint == &MediaTrackConstraintSetPlatform::tilt)
@@ -1980,6 +1960,50 @@
   }
 }
 
+TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, PresentPanTiltZoom) {
+  for (auto& constraint : kPanTiltZoomConstraints) {
+    constraint_factory_.Reset();
+    (constraint_factory_.basic().*constraint).SetIsPresent(true);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // The algorithm should select the first device that supports the boolean
+    // PTZ constraint natively, which is the low-res device.
+    EXPECT_EQ(low_res_device_->device_id.Utf8(), result.device_id());
+  }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
+       PresentPanTiltZoomOnSystemWithoutPanTiltZoomCamera) {
+  // Simulate a system with camera that does not support PTZ.
+  // Manually adding device capabilities because VideoDeviceCaptureCapabilities
+  // is move only.
+  VideoDeviceCaptureCapabilities capabilities;
+  VideoInputDeviceCapabilities device;
+  device.device_id = kDeviceID1;
+  device.facing_mode = media::MEDIA_VIDEO_FACING_NONE;
+  device.formats = {
+      media::VideoCaptureFormat(gfx::Size(200, 200), 40.0f,
+                                media::PIXEL_FORMAT_I420),
+  };
+  device.pan_tilt_zoom_supported = false;
+  capabilities.device_capabilities.push_back(std::move(device));
+  capabilities.noise_reduction_capabilities = {
+      base::Optional<bool>(),
+      base::Optional<bool>(true),
+      base::Optional<bool>(false),
+  };
+
+  for (auto& constraint : kPanTiltZoomConstraints) {
+    constraint_factory_.Reset();
+    (constraint_factory_.basic().*constraint).SetIsPresent(true);
+    auto constraints = constraint_factory_.CreateMediaConstraints();
+    auto result = SelectSettingsVideoDeviceCapture(capabilities, constraints);
+    EXPECT_TRUE(result.HasValue());
+    // The algorithm should select one device, even if it doesn't support PTZ.
+    EXPECT_EQ(std::string(kDeviceID1), result.device_id());
+  }
+}
+
 // The "Advanced" tests check selection criteria involving advanced constraint
 // sets.
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
@@ -2557,7 +2581,7 @@
 
     MediaTrackConstraintSetPlatform& advanced2 =
         constraint_factory_.AddAdvanced();
-    advanced2.device_id.SetExact({no_ptz_device_->device_id});
+    advanced2.device_id.SetExact({default_device_->device_id});
     (advanced2.*constraint).SetExact(4);
 
     MediaTrackConstraintSetPlatform& advanced3 =
@@ -2633,13 +2657,13 @@
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, AdvancedPanTiltZoom) {
   for (auto& constraint : kPanTiltZoomConstraints) {
     constraint_factory_.Reset();
-    constraint_factory_.basic().device_id.SetExact(no_ptz_device_->device_id);
+    constraint_factory_.basic().device_id.SetExact(default_device_->device_id);
     MediaTrackConstraintSetPlatform& advanced =
         constraint_factory_.AddAdvanced();
     (advanced.*constraint).SetExact(3);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    EXPECT_EQ(no_ptz_device_->device_id.Utf8(), result.device_id());
+    EXPECT_EQ(default_device_->device_id.Utf8(), result.device_id());
     // The advanced set must be ignored because the device does not support PTZ.
     if (constraint == &MediaTrackConstraintSetPlatform::pan)
       EXPECT_FALSE(result.pan().has_value());
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_request.cc b/third_party/blink/renderer/modules/mediastream/user_media_request.cc
index 877e0eb..b32d6a7b 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_request.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_request.cc
@@ -339,7 +339,21 @@
   if (error_state.HadException())
     return nullptr;
 
-  if (media_type == UserMediaRequest::MediaType::kDisplayMedia) {
+  if (media_type == UserMediaRequest::MediaType::kUserMedia &&
+      !video.IsNull()) {
+    if (video.Basic().pan.HasMandatory()) {
+      error_state.ThrowTypeError("Mandatory pan constraint is not supported");
+      return nullptr;
+    }
+    if (video.Basic().tilt.HasMandatory()) {
+      error_state.ThrowTypeError("Mandatory tilt constraint is not supported");
+      return nullptr;
+    }
+    if (video.Basic().zoom.HasMandatory()) {
+      error_state.ThrowTypeError("Mandatory zoom constraint is not supported");
+      return nullptr;
+    }
+  } else if (media_type == UserMediaRequest::MediaType::kDisplayMedia) {
     // https://w3c.github.io/mediacapture-screen-share/#mediadevices-additions
     // MediaDevices Additions
     // The user agent MUST reject audio-only requests.
diff --git a/third_party/blink/renderer/modules/webshare/navigator_share.cc b/third_party/blink/renderer/modules/webshare/navigator_share.cc
index e6ad295..9d0bb56 100644
--- a/third_party/blink/renderer/modules/webshare/navigator_share.cc
+++ b/third_party/blink/renderer/modules/webshare/navigator_share.cc
@@ -133,8 +133,10 @@
                   {SchedulingPolicy::RecordMetricsForBackForwardCache()})) {}
 
 void NavigatorShare::ShareClientImpl::Callback(mojom::blink::ShareError error) {
-  if (navigator_)
-    navigator_->clients_.erase(this);
+  if (navigator_) {
+    DCHECK_EQ(navigator_->client_, this);
+    navigator_->client_ = nullptr;
+  }
 
   if (error == mojom::blink::ShareError::OK) {
     UseCounter::Count(ExecutionContext::From(resolver_->GetScriptState()),
@@ -173,7 +175,7 @@
 
 void NavigatorShare::Trace(Visitor* visitor) const {
   visitor->Trace(service_remote_);
-  visitor->Trace(clients_);
+  visitor->Trace(client_);
   Supplement<Navigator>::Trace(visitor);
 }
 
@@ -213,6 +215,12 @@
           ? WebFeature::kWebSharePolicyAllow
           : WebFeature::kWebSharePolicyDisallow);
 
+  if (client_) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "A earlier share had not yet completed.");
+    return ScriptPromise();
+  }
+
   if (!LocalFrame::ConsumeTransientUserActivation(window->GetFrame())) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kNotAllowedError,
@@ -238,10 +246,7 @@
 
   bool has_files = HasFiles(*data);
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ShareClientImpl* client =
-      MakeGarbageCollected<ShareClientImpl>(this, has_files, resolver);
-  clients_.insert(client);
-  ScriptPromise promise = resolver->Promise();
+  client_ = MakeGarbageCollected<ShareClientImpl>(this, has_files, resolver);
 
   WTF::Vector<mojom::blink::SharedFilePtr> files;
   uint64_t total_bytes = 0;
@@ -264,9 +269,9 @@
   service_remote_->Share(
       data->hasTitle() ? data->title() : g_empty_string,
       data->hasText() ? data->text() : g_empty_string, url, std::move(files),
-      WTF::Bind(&ShareClientImpl::Callback, WrapPersistent(client)));
+      WTF::Bind(&ShareClientImpl::Callback, WrapPersistent(client_.Get())));
 
-  return promise;
+  return resolver->Promise();
 }
 
 ScriptPromise NavigatorShare::share(ScriptState* script_state,
@@ -277,10 +282,10 @@
 }
 
 void NavigatorShare::OnConnectionError() {
-  for (auto& client : clients_) {
-    client->OnConnectionError();
+  if (client_) {
+    client_->OnConnectionError();
+    client_ = nullptr;
   }
-  clients_.clear();
   service_remote_.reset();
 }
 
diff --git a/third_party/blink/renderer/modules/webshare/navigator_share.h b/third_party/blink/renderer/modules/webshare/navigator_share.h
index f77d235..be5fe4cc 100644
--- a/third_party/blink/renderer/modules/webshare/navigator_share.h
+++ b/third_party/blink/renderer/modules/webshare/navigator_share.h
@@ -56,7 +56,8 @@
   // |NavigatorShare| is not ExecutionContext-associated.
   HeapMojoRemote<blink::mojom::blink::ShareService> service_remote_{nullptr};
 
-  HeapHashSet<Member<ShareClientImpl>> clients_;
+  // Represents a user's current intent to share some data.
+  Member<ShareClientImpl> client_ = nullptr;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.cc b/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.cc
index 9ec4c809..db83df8 100644
--- a/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.cc
+++ b/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.cc
@@ -186,7 +186,7 @@
 
   sk_sp<SkData> font_data = SkData::MakeFromFD(font_file.GetPlatformFile());
 
-  if (font_data->isEmpty()) {
+  if (!font_data || font_data->isEmpty()) {
     LOG(ERROR) << "Received file descriptor has 0 size.";
     lookup_latency_histogram_failure.CountMicroseconds(elapsed_timer.Elapsed());
     return nullptr;
diff --git a/third_party/blink/renderer/platform/scheduler/common/features.cc b/third_party/blink/renderer/platform/scheduler/common/features.cc
index c1e2a25..b6853b9 100644
--- a/third_party/blink/renderer/platform/scheduler/common/features.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/features.cc
@@ -121,5 +121,23 @@
   return base::TimeDelta::FromSeconds(seconds);
 }
 
+bool CanIntensivelyThrottleLowNestingLevel() {
+  DCHECK(IsIntensiveWakeUpThrottlingEnabled());
+
+  static const base::FeatureParam<bool> kFeatureParam{
+      &features::kIntensiveWakeUpThrottling,
+      kIntensiveWakeUpThrottling_CanIntensivelyThrottleLowNestingLevel_Name,
+      kIntensiveWakeUpThrottling_CanIntensivelyThrottleLowNestingLevel_Default};
+
+  bool value =
+      kIntensiveWakeUpThrottling_CanIntensivelyThrottleLowNestingLevel_Default;
+  if (GetIntensiveWakeUpThrottlingPolicyOverride() ==
+      PolicyOverride::NO_OVERRIDE) {
+    value = kFeatureParam.Get();
+  }
+
+  return value;
+}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/common/features.h b/third_party/blink/renderer/platform/scheduler/common/features.h
index 4f0eb65..183cc4e 100644
--- a/third_party/blink/renderer/platform/scheduler/common/features.h
+++ b/third_party/blink/renderer/platform/scheduler/common/features.h
@@ -181,10 +181,12 @@
 const base::Feature kHighPriorityDatabaseTaskType{
     "HighPriorityDatabaseTaskType", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// When features::kIntensiveWakeUpThrottling is enabled, wake ups from
-// throttleable TaskQueues are limited to 1 per
+// When features::kIntensiveWakeUpThrottling is enabled, wake ups from timers
+// with a high nesting level are limited to 1 per
 // GetIntensiveWakeUpThrottlingDurationBetweenWakeUp() in a page that has been
-// backgrounded for GetIntensiveWakeUpThrottlingGracePeriod().
+// backgrounded for GetIntensiveWakeUpThrottlingGracePeriod(). If
+// CanIntensivelyThrottleLowNestingLevel() is true, this policy is also applied
+// to timers with a non-zero delay and a low nesting level.
 //
 // Intensive wake up throttling is enforced in addition to other throttling
 // mechanisms:
@@ -193,7 +195,6 @@
 //
 // Feature tracking bug: https://crbug.com/1075553
 //
-//
 // Note that features::kIntensiveWakeUpThrottling should not be read from;
 // rather the provided accessors should be used, which also take into account
 // the managed policy override of the feature.
@@ -204,8 +205,13 @@
 constexpr const char*
     kIntensiveWakeUpThrottling_DurationBetweenWakeUpsSeconds_Name =
         "duration_between_wake_ups_seconds";
-
 constexpr int kIntensiveWakeUpThrottling_GracePeriodSeconds_Default = 5 * 60;
+constexpr const char*
+    kIntensiveWakeUpThrottling_CanIntensivelyThrottleLowNestingLevel_Name =
+        "can_intensively_throttle_low_nesting_level";
+constexpr const bool
+    kIntensiveWakeUpThrottling_CanIntensivelyThrottleLowNestingLevel_Default =
+        false;
 
 // Exposed so that multiple tests can tinker with the policy override.
 PLATFORM_EXPORT void
@@ -224,6 +230,9 @@
 // that updating the title or favicon has no effect on intensive throttling.
 PLATFORM_EXPORT base::TimeDelta
 GetTimeToInhibitIntensiveThrottlingOnTitleOrFaviconUpdate();
+// Whether timers with a non-zero delay and a low nesting level can be
+// intensively throttled.
+PLATFORM_EXPORT bool CanIntensivelyThrottleLowNestingLevel();
 
 // Per-agent scheduling experiments.
 constexpr base::Feature kPerAgentSchedulingExperiments{
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
index 21cc776b..36e46eb 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -376,9 +376,17 @@
       return ThrottleableTaskQueueTraits().SetPrioritisationType(
           QueueTraits::PrioritisationType::kBestEffort);
     case TaskType::kJavascriptTimerDelayedLowNesting:
+      return ThrottleableTaskQueueTraits()
+          .SetPrioritisationType(
+              QueueTraits::PrioritisationType::kJavaScriptTimer)
+          .SetCanBeIntensivelyThrottled(
+              IsIntensiveWakeUpThrottlingEnabled() &&
+              CanIntensivelyThrottleLowNestingLevel());
     case TaskType::kJavascriptTimerDelayedHighNesting:
-      return ThrottleableTaskQueueTraits().SetPrioritisationType(
-          QueueTraits::PrioritisationType::kJavaScriptTimer);
+      return ThrottleableTaskQueueTraits()
+          .SetPrioritisationType(
+              QueueTraits::PrioritisationType::kJavaScriptTimer)
+          .SetCanBeIntensivelyThrottled(IsIntensiveWakeUpThrottlingEnabled());
     case TaskType::kJavascriptTimerImmediate: {
       return DeferrableTaskQueueTraits()
           .SetPrioritisationType(
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
index 1730345b9..3ce85ea 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -187,13 +187,14 @@
     feature_list_.InitWithFeatures(features_to_enable, features_to_disable);
   }
 
-  // Constructs a FrameSchedulerImplTest with a list of features to enable and
-  // associated params.
-  explicit FrameSchedulerImplTest(
-      const std::vector<base::test::ScopedFeatureList::FeatureAndParams>&
-          features_to_enable)
+  // Constructs a FrameSchedulerImplTest with a feature to enable, associated
+  // params, and a list of features to disable.
+  FrameSchedulerImplTest(const base::Feature& feature_to_enable,
+                         const base::FieldTrialParams& feature_to_enable_params,
+                         const std::vector<base::Feature>& features_to_disable)
       : FrameSchedulerImplTest() {
-    feature_list_.InitWithFeaturesAndParameters(features_to_enable, {});
+    feature_list_.InitWithFeaturesAndParameters(
+        {{feature_to_enable, feature_to_enable_params}}, features_to_disable);
   }
 
   ~FrameSchedulerImplTest() override = default;
@@ -506,9 +507,19 @@
  public:
   using Super = FrameSchedulerImplTest;
 
-  FrameSchedulerImplTestWithIntensiveWakeUpThrottlingBase()
-      : FrameSchedulerImplTest({features::kIntensiveWakeUpThrottling},
-                               {features::kStopInBackground}) {}
+  explicit FrameSchedulerImplTestWithIntensiveWakeUpThrottlingBase(
+      bool can_intensively_throttle_low_nesting_level)
+      : FrameSchedulerImplTest(
+            features::kIntensiveWakeUpThrottling,
+            // If |can_intensively_throttle_low_nesting_level| is true, set the
+            // feature param to "true". Otherwise, test the default behavior (no
+            // feature params).
+            can_intensively_throttle_low_nesting_level
+                ? base::FieldTrialParams(
+                      {{kIntensiveWakeUpThrottling_CanIntensivelyThrottleLowNestingLevel_Name,
+                        "true"}})
+                : base::FieldTrialParams(),
+            {features::kStopInBackground}) {}
 
   void SetUp() override {
     Super::SetUp();
@@ -527,13 +538,44 @@
       GetIntensiveWakeUpThrottlingDurationBetweenWakeUps();
 };
 
+// Test param for FrameSchedulerImplTestWithIntensiveWakeUpThrottling
+struct IntensiveWakeUpThrottlingTestParam {
+  // TaskType used to obtain TaskRunners from the FrameScheduler.
+  TaskType task_type;
+  // Whether the feature param to allow throttling of timers with a low nesting
+  // level should be set to "true" at the beginning of the test.
+  bool can_intensively_throttle_low_nesting_level;
+  // Whether it is expected that tasks will be intensively throttled.
+  bool is_intensive_throttling_expected;
+};
+
 class FrameSchedulerImplTestWithIntensiveWakeUpThrottling
     : public FrameSchedulerImplTestWithIntensiveWakeUpThrottlingBase,
-      public ::testing::WithParamInterface<TaskType> {};
+      public ::testing::WithParamInterface<IntensiveWakeUpThrottlingTestParam> {
+ public:
+  FrameSchedulerImplTestWithIntensiveWakeUpThrottling()
+      : FrameSchedulerImplTestWithIntensiveWakeUpThrottlingBase(
+            GetParam().can_intensively_throttle_low_nesting_level) {}
+
+  TaskType GetTaskType() const { return GetParam().task_type; }
+  bool IsIntensiveThrottlingExpected() const {
+    return GetParam().is_intensive_throttling_expected;
+  }
+
+  base::TimeDelta GetExpectedWakeUpInterval() const {
+    if (IsIntensiveThrottlingExpected())
+      return kIntensiveThrottlingDurationBetweenWakeUps;
+    return kDefaultThrottledWakeUpInterval;
+  }
+};
 
 class FrameSchedulerImplTestWithIntensiveWakeUpThrottlingPolicyOverride
     : public FrameSchedulerImplTestWithIntensiveWakeUpThrottlingBase {
  public:
+  FrameSchedulerImplTestWithIntensiveWakeUpThrottlingPolicyOverride()
+      : FrameSchedulerImplTestWithIntensiveWakeUpThrottlingBase(
+            /* can_intensively_throttle_low_nesting_level=*/false) {}
+
   // This should only be called once per test, and prior to the
   // PageSchedulerImpl logic actually parsing the policy switch.
   void SetPolicyOverride(bool enabled) {
@@ -2886,7 +2928,7 @@
 
   // Throttled TaskRunner to which tasks are posted in this test.
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(GetParam());
+      frame_scheduler_->GetTaskRunner(GetTaskType());
 
   // Snap the time to a multiple of
   // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise, the time at which
@@ -2898,33 +2940,30 @@
   EXPECT_TRUE(page_scheduler_->IsPageVisible());
   page_scheduler_->SetPageVisible(false);
 
-  // Initially, wake ups are not throttled.
+  // Initially, wake ups are not intensively throttled.
   {
     const base::TimeTicks scope_start = base::TimeTicks::Now();
     EXPECT_EQ(scope_start, test_start);
     std::vector<base::TimeTicks> run_times;
 
     for (int i = 0; i < kNumTasks; ++i) {
-      task_runner->PostDelayedTask(FROM_HERE,
-                                   base::BindOnce(&RecordRunTime, &run_times),
-                                   i * kDefaultThrottledWakeUpInterval);
+      task_runner->PostDelayedTask(
+          FROM_HERE, base::BindOnce(&RecordRunTime, &run_times),
+          kShortDelay + i * kDefaultThrottledWakeUpInterval);
     }
 
     task_environment_.FastForwardBy(kGracePeriod);
     EXPECT_THAT(run_times, testing::ElementsAre(
-                               scope_start + base::TimeDelta::FromSeconds(0),
                                scope_start + base::TimeDelta::FromSeconds(1),
                                scope_start + base::TimeDelta::FromSeconds(2),
                                scope_start + base::TimeDelta::FromSeconds(3),
-                               scope_start + base::TimeDelta::FromSeconds(4)));
+                               scope_start + base::TimeDelta::FromSeconds(4),
+                               scope_start + base::TimeDelta::FromSeconds(5)));
   }
 
-  // After |kGracePeriod|, a wake up can occur
-  // |kIntensiveThrottlingDurationBetweenWakeUps| after the last wake up, or at
-  // a time aligned on |kIntensiveThrottlingDurationBetweenWakeUps|.
+  // After the grace period:
 
-  // Test waking up |kIntensiveThrottlingDurationBetweenWakeUps| after the last
-  // wake up.
+  // Test that wake ups are 1-second aligned if there is no recent wake up.
   {
     const base::TimeTicks scope_start = base::TimeTicks::Now();
     EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(5));
@@ -2939,8 +2978,9 @@
                                scope_start + base::TimeDelta::FromSeconds(1)));
   }
 
-  // Test waking up at a time aligned on
-  // ||kIntensiveThrottlingDurationBetweenWakeUps|.
+  // Test that if there is a recent wake up:
+  //   TaskType can be intensively throttled:   Wake ups are 1-minute aligned
+  //   Otherwise:                               Wake ups are 1-second aligned
   {
     const base::TimeTicks scope_start = base::TimeTicks::Now();
     EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(5) +
@@ -2948,23 +2988,33 @@
     std::vector<base::TimeTicks> run_times;
 
     for (int i = 0; i < kNumTasks; ++i) {
-      task_runner->PostDelayedTask(FROM_HERE,
-                                   base::BindOnce(&RecordRunTime, &run_times),
-                                   (i + 1) * kDefaultThrottledWakeUpInterval);
+      task_runner->PostDelayedTask(
+          FROM_HERE, base::BindOnce(&RecordRunTime, &run_times),
+          kShortDelay + i * kDefaultThrottledWakeUpInterval);
     }
 
-    // // All tasks should run at the next aligned time.
     FastForwardToAlignedTime(kIntensiveThrottlingDurationBetweenWakeUps);
-    EXPECT_THAT(run_times, testing::ElementsAre(
-                               scope_start + base::TimeDelta::FromSeconds(59),
-                               scope_start + base::TimeDelta::FromSeconds(59),
-                               scope_start + base::TimeDelta::FromSeconds(59),
-                               scope_start + base::TimeDelta::FromSeconds(59),
-                               scope_start + base::TimeDelta::FromSeconds(59)));
+
+    if (IsIntensiveThrottlingExpected()) {
+      const base::TimeTicks aligned_time =
+          scope_start + base::TimeDelta::FromSeconds(59);
+      EXPECT_THAT(run_times,
+                  testing::ElementsAre(aligned_time, aligned_time, aligned_time,
+                                       aligned_time, aligned_time));
+    } else {
+      EXPECT_THAT(
+          run_times,
+          testing::ElementsAre(scope_start + base::TimeDelta::FromSeconds(1),
+                               scope_start + base::TimeDelta::FromSeconds(2),
+                               scope_start + base::TimeDelta::FromSeconds(3),
+                               scope_start + base::TimeDelta::FromSeconds(4),
+                               scope_start + base::TimeDelta::FromSeconds(5)));
+    }
   }
 
-  // Post an extra task with a short delay. It should run at the next time
-  // aligned on |kIntensiveThrottlingDurationBetweenWakeUps|.
+  // Post an extra task with a short delay. The wake up should be 1-minute
+  // aligned if the TaskType supports intensive throttling, or 1-second aligned
+  // otherwise.
   {
     const base::TimeTicks scope_start = base::TimeTicks::Now();
     EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(6));
@@ -2975,14 +3025,15 @@
                                  kDefaultThrottledWakeUpInterval);
 
     task_environment_.FastForwardBy(kIntensiveThrottlingDurationBetweenWakeUps);
-    EXPECT_THAT(run_times, testing::ElementsAre(
-                               scope_start + base::TimeDelta::FromMinutes(1)));
+
+    EXPECT_THAT(run_times, testing::ElementsAre(scope_start +
+                                                GetExpectedWakeUpInterval()));
   }
 
-  // Post an extra task with a delay that is longer than
-  // |kIntensiveThrottlingDurationBetweenWakeUps|. The task should run at its
-  // desired run time, even if it's not aligned on
-  // |kIntensiveThrottlingDurationBetweenWakeUps|.
+  // Post an extra task with a delay longer than the intensive throttling wake
+  // up interval. The wake up should be 1-second aligned, even if the TaskType
+  // supports intensive throttling, because there was no wake up in the last
+  // minute.
   {
     const base::TimeTicks scope_start = base::TimeTicks::Now();
     EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(7));
@@ -2999,8 +3050,9 @@
   }
 
   // Post tasks with short delays after the page communicated with the user in
-  // background. They should run aligned on 1-second interval for 5 seconds.
-  // After that, intensive throttling is applied again.
+  // background. Tasks should be 1-second aligned for 3 seconds. After that, if
+  // the TaskType supports intensive throttling, wake ups should be 1-minute
+  // aligned.
   {
     const base::TimeTicks scope_start = base::TimeTicks::Now();
     EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(12) +
@@ -3021,16 +3073,28 @@
         kDefaultThrottledWakeUpInterval);
 
     task_environment_.FastForwardUntilNoTasksRemain();
-    EXPECT_THAT(run_times, testing::ElementsAre(
-                               scope_start + base::TimeDelta::FromSeconds(1),
+
+    if (IsIntensiveThrottlingExpected()) {
+      EXPECT_THAT(run_times, testing::ElementsAre(
+                                 scope_start + base::TimeDelta::FromSeconds(1),
+                                 scope_start + base::TimeDelta::FromSeconds(2),
+                                 scope_start + base::TimeDelta::FromSeconds(3),
+                                 scope_start - kDefaultThrottledWakeUpInterval +
+                                     base::TimeDelta::FromMinutes(1),
+                                 scope_start - kDefaultThrottledWakeUpInterval +
+                                     base::TimeDelta::FromMinutes(1),
+                                 scope_start - kDefaultThrottledWakeUpInterval +
+                                     base::TimeDelta::FromMinutes(1)));
+    } else {
+      EXPECT_THAT(
+          run_times,
+          testing::ElementsAre(scope_start + base::TimeDelta::FromSeconds(1),
                                scope_start + base::TimeDelta::FromSeconds(2),
                                scope_start + base::TimeDelta::FromSeconds(3),
-                               scope_start - kDefaultThrottledWakeUpInterval +
-                                   base::TimeDelta::FromMinutes(1),
-                               scope_start - kDefaultThrottledWakeUpInterval +
-                                   base::TimeDelta::FromMinutes(1),
-                               scope_start - kDefaultThrottledWakeUpInterval +
-                                   base::TimeDelta::FromMinutes(1)));
+                               scope_start + base::TimeDelta::FromSeconds(4),
+                               scope_start + base::TimeDelta::FromSeconds(5),
+                               scope_start + base::TimeDelta::FromSeconds(6)));
+    }
   }
 }
 
@@ -3042,7 +3106,7 @@
 
   // Throttled TaskRunner to which tasks are posted in this test.
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(GetParam());
+      frame_scheduler_->GetTaskRunner(GetTaskType());
 
   // Snap the time to a multiple of
   // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise, the time at which
@@ -3054,33 +3118,33 @@
   EXPECT_TRUE(page_scheduler_->IsPageVisible());
   page_scheduler_->SetPageVisible(false);
 
-  // Initially, wake ups are not throttled.
+  // Initially, wake ups are not intensively throttled.
   {
     const base::TimeTicks scope_start = base::TimeTicks::Now();
     EXPECT_EQ(scope_start, test_start);
     std::vector<base::TimeTicks> run_times;
 
     for (int i = 0; i < kNumTasks; ++i) {
-      task_runner->PostDelayedTask(FROM_HERE,
-                                   base::BindOnce(&RecordRunTime, &run_times),
-                                   i * kDefaultThrottledWakeUpInterval);
+      task_runner->PostDelayedTask(
+          FROM_HERE, base::BindOnce(&RecordRunTime, &run_times),
+          kShortDelay + i * kDefaultThrottledWakeUpInterval);
     }
 
     task_environment_.FastForwardBy(kGracePeriod);
     EXPECT_THAT(run_times, testing::ElementsAre(
-                               scope_start + base::TimeDelta::FromSeconds(0),
                                scope_start + base::TimeDelta::FromSeconds(1),
                                scope_start + base::TimeDelta::FromSeconds(2),
                                scope_start + base::TimeDelta::FromSeconds(3),
-                               scope_start + base::TimeDelta::FromSeconds(4)));
+                               scope_start + base::TimeDelta::FromSeconds(4),
+                               scope_start + base::TimeDelta::FromSeconds(5)));
   }
 
-  // After |kGracePeriod|, a wake up can occur aligned on
-  // |kIntensiveThrottlingDurationBetweenWakeUps| only.
+  // After the grace period:
 
-  // Test posting a first task. It should run at the next aligned time (in a
-  // main frame, it would have run kIntensiveThrottlingDurationBetweenWakeUps
-  // after the last wake up).
+  // Test posting a task when there is no recent wake up. The wake up should be
+  // 1-minute aligned if the TaskType supports intensive throttling (in a main
+  // frame, it would have been 1-second aligned since there was no wake up in
+  // the last minute). Otherwise, it should be 1-second aligned.
   {
     const base::TimeTicks scope_start = base::TimeTicks::Now();
     EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(5));
@@ -3091,34 +3155,46 @@
                                  kDefaultThrottledWakeUpInterval);
 
     task_environment_.FastForwardBy(kIntensiveThrottlingDurationBetweenWakeUps);
-    EXPECT_THAT(run_times, testing::ElementsAre(
-                               scope_start + base::TimeDelta::FromMinutes(1)));
+    EXPECT_THAT(run_times, testing::ElementsAre(scope_start +
+                                                GetExpectedWakeUpInterval()));
   }
 
-  // Test posting many tasks with short delays. They should all run on the next
-  // time aligned on |kIntensiveThrottlingDurationBetweenWakeUps|.
+  // Test posting many tasks with short delays. Wake ups should be 1-minute
+  // aligned if the TaskType supports intensive throttling, or 1-second aligned
+  // otherwise.
   {
     const base::TimeTicks scope_start = base::TimeTicks::Now();
     EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(6));
     std::vector<base::TimeTicks> run_times;
 
     for (int i = 0; i < kNumTasks; ++i) {
-      task_runner->PostDelayedTask(FROM_HERE,
-                                   base::BindOnce(&RecordRunTime, &run_times),
-                                   (i + 1) * kDefaultThrottledWakeUpInterval);
+      task_runner->PostDelayedTask(
+          FROM_HERE, base::BindOnce(&RecordRunTime, &run_times),
+          kShortDelay + i * kDefaultThrottledWakeUpInterval);
     }
 
     task_environment_.FastForwardBy(kIntensiveThrottlingDurationBetweenWakeUps);
-    EXPECT_THAT(run_times, testing::ElementsAre(
-                               scope_start + base::TimeDelta::FromMinutes(1),
-                               scope_start + base::TimeDelta::FromMinutes(1),
-                               scope_start + base::TimeDelta::FromMinutes(1),
-                               scope_start + base::TimeDelta::FromMinutes(1),
-                               scope_start + base::TimeDelta::FromMinutes(1)));
+
+    if (IsIntensiveThrottlingExpected()) {
+      const base::TimeTicks aligned_time =
+          scope_start + kIntensiveThrottlingDurationBetweenWakeUps;
+      EXPECT_THAT(run_times,
+                  testing::ElementsAre(aligned_time, aligned_time, aligned_time,
+                                       aligned_time, aligned_time));
+    } else {
+      EXPECT_THAT(
+          run_times,
+          testing::ElementsAre(scope_start + base::TimeDelta::FromSeconds(1),
+                               scope_start + base::TimeDelta::FromSeconds(2),
+                               scope_start + base::TimeDelta::FromSeconds(3),
+                               scope_start + base::TimeDelta::FromSeconds(4),
+                               scope_start + base::TimeDelta::FromSeconds(5)));
+    }
   }
 
-  // Post an extra task with a short delay. It should run at the next time
-  // aligned on |kIntensiveThrottlingDurationBetweenWakeUps|.
+  // Post an extra task with a short delay. Wake ups should be 1-minute aligned
+  // if the TaskType supports intensive throttling, or 1-second aligned
+  // otherwise.
   {
     const base::TimeTicks scope_start = base::TimeTicks::Now();
     EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(7));
@@ -3129,34 +3205,34 @@
                                  kDefaultThrottledWakeUpInterval);
 
     task_environment_.FastForwardBy(kIntensiveThrottlingDurationBetweenWakeUps);
-    EXPECT_THAT(run_times, testing::ElementsAre(
-                               scope_start + base::TimeDelta::FromMinutes(1)));
+    EXPECT_THAT(run_times, testing::ElementsAre(scope_start +
+                                                GetExpectedWakeUpInterval()));
   }
 
-  // Post an extra task with a delay that is longer than
-  // |kIntensiveThrottlingDurationBetweenWakeUps|. The task should run at an
-  // aligned time (in a main frame, it would have run at is desired unaligned
-  // run time).
+  // Post an extra task with a delay longer than the intensive throttling wake
+  // up interval. The wake up should be 1-minute aligned if the TaskType
+  // supports intensive throttling (in a main frame, it would have been 1-second
+  // aligned because there was no wake up in the last minute). Otherwise, it
+  // should be 1-second aligned.
   {
     const base::TimeTicks scope_start = base::TimeTicks::Now();
     EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(8));
     std::vector<base::TimeTicks> run_times;
 
     const base::TimeDelta kLongDelay =
-        kIntensiveThrottlingDurationBetweenWakeUps * 5 +
-        base::TimeDelta::FromSeconds(1);
-    task_runner->PostDelayedTask(
-        FROM_HERE, base::BindOnce(&RecordRunTime, &run_times), kLongDelay);
+        kIntensiveThrottlingDurationBetweenWakeUps * 6;
+    task_runner->PostDelayedTask(FROM_HERE,
+                                 base::BindOnce(&RecordRunTime, &run_times),
+                                 kLongDelay - kShortDelay);
 
-    task_environment_.FastForwardUntilNoTasksRemain();
-    EXPECT_THAT(run_times, testing::ElementsAre(
-                               scope_start +
-                               kIntensiveThrottlingDurationBetweenWakeUps * 6));
+    task_environment_.FastForwardBy(kLongDelay);
+    EXPECT_THAT(run_times, testing::ElementsAre(scope_start + kLongDelay));
   }
 
   // Post tasks with short delays after the page communicated with the user in
-  // background. They should run at an aligned time, since cross-origin
-  // frames are not affected by title or favicon update.
+  // background. Wake ups should be 1-minute aligned if the TaskType supports
+  // intensive throttling, since cross-origin frames are not affected by title
+  // or favicon update. Otherwise, they should be 1-second aligned.
   {
     const base::TimeTicks scope_start = base::TimeTicks::Now();
     EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(14));
@@ -3176,23 +3252,36 @@
         kDefaultThrottledWakeUpInterval);
 
     task_environment_.FastForwardUntilNoTasksRemain();
-    EXPECT_THAT(run_times, testing::ElementsAre(
-                               scope_start + base::TimeDelta::FromMinutes(1),
+
+    if (IsIntensiveThrottlingExpected()) {
+      EXPECT_THAT(
+          run_times,
+          testing::ElementsAre(scope_start + base::TimeDelta::FromMinutes(1),
                                scope_start + base::TimeDelta::FromMinutes(2),
                                scope_start + base::TimeDelta::FromMinutes(2),
                                scope_start + base::TimeDelta::FromMinutes(2),
                                scope_start + base::TimeDelta::FromMinutes(2),
                                scope_start + base::TimeDelta::FromMinutes(2)));
+    } else {
+      EXPECT_THAT(
+          run_times,
+          testing::ElementsAre(scope_start + base::TimeDelta::FromSeconds(1),
+                               scope_start + base::TimeDelta::FromSeconds(2),
+                               scope_start + base::TimeDelta::FromSeconds(3),
+                               scope_start + base::TimeDelta::FromSeconds(4),
+                               scope_start + base::TimeDelta::FromSeconds(5),
+                               scope_start + base::TimeDelta::FromSeconds(6)));
+    }
   }
 }
 
 // Verify that tasks from different frames that are same-origin with the main
 // frame run at the expected time.
 TEST_P(FrameSchedulerImplTestWithIntensiveWakeUpThrottling,
-       ManySameFrameOriginFrames) {
+       ManySameOriginFrames) {
   ASSERT_FALSE(frame_scheduler_->IsCrossOriginToMainFrame());
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(GetParam());
+      frame_scheduler_->GetTaskRunner(GetTaskType());
 
   // Create a FrameScheduler that is same-origin with the main frame, and an
   // associated throttled TaskRunner.
@@ -3202,7 +3291,7 @@
                            FrameScheduler::FrameType::kSubframe);
   ASSERT_FALSE(other_frame_scheduler->IsCrossOriginToMainFrame());
   const scoped_refptr<base::SingleThreadTaskRunner> other_task_runner =
-      other_frame_scheduler->GetTaskRunner(GetParam());
+      other_frame_scheduler->GetTaskRunner(GetTaskType());
 
   // Snap the time to a multiple of
   // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise, the time at which
@@ -3215,34 +3304,32 @@
   page_scheduler_->SetPageVisible(false);
   task_environment_.FastForwardBy(kGracePeriod);
 
-  // Post tasks in both frames, with delays shorter than the wake up interval.
-  int counter = 0;
-  task_runner->PostDelayedTask(
-      FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)),
-      kDefaultThrottledWakeUpInterval);
-  int other_counter = 0;
+  // Post tasks in both frames, with delays shorter than the intensive wake up
+  // interval.
+  const base::TimeTicks post_time = base::TimeTicks::Now();
+  std::vector<base::TimeTicks> run_times;
+  task_runner->PostDelayedTask(FROM_HERE,
+                               base::BindOnce(&RecordRunTime, &run_times),
+                               kDefaultThrottledWakeUpInterval + kShortDelay);
   other_task_runner->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&IncrementCounter, base::Unretained(&other_counter)),
-      2 * kDefaultThrottledWakeUpInterval);
+      FROM_HERE, base::BindOnce(&RecordRunTime, &run_times),
+      2 * kDefaultThrottledWakeUpInterval + kShortDelay);
+  task_environment_.FastForwardUntilNoTasksRemain();
 
-  // The first task should run at an unaligned time, because no wake up occurred
-  // in the last |kIntensiveThrottlingDurationBetweenWakeUps|.
-  EXPECT_EQ(0, counter);
-  task_environment_.FastForwardBy(kDefaultThrottledWakeUpInterval);
-  EXPECT_EQ(1, counter);
-
-  // The second task must run at an aligned time.
-  constexpr base::TimeDelta kEpsilon = base::TimeDelta::FromMicroseconds(1);
-  EXPECT_EQ(0, other_counter);
-  task_environment_.FastForwardBy(kDefaultThrottledWakeUpInterval);
-  EXPECT_EQ(0, other_counter);
-  task_environment_.FastForwardBy(kIntensiveThrottlingDurationBetweenWakeUps -
-                                  2 * kDefaultThrottledWakeUpInterval -
-                                  kEpsilon);
-  EXPECT_EQ(0, other_counter);
-  task_environment_.FastForwardBy(kEpsilon);
-  EXPECT_EQ(1, other_counter);
+  // The first task is 1-second aligned, because there was no wake up in the
+  // last minute. The second task is 1-minute aligned if the TaskType supports
+  // intensive throttling, or 1-second aligned otherwise.
+  if (IsIntensiveThrottlingExpected()) {
+    EXPECT_THAT(run_times,
+                testing::ElementsAre(
+                    post_time + 2 * kDefaultThrottledWakeUpInterval,
+                    post_time + kIntensiveThrottlingDurationBetweenWakeUps));
+  } else {
+    EXPECT_THAT(
+        run_times,
+        testing::ElementsAre(post_time + 2 * kDefaultThrottledWakeUpInterval,
+                             post_time + 3 * kDefaultThrottledWakeUpInterval));
+  }
 }
 
 // Verify that intensive throttling is disabled when there is an opt-out for all
@@ -3251,14 +3338,14 @@
   constexpr int kNumTasks = 3;
   // |task_runner| is throttled.
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(GetParam());
+      frame_scheduler_->GetTaskRunner(GetTaskType());
   // |other_task_runner| is throttled. It belongs to a different frame on the
   // same page.
   const auto other_frame_scheduler = CreateFrameScheduler(
       page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
       FrameScheduler::FrameType::kSubframe);
   const scoped_refptr<base::SingleThreadTaskRunner> other_task_runner =
-      frame_scheduler_->GetTaskRunner(GetParam());
+      frame_scheduler_->GetTaskRunner(GetTaskType());
 
   // Fast-forward the time to a multiple of
   // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise,
@@ -3272,7 +3359,8 @@
   task_environment_.FastForwardBy(kGracePeriod);
 
   {
-    // Wake ups are intensively throttled, since there is no throttling opt-out.
+    // Wake ups are intensively throttled for TaskTypes that support it, since
+    // there is no throttling opt-out.
     const base::TimeTicks scope_start = base::TimeTicks::Now();
     std::vector<base::TimeTicks> run_times;
     for (int i = 1; i < kNumTasks + 1; ++i) {
@@ -3286,16 +3374,28 @@
           kDefaultThrottledWakeUpInterval + i * kShortDelay);
     }
     task_environment_.FastForwardUntilNoTasksRemain();
-    // Note: Intensive throttling does not apply when there hasn't been a wake
-    // up in the last |kIntensiveThrottlingDurationBetweenWakeUps|.
-    EXPECT_THAT(run_times,
-                testing::ElementsAre(
-                    scope_start + kDefaultThrottledWakeUpInterval,
-                    scope_start + kDefaultThrottledWakeUpInterval,
-                    scope_start + kDefaultThrottledWakeUpInterval,
-                    scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
-                    scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
-                    scope_start + kIntensiveThrottlingDurationBetweenWakeUps));
+
+    if (IsIntensiveThrottlingExpected()) {
+      // Note: Wake ups can be unaligned when there is no recent wake up.
+      EXPECT_THAT(
+          run_times,
+          testing::ElementsAre(
+              scope_start + kDefaultThrottledWakeUpInterval,
+              scope_start + kDefaultThrottledWakeUpInterval,
+              scope_start + kDefaultThrottledWakeUpInterval,
+              scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+              scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+              scope_start + kIntensiveThrottlingDurationBetweenWakeUps));
+    } else {
+      EXPECT_THAT(run_times,
+                  testing::ElementsAre(
+                      scope_start + kDefaultThrottledWakeUpInterval,
+                      scope_start + kDefaultThrottledWakeUpInterval,
+                      scope_start + kDefaultThrottledWakeUpInterval,
+                      scope_start + 2 * kDefaultThrottledWakeUpInterval,
+                      scope_start + 2 * kDefaultThrottledWakeUpInterval,
+                      scope_start + 2 * kDefaultThrottledWakeUpInterval));
+    }
   }
 
   {
@@ -3355,16 +3455,27 @@
           kDefaultThrottledWakeUpInterval + i * kShortDelay);
     }
     task_environment_.FastForwardUntilNoTasksRemain();
-    // Note: Intensive throttling does not apply when there hasn't been a wake
-    // up in the last |kIntensiveThrottlingDurationBetweenWakeUps|.
-    EXPECT_THAT(run_times,
-                testing::ElementsAre(
-                    scope_start + kDefaultThrottledWakeUpInterval,
-                    scope_start + kDefaultThrottledWakeUpInterval,
-                    scope_start + kDefaultThrottledWakeUpInterval,
-                    scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
-                    scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
-                    scope_start + kIntensiveThrottlingDurationBetweenWakeUps));
+    if (IsIntensiveThrottlingExpected()) {
+      // Note: Wake ups can be unaligned when there is no recent wake up.
+      EXPECT_THAT(
+          run_times,
+          testing::ElementsAre(
+              scope_start + kDefaultThrottledWakeUpInterval,
+              scope_start + kDefaultThrottledWakeUpInterval,
+              scope_start + kDefaultThrottledWakeUpInterval,
+              scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+              scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+              scope_start + kIntensiveThrottlingDurationBetweenWakeUps));
+    } else {
+      EXPECT_THAT(run_times,
+                  testing::ElementsAre(
+                      scope_start + kDefaultThrottledWakeUpInterval,
+                      scope_start + kDefaultThrottledWakeUpInterval,
+                      scope_start + kDefaultThrottledWakeUpInterval,
+                      scope_start + 2 * kDefaultThrottledWakeUpInterval,
+                      scope_start + 2 * kDefaultThrottledWakeUpInterval,
+                      scope_start + 2 * kDefaultThrottledWakeUpInterval));
+    }
   }
 }
 
@@ -3375,14 +3486,14 @@
   constexpr int kNumTasks = 3;
   // |task_runner| is throttled.
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(GetParam());
+      frame_scheduler_->GetTaskRunner(GetTaskType());
   // |other_task_runner| is throttled. It belongs to a different frame on the
   // same page.
   const auto other_frame_scheduler = CreateFrameScheduler(
       page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
       FrameScheduler::FrameType::kSubframe);
   const scoped_refptr<base::SingleThreadTaskRunner> other_task_runner =
-      frame_scheduler_->GetTaskRunner(GetParam());
+      frame_scheduler_->GetTaskRunner(GetTaskType());
 
   // Fast-forward the time to a multiple of
   // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise,
@@ -3410,16 +3521,27 @@
           kDefaultThrottledWakeUpInterval + i * kShortDelay);
     }
     task_environment_.FastForwardUntilNoTasksRemain();
-    // Note: Intensive throttling does not apply when there hasn't been a wake
-    // up in the last |kIntensiveThrottlingDurationBetweenWakeUps|.
-    EXPECT_THAT(run_times,
-                testing::ElementsAre(
-                    scope_start + kDefaultThrottledWakeUpInterval,
-                    scope_start + kDefaultThrottledWakeUpInterval,
-                    scope_start + kDefaultThrottledWakeUpInterval,
-                    scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
-                    scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
-                    scope_start + kIntensiveThrottlingDurationBetweenWakeUps));
+    if (IsIntensiveThrottlingExpected()) {
+      // Note: Wake ups can be unaligned when there is no recent wake up.
+      EXPECT_THAT(
+          run_times,
+          testing::ElementsAre(
+              scope_start + kDefaultThrottledWakeUpInterval,
+              scope_start + kDefaultThrottledWakeUpInterval,
+              scope_start + kDefaultThrottledWakeUpInterval,
+              scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+              scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+              scope_start + kIntensiveThrottlingDurationBetweenWakeUps));
+    } else {
+      EXPECT_THAT(run_times,
+                  testing::ElementsAre(
+                      scope_start + kDefaultThrottledWakeUpInterval,
+                      scope_start + kDefaultThrottledWakeUpInterval,
+                      scope_start + kDefaultThrottledWakeUpInterval,
+                      scope_start + 2 * kDefaultThrottledWakeUpInterval,
+                      scope_start + 2 * kDefaultThrottledWakeUpInterval,
+                      scope_start + 2 * kDefaultThrottledWakeUpInterval));
+    }
   }
 
   {
@@ -3485,16 +3607,27 @@
           kDefaultThrottledWakeUpInterval + i * kShortDelay);
     }
     task_environment_.FastForwardUntilNoTasksRemain();
-    // Note: Intensive throttling does not apply when there hasn't been a wake
-    // up in the last |kIntensiveThrottlingDurationBetweenWakeUps|.
-    EXPECT_THAT(run_times,
-                testing::ElementsAre(
-                    scope_start + kDefaultThrottledWakeUpInterval,
-                    scope_start + kDefaultThrottledWakeUpInterval,
-                    scope_start + kDefaultThrottledWakeUpInterval,
-                    scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
-                    scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
-                    scope_start + kIntensiveThrottlingDurationBetweenWakeUps));
+    if (IsIntensiveThrottlingExpected()) {
+      // Note: Wake ups can be unaligned when there is no recent wake up.
+      EXPECT_THAT(
+          run_times,
+          testing::ElementsAre(
+              scope_start + kDefaultThrottledWakeUpInterval,
+              scope_start + kDefaultThrottledWakeUpInterval,
+              scope_start + kDefaultThrottledWakeUpInterval,
+              scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+              scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+              scope_start + kIntensiveThrottlingDurationBetweenWakeUps));
+    } else {
+      EXPECT_THAT(run_times,
+                  testing::ElementsAre(
+                      scope_start + kDefaultThrottledWakeUpInterval,
+                      scope_start + kDefaultThrottledWakeUpInterval,
+                      scope_start + kDefaultThrottledWakeUpInterval,
+                      scope_start + 2 * kDefaultThrottledWakeUpInterval,
+                      scope_start + 2 * kDefaultThrottledWakeUpInterval,
+                      scope_start + 2 * kDefaultThrottledWakeUpInterval));
+    }
   }
 }
 
@@ -3504,7 +3637,7 @@
        FrameChangesOriginType) {
   EXPECT_FALSE(frame_scheduler_->IsCrossOriginToMainFrame());
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(GetParam());
+      frame_scheduler_->GetTaskRunner(GetTaskType());
 
   // Create a new FrameScheduler that remains cross-origin with the main frame
   // throughout the test.
@@ -3514,7 +3647,7 @@
                            FrameScheduler::FrameType::kSubframe);
   cross_origin_frame_scheduler->SetCrossOriginToMainFrame(true);
   const scoped_refptr<base::SingleThreadTaskRunner> cross_origin_task_runner =
-      cross_origin_frame_scheduler->GetTaskRunner(GetParam());
+      cross_origin_frame_scheduler->GetTaskRunner(GetTaskType());
 
   // Snap the time to a multiple of
   // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise, the time at which
@@ -3529,9 +3662,8 @@
 
   {
     // Post delayed tasks with short delays to both frames. The
-    // main-frame-origin task can run at the desired time, because no wake up
-    // occurred in the last |kIntensiveThrottlingDurationBetweenWakeUps|. The
-    // cross-origin task must run at an aligned time.
+    // main-frame-origin task can run at the desired time, because there is no
+    // recent wake up. The cross-origin task must run at an aligned time.
     int counter = 0;
     task_runner->PostDelayedTask(
         FROM_HERE,
@@ -3547,9 +3679,15 @@
     // Make the |frame_scheduler_| cross-origin. Its task must now run at an
     // aligned time.
     frame_scheduler_->SetCrossOriginToMainFrame(true);
+
     task_environment_.FastForwardBy(kDefaultThrottledWakeUpInterval);
-    EXPECT_EQ(0, counter);
-    EXPECT_EQ(0, cross_origin_counter);
+    if (IsIntensiveThrottlingExpected()) {
+      EXPECT_EQ(0, counter);
+      EXPECT_EQ(0, cross_origin_counter);
+    } else {
+      EXPECT_EQ(1, counter);
+      EXPECT_EQ(1, cross_origin_counter);
+    }
 
     FastForwardToAlignedTime(kIntensiveThrottlingDurationBetweenWakeUps);
     EXPECT_EQ(1, counter);
@@ -3574,12 +3712,18 @@
                        base::Unretained(&cross_origin_counter)),
         kLongUnalignedDelay);
 
-    // Make the |frame_scheduler_| same-origin. Its task can now run at an
-    // unaligned time.
+    // Make the |frame_scheduler_| same-origin. Its task can now run at a
+    // 1-second aligned time, since there was no wake up in the last minute.
     frame_scheduler_->SetCrossOriginToMainFrame(false);
+
     task_environment_.FastForwardBy(kLongUnalignedDelay);
-    EXPECT_EQ(1, counter);
-    EXPECT_EQ(0, cross_origin_counter);
+    if (IsIntensiveThrottlingExpected()) {
+      EXPECT_EQ(1, counter);
+      EXPECT_EQ(0, cross_origin_counter);
+    } else {
+      EXPECT_EQ(1, counter);
+      EXPECT_EQ(1, cross_origin_counter);
+    }
 
     FastForwardToAlignedTime(kIntensiveThrottlingDurationBetweenWakeUps);
     EXPECT_EQ(1, counter);
@@ -3590,11 +3734,29 @@
 INSTANTIATE_TEST_SUITE_P(
     AllTimerTaskTypes,
     FrameSchedulerImplTestWithIntensiveWakeUpThrottling,
-    testing::Values(TaskType::kJavascriptTimerImmediate,
-                    TaskType::kJavascriptTimerDelayedLowNesting,
-                    TaskType::kJavascriptTimerDelayedHighNesting),
-    [](const testing::TestParamInfo<TaskType>& info) {
-      return TaskTypeNames::TaskTypeToString(info.param);
+    testing::Values(
+        IntensiveWakeUpThrottlingTestParam{
+            /* task_type=*/TaskType::kJavascriptTimerImmediate,
+            /* can_intensively_throttle_low_nesting_level=*/false,
+            /* is_intensive_throttling_expected=*/false},
+        IntensiveWakeUpThrottlingTestParam{
+            /* task_type=*/TaskType::kJavascriptTimerDelayedLowNesting,
+            /* can_intensively_throttle_low_nesting_level=*/false,
+            /* is_intensive_throttling_expected=*/false},
+        IntensiveWakeUpThrottlingTestParam{
+            /* task_type=*/TaskType::kJavascriptTimerDelayedLowNesting,
+            /* can_intensively_throttle_low_nesting_level=*/true,
+            /* is_intensive_throttling_expected=*/true},
+        IntensiveWakeUpThrottlingTestParam{
+            /* task_type=*/TaskType::kJavascriptTimerDelayedHighNesting,
+            /* can_intensively_throttle_low_nesting_level=*/false,
+            /* is_intensive_throttling_expected=*/true}),
+    [](const testing::TestParamInfo<IntensiveWakeUpThrottlingTestParam>& info) {
+      const std::string task_type =
+          TaskTypeNames::TaskTypeToString(info.param.task_type);
+      if (info.param.can_intensively_throttle_low_nesting_level)
+        return task_type + "_can_intensively_throttle_low_nesting_level";
+      return task_type;
     });
 
 TEST_F(FrameSchedulerImplTestWithIntensiveWakeUpThrottlingPolicyOverride,
@@ -3602,8 +3764,7 @@
   SetPolicyOverride(/* enabled = */ true);
   EXPECT_TRUE(IsIntensiveWakeUpThrottlingEnabled());
 
-  // The parameters should be the defaults, even though they were changed by the
-  // ScopedFeatureList.
+  // The parameters should be the defaults.
   EXPECT_EQ(base::TimeDelta::FromSeconds(
                 kIntensiveWakeUpThrottling_GracePeriodSeconds_Default),
             GetIntensiveWakeUpThrottlingGracePeriod());
@@ -3611,6 +3772,9 @@
       base::TimeDelta::FromSeconds(
           kIntensiveWakeUpThrottling_DurationBetweenWakeUpsSeconds_Default),
       GetIntensiveWakeUpThrottlingDurationBetweenWakeUps());
+  EXPECT_EQ(
+      kIntensiveWakeUpThrottling_CanIntensivelyThrottleLowNestingLevel_Default,
+      CanIntensivelyThrottleLowNestingLevel());
 }
 
 TEST_F(FrameSchedulerImplTestWithIntensiveWakeUpThrottlingPolicyOverride,
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
index 647616f..13dfa0fac 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
@@ -104,6 +104,7 @@
     QueueTraits()
         : can_be_deferred(false),
           can_be_throttled(false),
+          can_be_intensively_throttled(false),
           can_be_paused(false),
           can_be_frozen(false),
           can_run_in_background(true),
@@ -150,6 +151,11 @@
       return *this;
     }
 
+    QueueTraits SetCanBeIntensivelyThrottled(bool value) {
+      can_be_intensively_throttled = value;
+      return *this;
+    }
+
     QueueTraits SetCanBePaused(bool value) {
       can_be_paused = value;
       return *this;
@@ -183,6 +189,8 @@
     bool operator==(const QueueTraits& other) const {
       return can_be_deferred == other.can_be_deferred &&
              can_be_throttled == other.can_be_throttled &&
+             can_be_intensively_throttled ==
+                 other.can_be_intensively_throttled &&
              can_be_paused == other.can_be_paused &&
              can_be_frozen == other.can_be_frozen &&
              can_run_in_background == other.can_run_in_background &&
@@ -201,6 +209,7 @@
       int key = 1 << (offset++);
       key |= can_be_deferred << (offset++);
       key |= can_be_throttled << (offset++);
+      key |= can_be_intensively_throttled << (offset++);
       key |= can_be_paused << (offset++);
       key |= can_be_frozen << (offset++);
       key |= can_run_in_background << (offset++);
@@ -213,6 +222,7 @@
 
     bool can_be_deferred : 1;
     bool can_be_throttled : 1;
+    bool can_be_intensively_throttled : 1;
     bool can_be_paused : 1;
     bool can_be_frozen : 1;
     bool can_run_in_background : 1;
@@ -341,6 +351,10 @@
 
   bool CanBeThrottled() const { return queue_traits_.can_be_throttled; }
 
+  bool CanBeIntensivelyThrottled() const {
+    return queue_traits_.can_be_intensively_throttled;
+  }
+
   bool CanBePaused() const { return queue_traits_.can_be_paused; }
 
   // Used for WebView's pauseTimers API. This API expects layout, parsing, and
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
index 0878abf..ac1fc74 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -189,9 +189,6 @@
       keep_active_(
           agent_group_scheduler.GetMainThreadScheduler().SchedulerKeepActive()),
       had_recent_title_or_favicon_update_(false),
-      cpu_time_budget_pool_(nullptr),
-      same_origin_wake_up_budget_pool_(nullptr),
-      cross_origin_wake_up_budget_pool_(nullptr),
       delegate_(delegate),
       delay_for_background_tab_freezing_(GetDelayForBackgroundTabFreezing()),
       freeze_on_network_idle_enabled_(base::FeatureList::IsEnabled(
@@ -226,10 +223,12 @@
 
   if (cpu_time_budget_pool_)
     cpu_time_budget_pool_->Close();
-  if (same_origin_wake_up_budget_pool_)
-    same_origin_wake_up_budget_pool_->Close();
-  if (cross_origin_wake_up_budget_pool_)
-    cross_origin_wake_up_budget_pool_->Close();
+  if (HasWakeUpBudgetPools()) {
+    for (WakeUpBudgetPool* pool : AllWakeUpBudgetPools()) {
+      DCHECK(pool);
+      pool->Close();
+    }
+  }
 }
 
 // static
@@ -633,26 +632,30 @@
     MainThreadTaskQueue* task_queue,
     FrameOriginType frame_origin_type,
     base::sequence_manager::LazyNow* lazy_now) {
-  GetWakeUpBudgetPool(frame_origin_type)->AddQueue(lazy_now->Now(), task_queue);
+  GetWakeUpBudgetPool(task_queue, frame_origin_type)
+      ->AddQueue(lazy_now->Now(), task_queue);
 }
 
 void PageSchedulerImpl::RemoveQueueFromWakeUpBudgetPool(
     MainThreadTaskQueue* task_queue,
     FrameOriginType frame_origin_type,
     base::sequence_manager::LazyNow* lazy_now) {
-  GetWakeUpBudgetPool(frame_origin_type)
+  GetWakeUpBudgetPool(task_queue, frame_origin_type)
       ->RemoveQueue(lazy_now->Now(), task_queue);
 }
 
 WakeUpBudgetPool* PageSchedulerImpl::GetWakeUpBudgetPool(
+    MainThreadTaskQueue* task_queue,
     FrameOriginType frame_origin_type) {
+  if (!task_queue->CanBeIntensivelyThrottled())
+    return normal_wake_up_budget_pool_;
+
   switch (frame_origin_type) {
     case FrameOriginType::kMainFrame:
     case FrameOriginType::kSameOriginToMainFrame:
-      return same_origin_wake_up_budget_pool_;
-      break;
+      return same_origin_intensive_wake_up_budget_pool_;
     case FrameOriginType::kCrossOriginToMainFrame:
-      return cross_origin_wake_up_budget_pool_;
+      return cross_origin_intensive_wake_up_budget_pool_;
     case FrameOriginType::kCount:
       NOTREACHED();
       return nullptr;
@@ -695,28 +698,29 @@
 
 void PageSchedulerImpl::MaybeInitializeWakeUpBudgetPools(
     base::sequence_manager::LazyNow* lazy_now) {
-  DCHECK_EQ(!!same_origin_wake_up_budget_pool_,
-            !!cross_origin_wake_up_budget_pool_);
-  if (same_origin_wake_up_budget_pool_)
+  if (HasWakeUpBudgetPools())
     return;
 
-  same_origin_wake_up_budget_pool_ =
+  normal_wake_up_budget_pool_ =
       main_thread_scheduler_->task_queue_throttler()->CreateWakeUpBudgetPool(
-          "Page Wake Up Throttling - Same-Origin as Main Frame");
-  cross_origin_wake_up_budget_pool_ =
+          "Page - Normal Wake Up Throttling");
+  same_origin_intensive_wake_up_budget_pool_ =
       main_thread_scheduler_->task_queue_throttler()->CreateWakeUpBudgetPool(
-          "Page Wake Up Throttling - Cross-Origin to Main Frame");
+          "Page - Intensive Wake Up Throttling - Same-Origin as Main Frame");
+  cross_origin_intensive_wake_up_budget_pool_ =
+      main_thread_scheduler_->task_queue_throttler()->CreateWakeUpBudgetPool(
+          "Page - Intensive Wake Up Throttling - Cross-Origin to Main Frame");
 
   // The Wake Up Duration and Unaligned Wake Ups Allowance are constant and set
   // here. The Wake Up Interval is set in UpdateWakeUpBudgetPools(), based on
   // current state.
-  same_origin_wake_up_budget_pool_->SetWakeUpDuration(kThrottledWakeUpDuration);
-  if (IsIntensiveWakeUpThrottlingEnabled()) {
-    same_origin_wake_up_budget_pool_->AllowUnalignedWakeUpIfNoRecentWakeUp();
-  }
+  for (WakeUpBudgetPool* pool : AllWakeUpBudgetPools())
+    pool->SetWakeUpDuration(kThrottledWakeUpDuration);
 
-  cross_origin_wake_up_budget_pool_->SetWakeUpDuration(
-      kThrottledWakeUpDuration);
+  if (IsIntensiveWakeUpThrottlingEnabled()) {
+    same_origin_intensive_wake_up_budget_pool_
+        ->AllowUnalignedWakeUpIfNoRecentWakeUp();
+  }
 
   UpdateWakeUpBudgetPools(lazy_now);
 }
@@ -805,7 +809,7 @@
 }
 
 void PageSchedulerImpl::OnTitleOrFaviconUpdated() {
-  if (!same_origin_wake_up_budget_pool_)
+  if (!HasWakeUpBudgetPools())
     return;
 
   if (are_wake_ups_intensively_throttled_ &&
@@ -860,15 +864,12 @@
 
 void PageSchedulerImpl::UpdateWakeUpBudgetPools(
     base::sequence_manager::LazyNow* lazy_now) {
-  DCHECK_EQ(!!same_origin_wake_up_budget_pool_,
-            !!cross_origin_wake_up_budget_pool_);
-
-  if (!same_origin_wake_up_budget_pool_)
+  if (!same_origin_intensive_wake_up_budget_pool_)
     return;
 
-  same_origin_wake_up_budget_pool_->SetWakeUpInterval(
+  same_origin_intensive_wake_up_budget_pool_->SetWakeUpInterval(
       lazy_now->Now(), GetIntensiveWakeUpThrottlingDuration(true));
-  cross_origin_wake_up_budget_pool_->SetWakeUpInterval(
+  cross_origin_intensive_wake_up_budget_pool_->SetWakeUpInterval(
       lazy_now->Now(), GetIntensiveWakeUpThrottlingDuration(false));
 }
 
@@ -1055,6 +1056,23 @@
   return WebScopedVirtualTimePauser(main_thread_scheduler_, duration, name);
 }
 
+bool PageSchedulerImpl::HasWakeUpBudgetPools() const {
+  // All WakeUpBudgetPools should be initialized together.
+  DCHECK_EQ(!!normal_wake_up_budget_pool_,
+            !!same_origin_intensive_wake_up_budget_pool_);
+  DCHECK_EQ(!!normal_wake_up_budget_pool_,
+            !!cross_origin_intensive_wake_up_budget_pool_);
+
+  return !!normal_wake_up_budget_pool_;
+}
+
+std::array<WakeUpBudgetPool*, PageSchedulerImpl::kNumWakeUpBudgetPools>
+PageSchedulerImpl::AllWakeUpBudgetPools() {
+  return {normal_wake_up_budget_pool_,
+          same_origin_intensive_wake_up_budget_pool_,
+          cross_origin_intensive_wake_up_budget_pool_};
+}
+
 // static
 const char PageSchedulerImpl::kHistogramPageLifecycleStateTransition[] =
     "PageScheduler.PageLifecycleStateTransition";
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
index 97385cc..f8b9446 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
@@ -236,8 +236,10 @@
       MainThreadTaskQueue* task_queue,
       FrameOriginType frame_origin_type,
       base::sequence_manager::LazyNow* lazy_now);
-  // Returns the WakeUpBudgetPool to use for a frame with |frame_origin_type|.
-  WakeUpBudgetPool* GetWakeUpBudgetPool(FrameOriginType frame_origin_type);
+  // Returns the WakeUpBudgetPool to use for |task_queue| which belongs to a
+  // frame with |frame_origin_type|.
+  WakeUpBudgetPool* GetWakeUpBudgetPool(MainThreadTaskQueue* task_queue,
+                                        FrameOriginType frame_origin_type);
   // Initializes WakeUpBudgetPools, if not already initialized.
   void MaybeInitializeWakeUpBudgetPools(
       base::sequence_manager::LazyNow* lazy_now);
@@ -285,6 +287,13 @@
   // be freezable.
   void DoFreezePage();
 
+  // Returns true if WakeUpBudgetPools were initialized.
+  bool HasWakeUpBudgetPools() const;
+
+  // Returns all WakeUpBudgetPools owned by this PageSchedulerImpl.
+  static constexpr int kNumWakeUpBudgetPools = 3;
+  std::array<WakeUpBudgetPool*, kNumWakeUpBudgetPools> AllWakeUpBudgetPools();
+
   TraceableVariableController tracing_controller_;
   HashSet<FrameSchedulerImpl*> frame_schedulers_;
   MainThreadSchedulerImpl* main_thread_scheduler_;
@@ -303,21 +312,31 @@
   bool are_wake_ups_intensively_throttled_;
   bool keep_active_;
   bool had_recent_title_or_favicon_update_;
-  CPUTimeBudgetPool* cpu_time_budget_pool_;
-  // Throttles wake ups in throttleable TaskQueues of frames that have the same
-  // origin as the main frame.
+  CPUTimeBudgetPool* cpu_time_budget_pool_ = nullptr;
+
+  // Wake up budget pools for each throttling scenario:
   //
-  // This pool allows aligned wake ups and unaligned wake ups if there hasn't
-  // been a recent wake up.
-  WakeUpBudgetPool* same_origin_wake_up_budget_pool_;
-  // Throttles wake ups in throttleable TaskQueues of frames that are
-  // cross-origin with the main frame.
+  //                                  Same-origin frame    Cross-origin frame
+  // Normal throttling only           1                    1
+  // Normal and intensive throttling  2                    3
   //
-  // This pool only allows aligned wake ups. Because wake ups do not depend on
-  // recent wake ups like in |same_origin_wake_up_budget_pool_|, tasks cannot
-  // easily learn about tasks running in other queues in the same pool. This is
-  // important because this pool can have queues from different origins.
-  WakeUpBudgetPool* cross_origin_wake_up_budget_pool_;
+  // 1: This pool allows 1-second aligned wake ups.
+  WakeUpBudgetPool* normal_wake_up_budget_pool_ = nullptr;
+  // 2: This pool allows 1-second aligned wake ups if the page is not
+  //    intensively throttled of if there hasn't been a wake up in the last
+  //    minute. Otherwise, it allows 1-minute aligned wake ups.
+  WakeUpBudgetPool* same_origin_intensive_wake_up_budget_pool_ = nullptr;
+  // 3: This pool allows 1-second aligned wake ups if the page is not
+  //    intensively throttled. Otherwise, it allows 1-minute aligned wake ups.
+  //
+  //    Unlike |same_origin_intensive_wake_up_budget_pool_|, this pool does not
+  //    allow a 1-second aligned wake up when there hasn't been a wake up in the
+  //    last minute. This is to prevent frames from different origins from
+  //    learning about each other. Concretely, this means that
+  //    MaybeInitializeWakeUpBudgetPools() does not invoke
+  //    AllowUnalignedWakeUpIfNoRecentWakeUp() on this pool.
+  WakeUpBudgetPool* cross_origin_intensive_wake_up_budget_pool_ = nullptr;
+
   PageScheduler::Delegate* delegate_;
   CancelableClosureHolder do_throttle_cpu_time_callback_;
   CancelableClosureHolder do_intensively_throttle_wake_ups_callback_;
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index d8c71d2..36cbc0c 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -430,19 +430,6 @@
 # Flaky test.
 crbug.com/1054894 [ Mac ] http/tests/images/image-decode-in-frame.html [ Pass Failure ]
 
-# Disabled for Skia roll (https://crbug.com/skia/10744).
-paint/invalidation/svg/feImage-multiple-targets-id-change.svg [ Skip ]
-paint/invalidation/svg/feImage-reference-invalidation.svg [ Skip ]
-paint/invalidation/svg/feImage-target-add-to-document.svg [ Skip ]
-paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2.svg [ Skip ]
-paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection.svg [ Skip ]
-paint/invalidation/svg/feImage-target-attribute-change.svg [ Skip ]
-paint/invalidation/svg/feImage-target-inline-style-change.svg [ Skip ]
-paint/invalidation/svg/feImage-target-property-change.svg [ Skip ]
-paint/invalidation/svg/feImage-target-reappend-to-document.svg [ Skip ]
-paint/invalidation/svg/feImage-target-remove-from-document.svg [ Skip ]
-paint/invalidation/svg/feImage-target-style-change.svg [ Skip ]
-
 # ====== Paint team owned tests to here ======
 
 crbug.com/922249 virtual/android/fullscreen/compositor-touch-hit-rects-fullscreen-video-controls.html [ Failure Pass ]
@@ -766,9 +753,7 @@
 # These testcases are incorrect, mark them as failing until they're fixed in the testsuite.
 # https://lists.w3.org/Archives/Public/www-style/2016Jan/0275.html
 # https://lists.w3.org/Archives/Public/www-style/2016Jan/0276.html
-crbug.com/249112 external/wpt/css/css-flexbox/flex-minimum-height-flex-items-005.xht [ Failure ]
 crbug.com/249112 external/wpt/css/css-flexbox/flex-minimum-height-flex-items-007.xht [ Failure ]
-crbug.com/249112 external/wpt/css/css-flexbox/flex-minimum-width-flex-items-005.xht [ Failure ]
 crbug.com/249112 external/wpt/css/css-flexbox/flex-minimum-width-flex-items-007.xht [ Failure ]
 
 # These require css-align-3 positional keywords that Blink doesn't yet support for flexbox.
@@ -1536,8 +1521,6 @@
 crbug.com/936891 fast/scrolling/document-level-touchmove-event-listener-passive-by-default.html [ Failure ]
 crbug.com/936891 virtual/threaded-prefer-compositing/fast/scrolling/document-level-touchmove-event-listener-passive-by-default.html [ Pass ]
 
-crbug.com/597221 fast/dom/Window/window-postmessage-clone-deep-array.html [ Failure ]
-
 crbug.com/498539 http/tests/devtools/tracing/timeline-misc/timeline-bound-function.js [ Pass Failure ]
 
 crbug.com/498539 crbug.com/794869 crbug.com/798548 crbug.com/946716 http/tests/devtools/elements/styles-4/styles-update-from-js.js [ Crash Pass Failure Timeout ]
@@ -2695,6 +2678,9 @@
 crbug.com/958381 [ Mac ] external/wpt/css/CSS2/tables/table-anonymous-objects-206.xht [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html?Backspace [ Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html?TypingA [ Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html?Delete [ Timeout ]
 crbug.com/626703 external/wpt/screen-capture/feature-policy.https.html [ Timeout ]
 crbug.com/626703 external/wpt/infrastructure/testdriver/actions/wheelScroll.html [ Timeout ]
 crbug.com/626703 [ Fuchsia ] virtual/threaded/external/wpt/animation-worklet/worklet-animation-pause-immediately.https.html [ Failure ]
@@ -4463,9 +4449,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/swap-lines-if-start-is-further-endward-than-end-line.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/vertical-align-do-not-effect-grid-items.html [ Failure Crash ]
 
-# [css-logical]
-crbug.com/865579 external/wpt/css/css-logical/animation-001.html [ Failure Pass ]
-
 # [css-animations]
 crbug.com/993365 external/wpt/css/css-transitions/Element-getAnimations.tentative.html [ Failure Pass ]
 
@@ -6171,7 +6154,7 @@
 crbug.com/1044350 [ Release ] http/tests/devtools/network/network-xhr-replay.js [ Pass Timeout ]
 crbug.com/1044414 [ Release ] http/tests/devtools/elements/styles/selector-line-sourcemap-header.js [ Pass Timeout ]
 crbug.com/1044415 [ Release ] http/tests/devtools/runtime/evaluate-without-side-effects.js [ Pass Timeout ]
-crbug.com/1044424 [ Release ] http/tests/devtools/elements/css-rule-hover-highlights-selectors.js [ Pass Timeout ]
+# crbug.com/1044424 [ Release ] http/tests/devtools/elements/css-rule-hover-highlights-selectors.js [ Pass Timeout ]
 crbug.com/1044425 [ Release ] http/tests/devtools/elements/shadow/elements-panel-shadow-selection-on-refresh-1.js [ Pass Timeout ]
 crbug.com/1044429 [ Release ] http/tests/devtools/oopif/oopif-navigator.js [ Pass Timeout ]
 crbug.com/1002914 http/tests/devtools/elements/elements-panel-styles.js [ Pass Timeout Failure ]
@@ -6342,6 +6325,7 @@
 
 # DevTools roll
 crbug.com/1052111 http/tests/devtools/a11y-axe-core/audits-start-view-a11y-test.js [ Skip ]
+crbug.com/1131801 http/tests/devtools/elements/css-rule-hover-highlights-selectors.js [ Pass Failure Timeout ]
 
 # Sheriff 2020-02-18
 crbug.com/1050893 [ Fuchsia ] virtual/gpu/fast/canvas/feimage-with-foreignobject-taint-canvas-2.html [ Skip ]
@@ -6440,8 +6424,11 @@
 # The "timeout" was detected by the wpt-importer bot.
 crbug.com/1069714 [ Mac10.13 ] external/wpt/webrtc/RTCRtpSender-replaceTrack.https.html [ Pass Failure Timeout ]
 
-# COOP access reporting. This needs to be implemented in chrome to pass.
-crbug.com/1090273 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property.https.html [ Pass Timeout Failure ]
+# COOP access reporting:
+crbug.com/1090273 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-frames.https.html [ Timeout ]
+crbug.com/1090273 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-set.https.html [ Timeout ]
+crbug.com/1090273 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-self.https.html [ Timeout ]
+crbug.com/1090273 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-window.https.html [ Timeout ]
 
 # Sheriff 2020-04-17
 crbug.com/1071909 external/wpt/html/cross-origin-opener-policy/popup-coop-by-sw.https.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/android/ChromiumWPTExpectations b/third_party/blink/web_tests/android/ChromiumWPTExpectations
index f018d4aa..4fd3a5c 100644
--- a/third_party/blink/web_tests/android/ChromiumWPTExpectations
+++ b/third_party/blink/web_tests/android/ChromiumWPTExpectations
@@ -7,7 +7,6 @@
 # bug for the expectation to the new bug.
 
 # https://crbug.com/1126079
-crbug.com/1126079 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property.https.html [ Failure ]
 crbug.com/1126079 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/reporting-observer.html [ Failure ]
 crbug.com/1126079 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-openee_coop-ro.https.html [ Pass CRASH ]
 
diff --git a/third_party/blink/web_tests/android/WeblayerWPTExpectations b/third_party/blink/web_tests/android/WeblayerWPTExpectations
index 2d2ef77..1b7324e 100644
--- a/third_party/blink/web_tests/android/WeblayerWPTExpectations
+++ b/third_party/blink/web_tests/android/WeblayerWPTExpectations
@@ -7,7 +7,6 @@
 # bug for the expectation to the new bug.
 
 # https://crbug.com/1126079
-crbug.com/1126079 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property.https.html [ Failure ]
 crbug.com/1126079 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/reporting-observer.html [ Failure ]
 crbug.com/1126079 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-openee_coop-ro.https.html [ Pass CRASH ]
 
diff --git a/third_party/blink/web_tests/android/WebviewWPTExpectations b/third_party/blink/web_tests/android/WebviewWPTExpectations
index 2432292c..bd30dea 100644
--- a/third_party/blink/web_tests/android/WebviewWPTExpectations
+++ b/third_party/blink/web_tests/android/WebviewWPTExpectations
@@ -7,7 +7,6 @@
 # bug for the expectation to the new bug.
 
 # https://crbug.com/1126079
-crbug.com/1126079 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property.https.html [ Failure ]
 crbug.com/1126079 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/reporting-observer.html [ Failure ]
 crbug.com/1126079 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-openee_coop-ro.https.html [ Pass CRASH ]
 
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 45223971..ebbc818 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -226361,6 +226361,10 @@
     "input-events-exec-command-expected.txt": [
      "1e219ecd5bead581a7eca478bcefcdd72dc8c9be",
      []
+    ],
+    "input-events-get-target-ranges.js": [
+     "4d09c4da521fb57817ab70af9f67ef181dcb56cf",
+     []
     ]
    },
    "installedapp": {
@@ -271309,10 +271313,12 @@
       ]
      ],
      "required_csp-header-crlf.html": [
-      "87bda1bf502d25f20f8245bd4c89fa1bdedf8803",
+      "ae121899023a4d33e27c262d9b208abb97583a1b",
       [
        null,
-       {}
+       {
+        "timeout": "long"
+       }
       ]
      ],
      "required_csp-header.html": [
@@ -364746,7 +364752,7 @@
      ]
     ],
     "input-events-get-target-ranges-backspace.tentative.html": [
-     "50d5cb96e84a8400fd961d7221b551b536dbcc57",
+     "a67bc76364d9675f2048161544b26eee02bf1185",
      [
       null,
       {
@@ -364756,7 +364762,7 @@
      ]
     ],
     "input-events-get-target-ranges-forwarddelete.tentative.html": [
-     "03518d54e42e5aea5ed51635a4fb088d2428ef86",
+     "62a33c094000680bd5981f0e2e1cddb1a3c6c1f5",
      [
       null,
       {
@@ -364765,6 +364771,30 @@
       }
      ]
     ],
+    "input-events-get-target-ranges-non-collapsed-selection.tentative.html": [
+     "2e16d2a4c3acf5c2794ed312bafc75d003d52588",
+     [
+      "input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html?Backspace",
+      {
+       "testdriver": true,
+       "timeout": "long"
+      }
+     ],
+     [
+      "input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html?Delete",
+      {
+       "testdriver": true,
+       "timeout": "long"
+      }
+     ],
+     [
+      "input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html?TypingA",
+      {
+       "testdriver": true,
+       "timeout": "long"
+      }
+     ]
+    ],
     "input-events-typing.html": [
      "a894beea9bd381313b999e45abe0b4b8f61c804b",
      [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-logical/animation-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-logical/animation-001-expected.txt
new file mode 100644
index 0000000..26731f8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-logical/animation-001-expected.txt
@@ -0,0 +1,28 @@
+This is a testharness.js-based test.
+PASS Logical properties can be animated using object notation
+PASS Logical properties can be animated using array notation
+PASS Logical properties are NOT stored as physical properties
+PASS Logical properties in animations respect the writing-mode
+PASS Logical properties in animations respect the direction
+PASS Physical properties win over logical properties in object notation
+PASS Physical properties win over logical properties in array notation
+PASS Physical properties with variables win over logical properties
+FAIL Logical shorthands follow the usual prioritization based on number of component longhands assert_equals: expected "300px" but got "0px"
+PASS Physical longhands win over logical shorthands
+PASS Logical longhands win over physical shorthands
+PASS Physical shorthands win over logical shorthands
+PASS Physical shorthands using variables win over logical shorthands
+PASS Physical properties and logical properties can be mixed
+PASS Physical shorthands and logical shorthands can be mixed
+PASS Physical properties win over logical properties even when some keyframes only have logical properties
+PASS Animations update when the writing-mode is changed
+PASS Filling animations update when the writing-mode is changed
+PASS Animations with implicit from values update when the writing-mode is changed
+PASS Animations with overlapping physical and logical properties update when the writing-mode is changed
+PASS Animations update when the writing-mode is changed through a CSS variable
+PASS Animations update when the direction is changed
+PASS Logical shorthand with variable references animates correctly
+PASS writing-mode is not animatable
+PASS direction is not animatable
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/removing-collapsible-crash.html b/third_party/blink/web_tests/external/wpt/css/css-text/removing-collapsible-crash.html
new file mode 100644
index 0000000..a87fe00
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/removing-collapsible-crash.html
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/removing-collapsible-spaces-before-float-crash.html b/third_party/blink/web_tests/external/wpt/css/css-text/removing-collapsible-spaces-before-float-crash.html
index 92bba35..d1e5662 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text/removing-collapsible-spaces-before-float-crash.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/removing-collapsible-spaces-before-float-crash.html
@@ -1,6 +1,6 @@
+<!DOCTYPE html>
 <title>CSS Text Test: Removing collapsible space before a float element cause Chrome to crash</title>
 <link rel="help" href="https://crbug.com/1131470">
-<!DOCTYPE html>
 <style type="text/css">
     .CLASS9 {
         zoom:1.3%;
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-blur.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-blur.https.html
new file mode 100644
index 0000000..e10c8da
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-blur.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.blur() access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("blur", w => w.blur());
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-close.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-close.https.html
new file mode 100644
index 0000000..a60841b0c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-close.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.close() access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("close", w => w.close());
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-closed.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-closed.https.html
new file mode 100644
index 0000000..98a5fe6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-closed.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.closed access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("closed", w => w.closed);
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-focus.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-focus.https.html
new file mode 100644
index 0000000..49220dfd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-focus.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.focus() access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("focus", w => w.focus());
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-frames.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-frames.https.html
new file mode 100644
index 0000000..3109f6f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-frames.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.frames access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("frames", w => w.frames);
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-length.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-length.https.html
new file mode 100644
index 0000000..ba3ccf2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-length.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.length access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("length", w => w.length);
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-location-get.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-location-get.https.html
new file mode 100644
index 0000000..1aec3147
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-location-get.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.location access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("location", w => w.location);
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-location-set.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-location-set.https.html
new file mode 100644
index 0000000..6967833
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-location-set.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.location access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("location", w => w.location = "#");
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-get.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-get.https.html
new file mode 100644
index 0000000..0f684e1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-get.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.opener access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("opener", w => w.opener);
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-set.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-set.https.html
new file mode 100644
index 0000000..6226c643
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-set.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.opener access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("opener", w => w.opener = "");
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-postmessage-1.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-postmessage-1.https.html
new file mode 100644
index 0000000..64022bb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-postmessage-1.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.postMessage(arg1, arg2) access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("postMessage", w => w.postMessage("", ""));
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-postmessage-2.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-postmessage-2.https.html
new file mode 100644
index 0000000..30bd87d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-postmessage-2.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.postMessage(arg1) access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("postMessage", w => w.postMessage(""));
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-self.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-self.https.html
new file mode 100644
index 0000000..9e528d42
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-self.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.self access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("self", w => w.self);
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-top.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-top.https.html
new file mode 100644
index 0000000..4a4643e7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-top.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.top access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("top", w => w.top);
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-window.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-window.https.html
new file mode 100644
index 0000000..04bc277
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-window.https.html
@@ -0,0 +1,12 @@
+<title> Check openee.window access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script src="../resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("window", w => w.window);
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property.https-expected.txt b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property.https-expected.txt
deleted file mode 100644
index 9276b03e..0000000
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property.https-expected.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-This is a testharness.js-based test.
-PASS cross-origin > w => w.blur()
-PASS cross-origin > w => w.close()
-PASS cross-origin > w => w.closed
-PASS cross-origin > w => w.focus()
-PASS cross-origin > w => w.frames
-PASS cross-origin > w => w.length
-PASS cross-origin > w => w.location
-PASS cross-origin > w => w.location = "#"
-PASS cross-origin > w => w.opener
-FAIL cross-origin > w => w.opener = "" assert_not_equals: Report not received got disallowed value "timeout"
-PASS cross-origin > w => w.postMessage("")
-PASS cross-origin > w => w.postMessage("", "")
-PASS cross-origin > w => w.self
-PASS cross-origin > w => w.top
-PASS cross-origin > w => w.window
-PASS same-site > w => w.blur()
-PASS same-site > w => w.close()
-PASS same-site > w => w.closed
-PASS same-site > w => w.focus()
-PASS same-site > w => w.frames
-PASS same-site > w => w.length
-PASS same-site > w => w.location
-PASS same-site > w => w.location = "#"
-PASS same-site > w => w.opener
-PASS same-site > w => w.opener = ""
-PASS same-site > w => w.postMessage("")
-PASS same-site > w => w.postMessage("", "")
-PASS same-site > w => w.self
-PASS same-site > w => w.top
-FAIL same-site > w => w.window assert_not_equals: Report not received got disallowed value "timeout"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property.https.html
deleted file mode 100644
index 73e40314..0000000
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property.https.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<title> Check the "report > body > property" is properly populated.</title>
-<meta name=timeout content=long>
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-<script src=/common/get-host-info.sub.js></script>
-<script src="/common/utils.js"></script>
-<script src="../resources/dispatcher.js"></script>
-<script>
-
-const directory = "/html/cross-origin-opener-policy/reporting";
-const executor_path = directory + "/resources/executor.html?pipe=";
-const coep_header = '|header(Cross-Origin-Embedder-Policy,require-corp)';
-
-let origin = [
-  ["cross-origin" , get_host_info().HTTPS_REMOTE_ORIGIN ] ,
-  ["same-site"  , get_host_info().HTTPS_ORIGIN        ] ,
-];
-
-let property= [
-  ["blur"        , w => w.blur()             ] ,
-  ["close"       , w => w.close()            ] ,
-  ["closed"      , w => w.closed             ] ,
-  ["focus"       , w => w.focus()            ] ,
-  ["frames"      , w => w.frames             ] ,
-  ["length"      , w => w.length             ] ,
-  ["location"    , w => w.location           ] ,
-  ["location"    , w => w.location = "#"     ] ,
-  ["opener"      , w => w.opener             ] ,
-  ["opener"      , w => w.opener = ""        ] ,
-  ["postMessage" , w => w.postMessage("")    ] ,
-  ["postMessage" , w => w.postMessage("", "")] ,
-  ["self"        , w => w.self               ] ,
-  ["top"         , w => w.top                ] ,
-  ["window"      , w => w.window             ] ,
-];
-
-origin.forEach(([origin_name, origin]) => {
-  property.forEach(([property, op]) =>  {
-    promise_test(async t => {
-      const report_token = token();
-      const openee_token = token();
-      const opener_token = token(); // The current test window.
-
-      const reportTo = reportToHeaders(report_token);
-      const openee_url = origin + executor_path + reportTo.header +
-        reportTo.coopReportOnlySameOriginHeader + coep_header +
-        `&uuid=${openee_token}`;
-      const openee = window.open(openee_url);
-      t.add_cleanup(() => send(openee_token, "window.close()"))
-
-      // 1. Make sure the new document to be loaded.
-      send(openee_token, `send("${opener_token}", "Ready");`);
-      let reply = await receive(opener_token);
-      assert_equals(reply, "Ready");
-
-      // 2. Try to access the openee. This shouldn't work because of COOP+COEP.
-      try {op(openee)} catch(e) {}
-
-      // 3. Check a reports is sent to the opener.
-      let report = await receiveReport(report_token, "access-to-coop-page")
-      assert_equals(report.body.property, property);
-
-    }, `${origin_name} > ${op}`);
-  })
-});
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/resources/test-access-property.js b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/resources/test-access-property.js
new file mode 100644
index 0000000..1a28e593
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/resources/test-access-property.js
@@ -0,0 +1,42 @@
+const directory = "/html/cross-origin-opener-policy/reporting";
+const executor_path = directory + "/resources/executor.html?pipe=";
+const coep_header = '|header(Cross-Origin-Embedder-Policy,require-corp)';
+
+const origin = [
+  ["cross-origin", get_host_info().HTTPS_REMOTE_ORIGIN ] ,
+  ["same-site"   , get_host_info().HTTPS_ORIGIN        ] ,
+];
+
+let testAccessProperty = (property, op, message) => {
+  origin.forEach(([origin_name, origin]) => {
+    promise_test(async t => {
+      const report_token = token();
+      const openee_token = token();
+      const opener_token = token(); // The current test window.
+
+      const reportTo = reportToHeaders(report_token);
+      const openee_url = origin + executor_path + reportTo.header +
+        reportTo.coopReportOnlySameOriginHeader + coep_header +
+        `&uuid=${openee_token}`;
+      const openee = window.open(openee_url);
+      t.add_cleanup(() => send(openee_token, "window.close()"))
+
+      // 1. Make sure the new document to be loaded.
+      send(openee_token, `send("${opener_token}", "Ready");`);
+      assert_equals(await receive(opener_token), "Ready");
+      // TODO(arthursozogni): Figure out why 2 round-trip is sometimes
+      // necessary to ensure the CoopAccessMonitor are installed.
+      send(openee_token, `send("${opener_token}", "Ready");`);
+      assert_equals(await receive(opener_token), "Ready");
+
+      // 2. Try to access the openee. This shouldn't work because of COOP+COEP.
+      try {op(openee)} catch(e) {}
+
+      // 3. Check a reports is sent to the opener.
+      let report = await receiveReport(report_token,
+        "access-to-coop-page-from-opener");
+      assert_equals(report.body.property, property);
+
+    }, `${origin_name} > ${op}`);
+  })
+};
diff --git a/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges-backspace.tentative.html b/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges-backspace.tentative.html
index 50d5cb96..a67bc763 100644
--- a/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges-backspace.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges-backspace.tentative.html
@@ -2,177 +2,25 @@
 <meta charset="utf-8">
 <meta name="timeout" content="long">
 <title>InputEvent.getTargetRanges() at Backspace</title>
+<div contenteditable></div>
+<script src="input-events-get-target-ranges.js"></script>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/testdriver.js"></script>
 <script src="/resources/testdriver-vendor.js"></script>
 <script src="/resources/testdriver-actions.js"></script>
-<div contenteditable></div>
 <script>
-const kBackspaceKey = "\uE003";
-const kArrowLeft =    "\uE012";
-const kShift =        "\uE008";
-const kMeta =         "\uE03d";
-const kControl =      "\uE009";
-const kAlt =          "\uE00A";
-
-const kImgSrc = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==";
-
-let selection = getSelection();
-let editor = document.querySelector("div[contenteditable]");
-let beforeinput = [];
-let input = [];
-editor.addEventListener("beforeinput", (e) => {
-  // NOTE: Blink makes `getTargetRanges()` return empty range after propagation,
-  //       but this test wants to check the result during propagation.
-  //       Therefore, we need to cache the result, but will assert if
-  //       `getTargetRanges()` returns different ranges after checking the
-  //       cached ranges.
-  e.cachedRanges = e.getTargetRanges();
-  beforeinput.push(e);
-});
-editor.addEventListener("input", (e) => {
-  e.cachedRanges = e.getTargetRanges();
-  input.push(e);
-});
-
-function reset() {
-  editor.focus();
-  beforeinput = [];
-  input = [];
-}
-
-function getRangeDescription(range) {
-  function getNodeDescription(node) {
-    if (!node) {
-      return "null";
-    }
-    switch (node.nodeType) {
-      case Node.TEXT_NODE:
-      case Node.COMMENT_NODE:
-      case Node.CDATA_SECTION_NODE:
-        return `${node.nodeName} "${node.data}"`;
-      case Node.ELEMENT_NODE:
-        return `<${node.nodeName.toLowerCase()}>`;
-      default:
-        return `${node.nodeName}`;
-    }
-  }
-  if (range === null) {
-    return "null";
-  }
-  if (range === undefined) {
-    return "undefined";
-  }
-  return range.startContainer == range.endContainer && range.startOffset == range.endOffset
-      ? `(${getNodeDescription(range.startContainer)}, ${range.startOffset})`
-      : `(${getNodeDescription(range.startContainer)}, ${range.startOffset}) - (${getNodeDescription(range.endContainer)}, ${range.endOffset})`;
-}
-
-function getArrayOfRangesDescription(arrayOfRanges) {
-  if (arrayOfRanges === null) {
-    return "null";
-  }
-  if (arrayOfRanges === undefined) {
-    return "undefined";
-  }
-  if (!Array.isArray(arrayOfRanges)) {
-    return "Unknown Object";
-  }
-  if (arrayOfRanges.length === 0) {
-    return "[]";
-  }
-  let result = "[";
-  for (let range of arrayOfRanges) {
-    result += `{${getRangeDescription(range)}},`;
-  }
-  result += "]";
-  return result;
-}
-
-function sendBackspaceKey(modifier) {
-  if (!modifier) {
-    return new test_driver.Actions()
-        .keyDown(kBackspaceKey)
-        .keyUp(kBackspaceKey)
-        .send();
-  }
-  return new test_driver.Actions()
-      .keyDown(modifier)
-      .keyDown(kBackspaceKey)
-      .keyUp(kBackspaceKey)
-      .keyUp(modifier)
-      .send();
-}
-
-function sendArrowLeftKey() {
-  return new test_driver.Actions()
-      .keyDown(kArrowLeft)
-      .keyUp(kArrowLeft)
-      .send();
-}
-
-function checkGetTargetRangesKeepReturningSameValue(event) {
-  // https://github.com/w3c/input-events/issues/114
-  assert_equals(getArrayOfRangesDescription(event.getTargetRanges()),
-      getArrayOfRangesDescription(event.cachedRanges),
-      `${event.type}.getTargetRanges() should keep returning the same array of ranges even after its propagation finished`);
-}
-
-function checkGetTargetRangesOfBeforeinputOnDeleteSomething(expectedRange) {
-  assert_equals(beforeinput.length, 1,
-      "One beforeinput event should be fired if the key operation deletes something");
-  assert_true(Array.isArray(beforeinput[0].cachedRanges),
-      "beforeinput[0].getTargetRanges() should return an array of StaticRange instances during propagation");
-  // Before checking the length of array of ranges, we should check first range
-  // first because the first range data is more important than whether there
-  // are additional unexpected ranges.
-  if (beforeinput[0].cachedRanges.length > 0) {
-    assert_equals(
-        getRangeDescription(beforeinput[0].cachedRanges[0]),
-        getRangeDescription(expectedRange),
-        `beforeinput.getTargetRanges() should return expected range (inputType is "${beforeinput[0].inputType}")`);
-    assert_equals(beforeinput[0].cachedRanges.length, 1,
-        "beforeinput.getTargetRanges() should return one range within an array");
-  }
-  assert_equals(beforeinput[0].cachedRanges.length, 1,
-      "One range should be returned from getTargetRanges() when the key operation deletes something");
-  checkGetTargetRangesKeepReturningSameValue(beforeinput[0]);
-}
-
-function checkGetTargetRangesOfInputOnDeleteSomething() {
-  assert_equals(input.length, 1,
-      "One input event should be fired if the key operation deletes something");
-  // https://github.com/w3c/input-events/issues/113
-  assert_true(Array.isArray(input[0].cachedRanges),
-      "input[0].getTargetRanges() should return an array of StaticRange instances during propagation");
-  assert_equals(input[0].cachedRanges.length, 0,
-      "input[0].getTargetRanges() should return empty array during propagation");
-  checkGetTargetRangesKeepReturningSameValue(input[0]);
-}
-
-function checkGetTargetRangesOfInputOnDoNothing() {
-  assert_equals(input.length, 0,
-      "input event shouldn't be fired when the key operation does not cause modifying the DOM tree");
-}
-
-function checkBeforeinputAndInputEventsOnNOOP() {
-  assert_equals(beforeinput.length, 0,
-      "beforeinput event shouldn't be fired when the key operation does not cause modifying the DOM tree");
-  assert_equals(input.length, 0,
-      "input event shouldn't be fired when the key operation does not cause modifying the DOM tree");
-}
+"use strict";
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p>";
-  selection.collapse(editor.firstChild.firstChild, 0);
+  initializeTest("<p>abc</p>");
+  gSelection.collapse(gEditor.firstChild.firstChild, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abc</p>");
+  assert_equals(gEditor.innerHTML, "<p>abc</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: editor.firstChild.firstChild,
+    startContainer: gEditor.firstChild.firstChild,
     startOffset: 0,
-    endContainer: editor.firstChild.firstChild,
+    endContainer: gEditor.firstChild.firstChild,
     endOffset: 0,
   });
   checkGetTargetRangesOfInputOnDoNothing();
@@ -180,15 +28,14 @@
 
 // Simply deletes the previous ASCII character of caret position.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p>";
-  selection.collapse(editor.firstChild.firstChild, 1);
+  initializeTest("<p>abc</p>");
+  gSelection.collapse(gEditor.firstChild.firstChild, 1);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>bc</p>");
+  assert_equals(gEditor.innerHTML, "<p>bc</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: editor.firstChild.firstChild,
+    startContainer: gEditor.firstChild.firstChild,
     startOffset: 0,
-    endContainer: editor.firstChild.firstChild,
+    endContainer: gEditor.firstChild.firstChild,
     endOffset: 1,
   });
   checkGetTargetRangesOfInputOnDeleteSomething();
@@ -196,15 +43,14 @@
 
 // Simply deletes the previous ASCII character of caret position.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p>";
-  selection.collapse(editor.firstChild.firstChild, 2);
+  initializeTest("<p>abc</p>");
+  gSelection.collapse(gEditor.firstChild.firstChild, 2);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>ac</p>");
+  assert_equals(gEditor.innerHTML, "<p>ac</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: editor.firstChild.firstChild,
+    startContainer: gEditor.firstChild.firstChild,
     startOffset: 1,
-    endContainer: editor.firstChild.firstChild,
+    endContainer: gEditor.firstChild.firstChild,
     endOffset: 2,
   });
   checkGetTargetRangesOfInputOnDeleteSomething();
@@ -212,15 +58,14 @@
 
 // Simply deletes the previous ASCII character of caret position.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p>";
-  selection.collapse(editor.firstChild.firstChild, 3);
+  initializeTest("<p>abc</p>");
+  gSelection.collapse(gEditor.firstChild.firstChild, 3);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>ab</p>");
+  assert_equals(gEditor.innerHTML, "<p>ab</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: editor.firstChild.firstChild,
+    startContainer: gEditor.firstChild.firstChild,
     startOffset: 2,
-    endContainer: editor.firstChild.firstChild,
+    endContainer: gEditor.firstChild.firstChild,
     endOffset: 3,
   });
   checkGetTargetRangesOfInputOnDeleteSomething();
@@ -231,14 +76,13 @@
 // contained by a range of `getTargetRanges()`.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>a<span>b</span>c</p>";
-  let c = editor.querySelector("span").nextSibling;
-  selection.collapse(c, 0);
+  initializeTest("<p>a<span>b</span>c</p>");
+  let c = gEditor.querySelector("span").nextSibling;
+  gSelection.collapse(c, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>ac</p>");
+  assert_equals(gEditor.innerHTML, "<p>ac</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: editor.firstChild,
+    startContainer: gEditor.firstChild,
     startOffset: 1,
     endContainer: c,
     endOffset: 0,
@@ -251,16 +95,15 @@
 // contained by a range of `getTargetRanges()`.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>a<span>b</span>c</p>";
-  let b = editor.querySelector("span").firstChild;
-  selection.collapse(b, 1);
+  initializeTest("<p>a<span>b</span>c</p>");
+  let b = gEditor.querySelector("span").firstChild;
+  gSelection.collapse(b, 1);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>ac</p>");
+  assert_equals(gEditor.innerHTML, "<p>ac</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: editor.firstChild,
+    startContainer: gEditor.firstChild,
     startOffset: 1,
-    endContainer: editor.firstChild,
+    endContainer: gEditor.firstChild,
     endOffset: 2,
   });
   checkGetTargetRangesOfInputOnDeleteSomething();
@@ -271,30 +114,29 @@
 // the range of `getTargetRanges()`, but needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p> abc</p>";
-  selection.collapse(editor.firstChild.firstChild, 2);
+  initializeTest("<p> abc</p>");
+  gSelection.collapse(gEditor.firstChild.firstChild, 2);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<p>bc</p>", "<p> bc</p>"]);
+  assert_in_array(gEditor.innerHTML, ["<p>bc</p>",
+                                      "<p> bc</p>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: editor.firstChild.firstChild,
-    startOffset: editor.firstChild.firstChild.length == 2 ? 0 : 1,
-    endContainer: editor.firstChild.firstChild,
+    startContainer: gEditor.firstChild.firstChild,
+    startOffset: gEditor.firstChild.firstChild.length == 2 ? 0 : 1,
+    endContainer: gEditor.firstChild.firstChild,
     endOffset: 2,
   });
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Backspace at "<p> a[]bc</p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p><p>def</p>";
-  let p1 = editor.firstChild;
+  initializeTest("<p>abc</p><p>def</p>");
+  let p1 = gEditor.firstChild;
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(def, 0);
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -310,15 +152,14 @@
 // contained by the range of `getTargetRanges()`, but needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc   </p><p>   def</p>";
-  let p1 = editor.firstChild;
+  initializeTest("<p>abc   </p><p>   def</p>");
+  let p1 = gEditor.firstChild;
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(def, 3);
+  gSelection.collapse(def, 3);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -334,15 +175,14 @@
 // contained by the range of `getTargetRanges()`, but needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc   </p><p>   def</p>";
-  let p1 = editor.firstChild;
+  initializeTest("<p>abc   </p><p>   def</p>");
+  let p1 = gEditor.firstChild;
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(def, 2);
+  gSelection.collapse(def, 2);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -358,15 +198,14 @@
 // contained by the range of `getTargetRanges()`, but needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc   </p><p>   def</p>";
-  let p1 = editor.firstChild;
+  initializeTest("<p>abc   </p><p>   def</p>");
+  let p1 = gEditor.firstChild;
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(def, 1);
+  gSelection.collapse(def, 1);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -382,15 +221,14 @@
 // contained by the range of `getTargetRanges()`, but needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc   </p><p>   def</p>";
-  let p1 = editor.firstChild;
+  initializeTest("<p>abc   </p><p>   def</p>");
+  let p1 = gEditor.firstChild;
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(def, 0);
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -400,38 +238,13 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Backspace at "<p>abc   </p><p>[]   def</p>"');
 
-// Invisible leading white-spaces in current block and invisible trailing
-// white-spaces in the previous block should be deleted for avoiding they
-// becoming visible when the blocks are joined.  Perhaps, they should be
-// contained by the range of `getTargetRanges()`, but needs discussion.
-// https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc   </p><p>   def</p>";
-  let p1 = editor.firstChild;
-  let abc = p1.firstChild;
-  let p2 = p1.nextSibling;
-  let def = p2.firstChild;
-  selection.setBaseAndExtent(abc, 6, def, 0);
+  initializeTest("<p>abc</p><p><b>def</b></p>");
+  let abc = gEditor.querySelector("p").firstChild;
+  let def = gEditor.querySelector("b").firstChild;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 3,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<p>abc   [</p><p>]   def</p>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p><p><b>def</b></p>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("b").firstChild;
-  selection.collapse(def, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abc<b>def</b></p>");
+  assert_equals(gEditor.innerHTML, "<p>abc<b>def</b></p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -442,14 +255,13 @@
 }, 'Backspace at "<p>abc</p><p><b>[]def</b></p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p><b>abc</b></p><p><b>def</b></p>";
-  let abc = editor.querySelector("p > b").firstChild;
-  let def = editor.querySelector("P + p > b").firstChild;
-  selection.collapse(def, 0);
+  initializeTest("<p><b>abc</b></p><p><b>def</b></p>");
+  let abc = gEditor.querySelector("p > b").firstChild;
+  let def = gEditor.querySelector("P + p > b").firstChild;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<p><b>abc</b><b>def</b></p>",
-                                     "<p><b>abcdef</b></p>"]);
+  assert_in_array(gEditor.innerHTML, ["<p><b>abc</b><b>def</b></p>",
+                                      "<p><b>abcdef</b></p>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -460,13 +272,12 @@
 }, 'Backspace at "<p><b>abc</b></p><p><b>[]def</b></p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p><i>abc</i></p><p><b>def</b></p>";
-  let abc = editor.querySelector("i").firstChild;
-  let def = editor.querySelector("b").firstChild;
-  selection.collapse(def, 0);
+  initializeTest("<p><i>abc</i></p><p><b>def</b></p>");
+  let abc = gEditor.querySelector("i").firstChild;
+  let def = gEditor.querySelector("b").firstChild;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p><i>abc</i><b>def</b></p>");
+  assert_equals(gEditor.innerHTML, "<p><i>abc</i><b>def</b></p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -483,17 +294,16 @@
 // the range of `getTargetRanges()`, but needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<pre>abc   </pre><p>   def</p>";
-  let pre = editor.firstChild;
+  initializeTest("<pre>abc   </pre><p>   def</p>");
+  let pre = gEditor.firstChild;
   let abc = pre.firstChild;
   let p = pre.nextSibling;
   let def = p.firstChild;
-  selection.collapse(def, 3);
+  gSelection.collapse(def, 3);
   await sendBackspaceKey();
   // https://github.com/w3c/input-events/issues/112
   // Shouldn't make the invisible white-spaces visible.
-  assert_equals(editor.innerHTML, "<pre>abc   def</pre>");
+  assert_equals(gEditor.innerHTML, "<pre>abc   def</pre>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 6,
@@ -513,15 +323,14 @@
 // implementation cost and runtime cost.  Needs discuss.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<pre>abc   </pre><p>   def   </p>";
-  let pre = editor.firstChild;
+  initializeTest("<pre>abc   </pre><p>   def   </p>");
+  let pre = gEditor.firstChild;
   let abc = pre.firstChild;
   let p = pre.nextSibling;
   let def = p.firstChild;
-  selection.collapse(def, 3);
+  gSelection.collapse(def, 3);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<pre>abc   def   </pre>");
+  assert_equals(gEditor.innerHTML, "<pre>abc   def   </pre>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 6,
@@ -539,18 +348,17 @@
 // visible leading white-spaces.  But needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc   </p><pre>   def</pre>";
-  let p = editor.firstChild;
+  initializeTest("<p>abc   </p><pre>   def</pre>");
+  let p = gEditor.firstChild;
   let abc = p.firstChild;
   let pre = p.nextSibling;
   let def = pre.firstChild;
-  selection.collapse(def, 0);
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<p>abc &nbsp; def</p>",
-                                     "<p>abc&nbsp;&nbsp; def</p>",
-                                     "<p>abc&nbsp; &nbsp;def</p>",
-                                     "<p>abc &nbsp;&nbsp;def</p>"]);
+  assert_in_array(gEditor.innerHTML, ["<p>abc &nbsp; def</p>",
+                                      "<p>abc&nbsp;&nbsp; def</p>",
+                                      "<p>abc&nbsp; &nbsp;def</p>",
+                                      "<p>abc &nbsp;&nbsp;def</p>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 6,
@@ -566,15 +374,14 @@
 // the `<br>` element and block boundaries. But maybe needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<br></p><p>def</p>";
-  let p1 = editor.firstChild;
+  initializeTest("<p>abc<br></p><p>def</p>");
+  let p1 = gEditor.firstChild;
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(def, 0);
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p1,
     startOffset: 1,
@@ -590,15 +397,14 @@
 // contain the `<br>` element and block boundaries. But maybe needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<br><br></p><p>def</p>";
-  let p1 = editor.firstChild;
+  initializeTest("<p>abc<br><br></p><p>def</p>");
+  let p1 = gEditor.firstChild;
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(def, 0);
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abc<br>def</p>");
+  assert_equals(gEditor.innerHTML, "<p>abc<br>def</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p1,
     startOffset: 2,
@@ -608,101 +414,15 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Backspace at "<p>abc<br><br></p><p>[]def</p>"');
 
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p><p>def</p>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p + p").firstChild;
-  selection.setBaseAndExtent(abc, 2, def, 1);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abef</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 2,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<p>ab[c</p><p>d]ef</p>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc </p><p> def</p>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p + p").firstChild;
-  selection.setBaseAndExtent(abc, 2, def, 2);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abef</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 2,
-    endContainer: def,
-    endOffset: 2,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<p>ab[c </p><p> d]ef</p>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc </p><p> def</p>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p + p").firstChild;
-  selection.setBaseAndExtent(abc, 2, def, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abdef</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 2,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<p>ab[c </p><p>] def</p>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc </p><p> def</p>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p + p").firstChild;
-  selection.setBaseAndExtent(abc, 4, def, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<p>abc [</p><p>] def</p>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc </p><p> def</p>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p + p").firstChild;
-  selection.setBaseAndExtent(abc, 4, def, 1);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<p>abc [</p><p> ]def</p>"');
-
 // Deleting visible `<br>` element should be contained by a range of
 // `getTargetRanges()`.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<br>def</p>";
+  initializeTest("<p>abc<br>def</p>");
   let p = document.querySelector("p");
-  let def = editor.querySelector("br").nextSibling;
-  selection.collapse(def, 0);
+  let def = gEditor.querySelector("br").nextSibling;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -715,13 +435,12 @@
 // Deleting visible `<br>` element following white-space should not include
 // the preceding white-space in the range.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc <br>def</p>";
-  let p = editor.querySelector("p");
-  let def = editor.querySelector("br").nextSibling;
-  selection.collapse(def, 0);
+  initializeTest("<p>abc <br>def</p>");
+  let p = gEditor.querySelector("p");
+  let def = gEditor.querySelector("br").nextSibling;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abc def</p>");
+  assert_equals(gEditor.innerHTML, "<p>abc def</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -732,13 +451,12 @@
 }, 'Backspace at "<p>abc <br>[]def</p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<p>abc<img src="${kImgSrc}">def</p>`;
-  let p = editor.querySelector("p");
+  initializeTest(`<p>abc<img src="${kImgSrc}">def</p>`);
+  let p = gEditor.querySelector("p");
   let def = p.lastChild;
-  selection.collapse(def, 0);
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -751,13 +469,12 @@
 // White-spaces around `<img>` element are visible so that they shouldn't
 // be included into the target ranges.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<p>abc <img src="${kImgSrc}">def</p>`;
-  let p = editor.querySelector("p");
+  initializeTest(`<p>abc <img src="${kImgSrc}">def</p>`);
+  let p = gEditor.querySelector("p");
   let def = p.lastChild;
-  selection.collapse(def, 0);
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abc def</p>");
+  assert_equals(gEditor.innerHTML, "<p>abc def</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -770,13 +487,12 @@
 // White-spaces around `<img>` element are visible so that they shouldn't
 // be included into the target ranges.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<p>abc<img src="${kImgSrc}"> def</p>`;
-  let p = editor.querySelector("p");
+  initializeTest(`<p>abc<img src="${kImgSrc}"> def</p>`);
+  let p = gEditor.querySelector("p");
   let def = p.lastChild;
-  selection.collapse(def, 0);
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abc def</p>");
+  assert_equals(gEditor.innerHTML, "<p>abc def</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -787,13 +503,12 @@
 }, 'Backspace at "<p>abc<img>[] def</p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<p>abc<img src="${kImgSrc}"><img src="${kImgSrc}">def</p>`;
-  let p = editor.querySelector("p");
+  initializeTest(`<p>abc<img src="${kImgSrc}"><img src="${kImgSrc}">def</p>`);
+  let p = gEditor.querySelector("p");
   let abc = p.firstChild;
-  selection.collapse(p, 2);
+  gSelection.collapse(p, 2);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, `<p>abc<img src="${kImgSrc}">def</p>`);
+  assert_equals(gEditor.innerHTML, `<p>abc<img src="${kImgSrc}">def</p>`);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -804,13 +519,12 @@
 }, 'Backspace at "<p>abc<img>{}<img>def</p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<p>abc<img src="${kImgSrc}"><img src="${kImgSrc}">def</p>`;
-  let p = editor.querySelector("p");
+  initializeTest(`<p>abc<img src="${kImgSrc}"><img src="${kImgSrc}">def</p>`);
+  let p = gEditor.querySelector("p");
   let def = p.lastChild;
-  selection.collapse(def, 0);
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, `<p>abc<img src="${kImgSrc}">def</p>`);
+  assert_equals(gEditor.innerHTML, `<p>abc<img src="${kImgSrc}">def</p>`);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 2,
@@ -820,36 +534,14 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Backspace at "<p>abc<img><img>[]def</p>"');
 
-// Different from collapsed range around an atomic content, non-collapsed
-// range may not be shrunken to select only the atomic content for avoid
-// to waste runtime cost.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<p>abc<img src="${kImgSrc}">def</p>`;
-  let p = editor.querySelector("p");
-  let abc = p.firstChild;
-  let def = p.lastChild;
-  selection.setBaseAndExtent(abc, 3, def, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, `<p>abcdef</p>`);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<p>abc[<img>]def</p>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc<hr>def</div>`;
-  let div = editor.querySelector("div");
-  let hr = editor.querySelector("hr");
+  initializeTest(`<div>abc<hr>def</div>`);
+  let div = gEditor.querySelector("div");
+  let hr = gEditor.querySelector("hr");
   let def = hr.nextSibling;
-  selection.collapse(def, 0);
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdef</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: div,
     startOffset: 1,
@@ -864,15 +556,14 @@
 // visible.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc <hr>def</div>`;
-  let div = editor.querySelector("div");
+  initializeTest(`<div>abc <hr>def</div>`);
+  let div = gEditor.querySelector("div");
   let abc = div.firstChild;
-  let hr = editor.querySelector("hr");
+  let hr = gEditor.querySelector("hr");
   let def = hr.nextSibling;
-  selection.collapse(def, 0);
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdef</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -887,14 +578,13 @@
 // visible.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc<hr> def</div>`;
-  let div = editor.querySelector("div");
-  let hr = editor.querySelector("hr");
+  initializeTest(`<div>abc<hr> def</div>`);
+  let div = gEditor.querySelector("div");
+  let hr = gEditor.querySelector("hr");
   let def = hr.nextSibling;
-  selection.collapse(def, 1);
+  gSelection.collapse(def, 1);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdef</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: div,
     startOffset: 1,
@@ -907,13 +597,12 @@
 // Invisible `<br>` element immediately before `<hr>` element should be
 // deleted once, and both of them should be included in the target range.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc<br><hr>def</div>`;
-  let div = editor.querySelector("div");
+  initializeTest(`<div>abc<br><hr>def</div>`);
+  let div = gEditor.querySelector("div");
   let def = div.lastChild;
-  selection.collapse(def, 0);
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdef</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: div,
     startOffset: 1,
@@ -923,90 +612,17 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Backspace at "<div>abc<br><hr>[]def</div>"');
 
-promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc<hr>def</div>`;
-  let div = editor.querySelector("div");
-  let abc = div.firstChild;
-  let def = div.lastChild;
-  selection.setBaseAndExtent(abc, 3, def, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc[<hr>]def</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc <hr>def</div>`;
-  let div = editor.querySelector("div");
-  let abc = div.firstChild;
-  let def = div.lastChild;
-  selection.setBaseAndExtent(abc, 4, def, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc [<hr>]def</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc <hr> def</div>`;
-  let div = editor.querySelector("div");
-  let abc = div.firstChild;
-  let def = div.lastChild;
-  selection.setBaseAndExtent(abc, 4, def, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc [<hr>] def</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc <hr> def</div>`;
-  let div = editor.querySelector("div");
-  let abc = div.firstChild;
-  let def = div.lastChild;
-  selection.setBaseAndExtent(div, 1, div, 2);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc {<hr>} def</div>"');
-
 // Deleting visible `<br>` element followed by white-space should include
 // the following white-space in the range because it shouldn't become
 // visible and should be deleted for avoiding it.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<br> def</p>";
-  let p = editor.querySelector("p");
-  let def = editor.querySelector("br").nextSibling;
-  selection.collapse(def, 0);
+  initializeTest("<p>abc<br> def</p>");
+  let p = gEditor.querySelector("p");
+  let def = gEditor.querySelector("br").nextSibling;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -1016,13 +632,12 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Backspace at "<p>abc<br>[] def</p>"');
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<br> def</p>";
-  let p = editor.querySelector("p");
-  let def = editor.querySelector("br").nextSibling;
-  selection.collapse(def, 1);
+  initializeTest("<p>abc<br> def</p>");
+  let p = gEditor.querySelector("p");
+  let def = gEditor.querySelector("br").nextSibling;
+  gSelection.collapse(def, 1);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -1032,35 +647,15 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Backspace at "<p>abc<br> []def</p>"');
 
-// Deleting visible `<br>` element should be contained by a range of
-// `getTargetRanges()`.  However, when only the `<br>` element is selected,
-// the range shouldn't start from nor end by surrounding text nodes?
-// https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<br>def</p>";
-  selection.setBaseAndExtent(editor.firstChild, 1, editor.firstChild, 2);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: editor.firstChild,
-    startOffset: 1,
-    endContainer: editor.firstChild,
-    endOffset: 2,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<p>abc{<br>}def</p>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<p>def<br>ghi</p></div>";
-  let p = editor.querySelector("p");
+  initializeTest("<div>abc<p>def<br>ghi</p></div>");
+  let p = gEditor.querySelector("p");
   let def = p.firstChild;
-  let abc = editor.firstChild.firstChild;
-  selection.collapse(def, 0);
+  let abc = gEditor.firstChild.firstChild;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
-                                     "<div>abcdef<br><p>ghi</p></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
+                                      "<div>abcdef<br><p>ghi</p></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1070,40 +665,20 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Backspace at "<div>abc<p>[]def<br>ghi</p></div>"');
 
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<p>def<br>ghi</p></div>";
-  let p = editor.querySelector("p");
-  let def = p.firstChild;
-  let abc = editor.firstChild.firstChild;
-  selection.setBaseAndExtent(abc, 3, def, 0);
-  await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
-                                     "<div>abcdef<br><p>ghi</p></div>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc[<p>]def<br>ghi</p></div>"');
-
 // Joining parent block and child block should remove invisible preceding
 // white-spaces of the child block and invisible leading white-spaces in
 // the child block, and they should be contained in a range of
 // `getTargetRanges()`, but maybe needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc   <p>   def<br>ghi</p></div>";
-  let p = editor.querySelector("p");
+  initializeTest("<div>abc   <p>   def<br>ghi</p></div>");
+  let p = gEditor.querySelector("p");
   let def = p.firstChild;
-  let abc = editor.firstChild.firstChild;
-  selection.collapse(def, 3);
+  let abc = gEditor.firstChild.firstChild;
+  gSelection.collapse(def, 3);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
-                                     "<div>abcdef<br><p>ghi</p></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
+                                      "<div>abcdef<br><p>ghi</p></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1114,32 +689,12 @@
 }, 'Backspace at "<div>abc   <p>   []def<br>ghi</p></div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc   <p>   def<br>ghi</p></div>";
-  let p = editor.querySelector("p");
-  let def = p.firstChild;
-  let abc = editor.firstChild.firstChild;
-  selection.setBaseAndExtent(abc, abc.length, def, 0);
+  initializeTest("<div>abc<p><b>def</b></p></div>");
+  let abc = gEditor.querySelector("div").firstChild;
+  let def = gEditor.querySelector("b").firstChild;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
-                                     "<div>abcdef<br><p>ghi</p></div>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 3,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc   [<p>]   def<br>ghi</p></div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<p><b>def</b></p></div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("b").firstChild;
-  selection.collapse(def, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abc<b>def</b></div>");
+  assert_equals(gEditor.innerHTML, "<div>abc<b>def</b></div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1150,14 +705,13 @@
 }, 'Backspace at "<div>abc<p><b>[]def</b></p></div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><b>abc</b><p><b>def</b></p></div>";
-  let abc = editor.querySelector("b").firstChild;
-  let def = editor.querySelector("p > b").firstChild;
-  selection.collapse(def, 0);
+  initializeTest("<div><b>abc</b><p><b>def</b></p></div>");
+  let abc = gEditor.querySelector("b").firstChild;
+  let def = gEditor.querySelector("p > b").firstChild;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<div><b>abc</b><b>def</b></div>",
-                                     "<div><b>abcdef</b></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div><b>abc</b><b>def</b></div>",
+                                      "<div><b>abcdef</b></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1168,13 +722,12 @@
 }, 'Backspace at "<div><b>abc</b><p><b>[]def</b></p></div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><i>abc</i><p><b>def</b></p></div>";
-  let abc = editor.querySelector("i").firstChild;
-  let def = editor.querySelector("b").firstChild;
-  selection.collapse(def, 0);
+  initializeTest("<div><i>abc</i><p><b>def</b></p></div>");
+  let abc = gEditor.querySelector("i").firstChild;
+  let def = gEditor.querySelector("b").firstChild;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div><i>abc</i><b>def</b></div>");
+  assert_equals(gEditor.innerHTML, "<div><i>abc</i><b>def</b></div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1185,14 +738,13 @@
 }, 'Backspace at "<div><i>abc</i><p><b>[]def</b></p></div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p>abc</p>def</div>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p").nextSibling;
-  selection.collapse(def, 0);
+  initializeTest("<div><p>abc</p>def</div>");
+  let abc = gEditor.querySelector("p").firstChild;
+  let def = gEditor.querySelector("p").nextSibling;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<div><p>abcdef</p></div>",
-                                     "<div><p>abcdef<br></p></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div><p>abcdef</p></div>",
+                                      "<div><p>abcdef<br></p></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1202,38 +754,19 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Backspace at "<div><p>abc</p>[]def</div>"');
 
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p>abc</p>def</div>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p").nextSibling;
-  selection.setBaseAndExtent(abc, 3, def, 0);
-  await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<div><p>abcdef</p></div>",
-                                     "<div><p>abcdef<br></p></div>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div><p>abc[</p>]def</div>"');
-
 // Joining child block and parent block should remove invisible trailing
 // white-spaces of the child block and invisible following white-spaces
 // in the parent block, and they should be contained by a range of
 // `getTargetRanges()`, but maybe needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p>abc   </p>   def</div>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p").nextSibling;
-  selection.collapse(def, 3);
+  initializeTest("<div><p>abc   </p>   def</div>");
+  let abc = gEditor.querySelector("p").firstChild;
+  let def = gEditor.querySelector("p").nextSibling;
+  gSelection.collapse(def, 3);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<div><p>abcdef</p></div>",
-                                     "<div><p>abcdef<br></p></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div><p>abcdef</p></div>",
+                                      "<div><p>abcdef<br></p></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1244,31 +777,12 @@
 }, 'Backspace at "<div><p>abc   </p>   []def</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p>abc   </p>   def</div>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p").nextSibling;
-  selection.setBaseAndExtent(abc, abc.length, def, 0);
+  initializeTest("<div><p><b>abc</b></p>def</div>");
+  let abc = gEditor.querySelector("b").firstChild;
+  let def = gEditor.querySelector("p").nextSibling;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<div><p>abcdef</p></div>",
-                                     "<div><p>abcdef<br></p></div>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 3,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div><p>abc   [</p>]   def</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p><b>abc</b></p>def</div>";
-  let abc = editor.querySelector("b").firstChild;
-  let def = editor.querySelector("p").nextSibling;
-  selection.collapse(def, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div><p><b>abc</b>def</p></div>");
+  assert_equals(gEditor.innerHTML, "<div><p><b>abc</b>def</p></div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1279,14 +793,13 @@
 }, 'Backspace at "<div><p><b>abc</b></p>[]def</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p><b>abc</b></p><b>def</b></div>";
-  let abc = editor.querySelector("b").firstChild;
-  let def = editor.querySelector("div > b").firstChild;
-  selection.collapse(def, 0);
+  initializeTest("<div><p><b>abc</b></p><b>def</b></div>");
+  let abc = gEditor.querySelector("b").firstChild;
+  let def = gEditor.querySelector("div > b").firstChild;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<div><p><b>abc</b><b>def</b></p></div>",
-                                     "<div><p><b>abcdef</b></p></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div><p><b>abc</b><b>def</b></p></div>",
+                                      "<div><p><b>abcdef</b></p></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1297,13 +810,12 @@
 }, 'Backspace at "<div><p><b>abc</b></p><b>[]def</b></div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p><b>abc</b></p><i>def</i></div>";
-  let abc = editor.querySelector("b").firstChild;
-  let def = editor.querySelector("i").firstChild;
-  selection.collapse(def, 0);
+  initializeTest("<div><p><b>abc</b></p><i>def</i></div>");
+  let abc = gEditor.querySelector("b").firstChild;
+  let def = gEditor.querySelector("i").firstChild;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div><p><b>abc</b><i>def</i></p></div>");
+  assert_equals(gEditor.innerHTML, "<div><p><b>abc</b><i>def</i></p></div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1314,13 +826,12 @@
 }, 'Backspace at "<div><p><b>abc</b></p><i>[]def</i></div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li></ul>ghi</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  selection.collapse(def, 0);
+  initializeTest("<div>abc<ul><li>def</li></ul>ghi</div>");
+  let abc = gEditor.querySelector("div").firstChild;
+  let def = gEditor.querySelector("li").firstChild;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdefghi</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1331,13 +842,12 @@
 }, 'Backspace at "<div>abc<ul><li>[]def</li></ul>ghi</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc  <ul><li> def </li></ul>  ghi</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  selection.collapse(def, 1);
+  initializeTest("<div>abc  <ul><li> def </li></ul>  ghi</div>");
+  let abc = gEditor.querySelector("div").firstChild;
+  let def = gEditor.querySelector("li").firstChild;
+  gSelection.collapse(def, 1);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdefghi</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1348,13 +858,12 @@
 }, 'Backspace at "<div>abc  <ul><li> []def </li></ul>  ghi</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc  <ul><li> def </li></ul>  ghi</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  selection.collapse(def, 0);
+  initializeTest("<div>abc  <ul><li> def </li></ul>  ghi</div>");
+  let abc = gEditor.querySelector("div").firstChild;
+  let def = gEditor.querySelector("li").firstChild;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdefghi</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1365,47 +874,12 @@
 }, 'Backspace at "<div>abc  <ul><li>[] def </li></ul>  ghi</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li></ul>ghi</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  selection.setBaseAndExtent(abc, 3, def, 0);
+  initializeTest("<div>abc<ul><li>def</li></ul>ghi</div>");
+  let def = gEditor.querySelector("li").firstChild;
+  let ghi = gEditor.querySelector("ul").nextSibling;
+  gSelection.collapse(ghi, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc[<ul><li>]def</li></ul>ghi</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc  <ul><li> def </li></ul>  ghi</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  selection.setBaseAndExtent(abc, abc.length, def, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc  [<ul><li>] def </li></ul>  ghi</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li></ul>ghi</div>";
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("ul").nextSibling;
-  selection.collapse(ghi, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abc<ul><li>defghi</li></ul></div>");
+  assert_equals(gEditor.innerHTML, "<div>abc<ul><li>defghi</li></ul></div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: def,
     startOffset: 3,
@@ -1416,15 +890,14 @@
 }, 'Backspace at "<div>abc<ul><li>def</li></ul>[]ghi</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc <ul><li>  def  </li></ul> ghi</div>";
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("ul").nextSibling;
-  selection.collapse(ghi, 1);
+  initializeTest("<div>abc <ul><li>  def  </li></ul> ghi</div>");
+  let def = gEditor.querySelector("li").firstChild;
+  let ghi = gEditor.querySelector("ul").nextSibling;
+  gSelection.collapse(ghi, 1);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<div>abc <ul><li>  defghi</li></ul></div>",
-                                     "<div>abc <ul><li>defghi</li></ul></div>",
-                                     "<div>abc<ul><li>defghi</li></ul></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div>abc <ul><li>  defghi</li></ul></div>",
+                                      "<div>abc <ul><li>defghi</li></ul></div>",
+                                      "<div>abc<ul><li>defghi</li></ul></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: def,
     startOffset: 5,
@@ -1435,15 +908,14 @@
 }, 'Backspace at "<div>abc <ul><li>  def  </li></ul> []ghi</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc <ul><li>  def  </li></ul> ghi</div>";
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("ul").nextSibling;
-  selection.collapse(ghi, 0);
+  initializeTest("<div>abc <ul><li>  def  </li></ul> ghi</div>");
+  let def = gEditor.querySelector("li").firstChild;
+  let ghi = gEditor.querySelector("ul").nextSibling;
+  gSelection.collapse(ghi, 0);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<div>abc <ul><li>  defghi</li></ul></div>",
-                                     "<div>abc <ul><li>defghi</li></ul></div>",
-                                     "<div>abc<ul><li>defghi</li></ul></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div>abc <ul><li>  defghi</li></ul></div>",
+                                      "<div>abc <ul><li>defghi</li></ul></div>",
+                                      "<div>abc<ul><li>defghi</li></ul></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: def,
     startOffset: 5,
@@ -1454,49 +926,12 @@
 }, 'Backspace at "<div>abc <ul><li>  def  </li></ul>[] ghi</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li></ul>ghi</div>";
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("ul").nextSibling;
-  selection.setBaseAndExtent(def, 3, ghi, 0);
+  initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>");
+  let abc = gEditor.querySelector("div").firstChild;
+  let def = gEditor.querySelector("li").firstChild;
+  gSelection.collapse(def, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abc<ul><li>defghi</li></ul></div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: def,
-    startOffset: 3,
-    endContainer: ghi,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc<ul><li>def[</li></ul>]ghi</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc <ul><li>  def  </li></ul> ghi</div>";
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("ul").nextSibling;
-  selection.setBaseAndExtent(def, def.length, ghi, 0);
-  await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<div>abc <ul><li>  defghi</li></ul></div>",
-                                     "<div>abc <ul><li>defghi</li></ul></div>",
-                                     "<div>abc<ul><li>defghi</li></ul></div>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: def,
-    startOffset: 5,
-    endContainer: ghi,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc <ul><li>  def  [</li></ul>] ghi</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  selection.collapse(def, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdef<ul><li>ghi</li></ul>jkl</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdef<ul><li>ghi</li></ul>jkl</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1507,49 +942,12 @@
 }, 'Backspace at "<div>abc<ul><li>[]def</li><li>ghi</li></ul>jkl</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  selection.setBaseAndExtent(abc, 3, def, 0);
+  initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>");
+  let def = gEditor.querySelector("li").firstChild;
+  let ghi = gEditor.querySelector("li + li").firstChild;
+  gSelection.collapse(ghi, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abcdef<ul><li>ghi</li></ul>jkl</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc[<ul><li>]def</li><li>ghi</li></ul>jkl</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("li + li").firstChild;
-  selection.setBaseAndExtent(abc, 3, ghi, 0);
-  await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<div>abcghijkl</div>",
-                                     "<div>abcghijkl<br></div>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: ghi,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc[<ul><li>def</li><li>]ghi</li></ul>jkl</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("li + li").firstChild;
-  selection.collapse(ghi, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abc<ul><li>defghi</li></ul>jkl</div>");
+  assert_equals(gEditor.innerHTML, "<div>abc<ul><li>defghi</li></ul>jkl</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: def,
     startOffset: 3,
@@ -1560,30 +958,12 @@
 }, 'Backspace at "<div>abc<ul><li>def</li><li>[]ghi</li></ul>jkl</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("li + li").firstChild;
-  selection.setBaseAndExtent(def, 3, ghi, 0);
+  initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>");
+  let ghi = gEditor.querySelector("li + li").firstChild;
+  let jkl = gEditor.querySelector("ul").nextSibling;
+  gSelection.collapse(jkl, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abc<ul><li>defghi</li></ul>jkl</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: def,
-    startOffset: 3,
-    endContainer: ghi,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc<ul><li>def[</li><li>]ghi</li></ul>jkl</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let ghi = editor.querySelector("li + li").firstChild;
-  let jkl = editor.querySelector("ul").nextSibling;
-  selection.collapse(jkl, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abc<ul><li>def</li><li>ghijkl</li></ul></div>");
+  assert_equals(gEditor.innerHTML, "<div>abc<ul><li>def</li><li>ghijkl</li></ul></div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: ghi,
     startOffset: 3,
@@ -1593,53 +973,18 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Backspace at "<div>abc<ul><li>def</li><li>ghi</li></ul>[]jkl</div>"');
 
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let ghi = editor.querySelector("li + li").firstChild;
-  let jkl = editor.querySelector("ul").nextSibling;
-  selection.setBaseAndExtent(ghi, 3, jkl, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abc<ul><li>def</li><li>ghijkl</li></ul></div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: ghi,
-    startOffset: 3,
-    endContainer: jkl,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc<ul><li>def</li><li>ghi[</li></ul>]jkl</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let def = editor.querySelector("li").firstChild;
-  let jkl = editor.querySelector("ul").nextSibling;
-  selection.setBaseAndExtent(def, 3, jkl, 0);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<div>abc<ul><li>defjkl</li></ul></div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: def,
-    startOffset: 3,
-    endContainer: jkl,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<div>abc<ul><li>def[</li><li>ghi</li></ul>]jkl</div>"');
-
 // Backspace in empty paragraph should remove the empty paragraph.  In this
 // case, it should be treated as joining with the previous paragraph.
 // The target range should include the invisible <br> element in the empty
 // paragraph.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p><p><br></p>";
-  let p1 = editor.querySelector("p");
+  initializeTest("<p>abc</p><p><br></p>");
+  let p1 = gEditor.querySelector("p");
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
-  selection.collapse(p2, 0);
+  gSelection.collapse(p2, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abc</p>");
+  assert_equals(gEditor.innerHTML, "<p>abc</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1649,47 +994,17 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Backspace at "<p>abc</p><p>{}<br></p>"');
 
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p><p><br></p>";
-  let p1 = editor.querySelector("p");
-  let abc = p1.firstChild;
-  let p2 = p1.nextSibling;
-  selection.setBaseAndExtent(abc, 3, p2, 0);
-  await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<p>abc</p>",
-                                     "<p>abc<br></p>"]);
-  if (editor.innerHTML === "<p>abc</p>") {
-    // Include the invisible `<br>` element if it's deleted.
-    checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-      startContainer: abc,
-      startOffset: 3,
-      endContainer: p2,
-      endOffset: 1,
-    });
-  } else {
-    checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-      startContainer: abc,
-      startOffset: 3,
-      endContainer: p2,
-      endOffset: 0,
-    });
-  }
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<p>abc[</p><p>}<br></p>"');
-
 // Delete ignore the empty span and the other things must be same as the
 // previous test.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p><p><span></span><br></p>";
-  let p1 = editor.querySelector("p");
+  initializeTest("<p>abc</p><p><span></span><br></p>");
+  let p1 = gEditor.querySelector("p");
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let span = p2.firstChild;
-  selection.collapse(span, 0);
+  gSelection.collapse(span, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abc</p>");
+  assert_equals(gEditor.innerHTML, "<p>abc</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1702,15 +1017,14 @@
 // If invisible white-spaces are removed with same action as above tests,
 // the range should be included in the target ranges.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc  </p><p><br></p>";
-  let p1 = editor.querySelector("p");
+  initializeTest("<p>abc  </p><p><br></p>");
+  let p1 = gEditor.querySelector("p");
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
-  selection.collapse(p2, 0);
+  gSelection.collapse(p2, 0);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<p>abc  </p>",
-                                     "<p>abc</p>"]);
+  assert_in_array(gEditor.innerHTML, ["<p>abc  </p>",
+                                      "<p>abc</p>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: abc.length,
@@ -1723,14 +1037,13 @@
 // If the previous block ends with non-editable content, target range
 // should be after the non-editable content node.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<span contenteditable=\"false\">def</span></p><p><br></p>";
-  let p1 = editor.querySelector("p");
-  let span = editor.querySelector("span");
+  initializeTest("<p>abc<span contenteditable=\"false\">def</span></p><p><br></p>");
+  let p1 = gEditor.querySelector("p");
+  let span = gEditor.querySelector("span");
   let p2 = p1.nextSibling;
-  selection.collapse(p2, 0);
+  gSelection.collapse(p2, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abc<span contenteditable=\"false\">def</span></p>");
+  assert_equals(gEditor.innerHTML, "<p>abc<span contenteditable=\"false\">def</span></p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p1,
     startOffset: 2,
@@ -1744,18 +1057,17 @@
 // with end of the text node in the first paragraph.  Otherwise, start from
 // after the non-editable paragraph.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p><p contenteditable=\"false\">def</p><p><br></p>";
-  let p1 = editor.querySelector("p");
+  initializeTest("<p>abc</p><p contenteditable=\"false\">def</p><p><br></p>");
+  let p1 = gEditor.querySelector("p");
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let p3 = p2.nextSibling;
-  selection.collapse(p3, 0);
+  gSelection.collapse(p3, 0);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<p>abc</p>",
-                                     "<p>abc</p><p contenteditable=\"false\">def</p>"]);
+  assert_in_array(gEditor.innerHTML, ["<p>abc</p>",
+                                      "<p>abc</p><p contenteditable=\"false\">def</p>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: p2.isConnected ? editor : abc,
+    startContainer: p2.isConnected ? gEditor : abc,
     startOffset: p2.isConnected ? 2 : abc.length,
     endContainer: p3,
     endOffset: 1,
@@ -1764,16 +1076,15 @@
 }, 'Backspace at "<p>abc</p><p contenteditable=\"false\">def</p><p>{}<br></p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<span contenteditable=\"false\">def</span>ghi</p>";
-  let p = editor.querySelector("p");
+  initializeTest("<p>abc<span contenteditable=\"false\">def</span>ghi</p>");
+  let p = gEditor.querySelector("p");
   let ghi = p.lastChild;
-  selection.collapse(ghi, 0);
+  gSelection.collapse(ghi, 0);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<p>abc<span contenteditable=\"false\">def</span>ghi</p>",
-                                     "<p>abcghi</p>",
-                                     "<p>abcghi<br></p>"]);
-  if (editor.innerHTML === "<p>abc<span contenteditable=\"false\">def</span>ghi</p>") {
+  assert_in_array(gEditor.innerHTML, ["<p>abc<span contenteditable=\"false\">def</span>ghi</p>",
+                                      "<p>abcghi</p>",
+                                      "<p>abcghi<br></p>"]);
+  if (gEditor.innerHTML === "<p>abc<span contenteditable=\"false\">def</span>ghi</p>") {
     checkGetTargetRangesOfBeforeinputOnDeleteSomething({
       startContainer: ghi,
       startOffset: 0,
@@ -1794,42 +1105,16 @@
   }
 }, 'Backspace at "<p>abc<span contenteditable=\"false\">def</span>[]ghi</p>"');
 
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<span contenteditable=\"false\">def</span>ghi</p>";
-  let p = editor.querySelector("p");
-  let abc = p.firstChild;
-  let ghi = p.lastChild;
-  selection.setBaseAndExtent(abc, 3, ghi, 0);
-  await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<p>abc<span contenteditable=\"false\">def</span>ghi</p>",
-                                     "<p>abcghi</p>",
-                                     "<p>abcghi<br></p>"]);
-  // Don't need to shrink the range for avoiding to waste runtime cost.
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: ghi,
-    endOffset: 0,
-  });
-  if (editor.innerHTML === "<p>abc<span contenteditable=\"false\">def</span>ghi</p>") {
-    checkGetTargetRangesOfInputOnDoNothing();
-  } else {
-    checkGetTargetRangesOfInputOnDeleteSomething();
-  }
-}, 'Backspace at "<p>abc[<span contenteditable=\"false\">def</span>]ghi</p>"');
-
 // If just removes the paragraph, target range should start from after the
 // table element.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<table><tr><td>cell</td></tr></table><p><br></p>";
-  let table = editor.querySelector("table");
+  initializeTest("<table><tr><td>cell</td></tr></table><p><br></p>");
+  let table = gEditor.querySelector("table");
   let p = table.nextSibling;
-  selection.collapse(p, 0);
+  gSelection.collapse(p, 0);
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<table><tbody><tr><td>cell</td></tr></tbody></table>",
-                                     "<table><tbody><tr><td>cell</td></tr></tbody></table><p><br></p>"]);
+  assert_in_array(gEditor.innerHTML, ["<table><tbody><tr><td>cell</td></tr></tbody></table>",
+                                      "<table><tbody><tr><td>cell</td></tr></tbody></table><p><br></p>"]);
   if (p.isConnected) {
     checkGetTargetRangesOfBeforeinputOnDeleteSomething({
       startContainer: p,
@@ -1840,7 +1125,7 @@
     checkGetTargetRangesOfInputOnDoNothing();
   } else {
     checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-      startContainer: editor,
+      startContainer: gEditor,
       startOffset: 1,
       endContainer: p,
       endOffset: 1,
@@ -1852,13 +1137,12 @@
 // If table cell won't be joined, target range should be collapsed in the
 // cell.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<table><tr><td>cell1</td><td><br></td></tr></table>";
-  let cell1 = editor.querySelector("td");
+  initializeTest("<table><tr><td>cell1</td><td><br></td></tr></table>");
+  let cell1 = gEditor.querySelector("td");
   let cell2 = cell1.nextSibling;
-  selection.collapse(cell2, 0);
+  gSelection.collapse(cell2, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<table><tbody><tr><td>cell1</td><td><br></td></tr></tbody></table>");
+  assert_equals(gEditor.innerHTML, "<table><tbody><tr><td>cell1</td><td><br></td></tr></tbody></table>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: cell2,
     startOffset: 0,
@@ -1868,71 +1152,14 @@
   checkGetTargetRangesOfInputOnDoNothing();
 }, 'Backspace at "<table><tr><td>cell1</td><td>{}<br></td></tr></table>"');
 
-// The table structure shouldn't be modified when deleting cell contents,
-// in this case, getTargetRanges() should return multiple ranges in each
-// cell?
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<table><tr><td>abc</td><td>def</td></tr></table>";
-  let abc = editor.querySelector("td").firstChild;
-  let def = editor.querySelector("td + td").firstChild;
-  selection.setBaseAndExtent(abc, 2, def, 1);
-  await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<table><tbody><tr><td>ab</td><td>ef</td></tr></tbody></table>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 2,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<table><tr><td>ab[c</td><td>d]ef</td></tr></table>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<table><tr><td>abc</td><td>def</td></tr><tr><td>ghi</td><td>jkl</td></tr></table>";
-  let abc = editor.querySelector("td").firstChild;
-  let jkl = editor.querySelector("tr + tr > td + td").firstChild;
-  selection.setBaseAndExtent(abc, 2, jkl, 1);
-  await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<table><tbody><tr><td>ab</td><td></td></tr><tr><td></td><td>kl</td></tr></tbody></table>",
-                                     "<table><tbody><tr><td>ab</td><td><br></td></tr><tr><td><br></td><td>kl</td></tr></tbody></table>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 2,
-    endContainer: jkl,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<table><tr><td>ab[c</td><td>def</td></tr><tr><td>ghi</td><td>j]kl</td></tr></table>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<table><tr><td>abc</td><td>def</td></tr></table><table><tr><td>ghi</td><td>jkl</td></tr></table>";
-  let abc = editor.querySelector("td").firstChild;
-  let jkl = editor.querySelector("table + table td + td").firstChild;
-  selection.setBaseAndExtent(abc, 2, jkl, 1);
-  await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<table><tbody><tr><td>ab</td><td></td></tr></tbody></table><table><tbody><tr><td></td><td>kl</td></tr></tbody></table>",
-                                     "<table><tbody><tr><td>ab</td><td><br></td></tr></tbody></table><table><tbody><tr><td><br></td><td>kl</td></tr></tbody></table>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 2,
-    endContainer: jkl,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Backspace at "<table><tr><td>ab[c</td><td>def</td></tr></table><table><tr><td>ghi</td><td>j]kl</td></tr></table>"');
-
 // If table caption won't be deleted, target range should be collapsed in the
 // caption element.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p><table><caption><br></caption><tr><td>cell</td></tr></table>";
-  let caption = editor.querySelector("caption");
-  selection.collapse(caption, 0);
+  initializeTest("<p>abc</p><table><caption><br></caption><tr><td>cell</td></tr></table>");
+  let caption = gEditor.querySelector("caption");
+  gSelection.collapse(caption, 0);
   await sendBackspaceKey();
-  assert_equals(editor.innerHTML, "<p>abc</p><table><caption><br></caption><tbody><tr><td>cell</td></tr></tbody></table>");
+  assert_equals(gEditor.innerHTML, "<p>abc</p><table><caption><br></caption><tbody><tr><td>cell</td></tr></tbody></table>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: caption,
     startOffset: 0,
@@ -1945,17 +1172,16 @@
 // If caret is not adjacent of deleting character, browser may not delete the
 // character, but update the caret position for next deletion.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>&#x5E9;&#x5DC;&#x5D5;&#x5DD;hello</p>";
-  let text1 = editor.querySelector("p").firstChild;
+  initializeTest("<p>&#x5E9;&#x5DC;&#x5D5;&#x5DD;hello</p>");
+  let text1 = gEditor.querySelector("p").firstChild;
   let text2 = text1.nextSibling;
-  selection.collapse(text2 ? text2 : text1, text2 ? 1 : 5);
+  gSelection.collapse(text2 ? text2 : text1, text2 ? 1 : 5);
   await sendArrowLeftKey();
   await sendBackspaceKey();
-  assert_in_array(editor.innerHTML, ["<p>\u05E9\u05DC\u05D5\u05DDhello</p>",
-                                     "<p>\u05DC\u05D5\u05DDhello</p>",
-                                     "<p>\u05E9\u05DC\u05D5hello</p>"]);
-  if (editor.innerHTML === "<p>\u05E9\u05DC\u05D5\u05DDhello</p>") {
+  assert_in_array(gEditor.innerHTML, ["<p>\u05E9\u05DC\u05D5\u05DDhello</p>",
+                                      "<p>\u05DC\u05D5\u05DDhello</p>",
+                                      "<p>\u05E9\u05DC\u05D5hello</p>"]);
+  if (gEditor.innerHTML === "<p>\u05E9\u05DC\u05D5\u05DDhello</p>") {
     checkGetTargetRangesOfBeforeinputOnDeleteSomething({
       startContainer: text2 ? text2 : text1,
       startOffset: text2 ? 0 : 4,
@@ -1963,7 +1189,7 @@
       endOffset: text2 ? 0 : 4,
     });
     checkGetTargetRangesOfInputOnDoNothing();
-  } else if (editor.innerHTML === "<p>\u05DC\u05D5\u05DDhello</p>") {
+  } else if (gEditor.innerHTML === "<p>\u05DC\u05D5\u05DDhello</p>") {
     checkGetTargetRangesOfBeforeinputOnDeleteSomething({
       startContainer: text1,
       startOffset: 0,
@@ -1999,15 +1225,14 @@
 }
 
 promise_test(async () => {
-  reset();
   const kText = "abc def ghi";
-  editor.innerHTML = `<p>${kText}</p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc def".length);
+  initializeTest(`<p>${kText}</p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc def".length);
   await sendBackspaceKey(kShift);
   let startOffset = getFirstDifferentOffset(p.firstChild.data, kText);
   let length = kText.length - p.firstChild.data.length;
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
@@ -2023,15 +1248,14 @@
 }, 'Shift + Backspace at "<p>abc def[] ghi</p>"');
 
 promise_test(async () => {
-  reset();
   const kText = "abc def ghi";
-  editor.innerHTML = `<p>${kText}</p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc def".length);
+  initializeTest(`<p>${kText}</p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc def".length);
   await sendBackspaceKey(kControl);
   let startOffset = getFirstDifferentOffset(p.firstChild.data, kText);
   let length = kText.length - p.firstChild.data.length;
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
@@ -2047,15 +1271,14 @@
 }, 'Control + Backspace at "<p>abc def[] ghi</p>"');
 
 promise_test(async () => {
-  reset();
   const kText = "abc def ghi";
-  editor.innerHTML = `<p>${kText}</p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc def".length);
+  initializeTest(`<p>${kText}</p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc def".length);
   await sendBackspaceKey(kAlt);
   let startOffset = getFirstDifferentOffset(p.firstChild.data, kText);
   let length = kText.length - p.firstChild.data.length;
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
@@ -2071,15 +1294,14 @@
 }, 'Alt + Backspace at "<p>abc def[] ghi</p>"');
 
 promise_test(async () => {
-  reset();
   const kText = "abc def ghi";
-  editor.innerHTML = `<p>${kText}</p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc def".length);
+  initializeTest(`<p>${kText}</p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc def".length);
   await sendBackspaceKey(kMeta);
   let startOffset = getFirstDifferentOffset(p.firstChild.data, kText);
   let length = kText.length - p.firstChild.data.length;
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
@@ -2095,18 +1317,17 @@
 }, 'Meta + Backspace at "<p>abc def[] ghi</p>"');
 
 promise_test(async () => {
-  reset();
   const kText = "abc def";
-  editor.innerHTML = `<p>   ${kText}</p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc".length);
+  initializeTest(`<p>   ${kText}</p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc".length);
   await sendBackspaceKey(kShift);
   let visibleText = p.firstChild.data.replace(/^\s+/, "");
   let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length);
   let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText);
   let length = kText.length + 3 - p.firstChild.data.length;
   // If invisible white-spaces are deleted, they should be contained in the target range.
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
@@ -2122,18 +1343,17 @@
 }, 'Shift + Backspace at "<p>   abc[] def</p>"');
 
 promise_test(async () => {
-  reset();
   const kText = "abc def";
-  editor.innerHTML = `<p>   ${kText}</p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc".length);
+  initializeTest(`<p>   ${kText}</p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc".length);
   await sendBackspaceKey(kControl);
   let visibleText = p.firstChild.data.replace(/^\s+/, "");
   let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length);
   let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText);
   let length = kText.length + 3 - p.firstChild.data.length;
   // If invisible white-spaces are deleted, they should be contained in the target range.
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
@@ -2149,18 +1369,17 @@
 }, 'Control + Backspace at "<p>   abc[] def</p>"');
 
 promise_test(async () => {
-  reset();
   const kText = "abc def";
-  editor.innerHTML = `<p>   ${kText}</p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc".length);
+  initializeTest(`<p>   ${kText}</p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc".length);
   await sendBackspaceKey(kAlt);
   let visibleText = p.firstChild.data.replace(/^\s+/, "");
   let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length);
   let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText);
   let length = kText.length + 3 - p.firstChild.data.length;
   // If invisible white-spaces are deleted, they should be contained in the target range.
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
@@ -2176,18 +1395,17 @@
 }, 'Alt + Backspace at "<p>   abc[] def</p>"');
 
 promise_test(async () => {
-  reset();
   const kText = "abc def";
-  editor.innerHTML = `<p>   ${kText}</p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc".length);
+  initializeTest(`<p>   ${kText}</p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc".length);
   await sendBackspaceKey(kMeta);
   let visibleText = p.firstChild.data.replace(/^\s+/, "");
   let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length);
   let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText);
   let length = kText.length + 3 - p.firstChild.data.length;
   // If invisible white-spaces are deleted, they should be contained in the target range.
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
diff --git a/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges-forwarddelete.tentative.html b/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges-forwarddelete.tentative.html
index 03518d5..62a33c094 100644
--- a/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges-forwarddelete.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges-forwarddelete.tentative.html
@@ -2,178 +2,26 @@
 <meta charset="utf-8">
 <meta name="timeout" content="long">
 <title>InputEvent.getTargetRanges() at Delete (forward delete)</title>
+<div contenteditable></div>
+<script src="input-events-get-target-ranges.js"></script>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/testdriver.js"></script>
 <script src="/resources/testdriver-vendor.js"></script>
 <script src="/resources/testdriver-actions.js"></script>
-<div contenteditable></div>
 <script>
-const kDeleteKey =  "\uE017";
-const kArrowRight = "\uE014";
-const kShift =      "\uE008";
-const kMeta =       "\uE03d";
-const kControl =    "\uE009";
-const kAlt =        "\uE00A";
-
-const kImgSrc = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==";
-
-let selection = getSelection();
-let editor = document.querySelector("div[contenteditable]");
-let beforeinput = [];
-let input = [];
-editor.addEventListener("beforeinput", (e) => {
-  // NOTE: Blink makes `getTargetRanges()` return empty range after propagation,
-  //       but this test wants to check the result during propagation.
-  //       Therefore, we need to cache the result, but will assert if
-  //       `getTargetRanges()` returns different ranges after checking the
-  //       cached ranges.
-  e.cachedRanges = e.getTargetRanges();
-  beforeinput.push(e);
-});
-editor.addEventListener("input", (e) => {
-  e.cachedRanges = e.getTargetRanges();
-  input.push(e);
-});
-
-function reset() {
-  editor.focus();
-  beforeinput = [];
-  input = [];
-}
-
-function getRangeDescription(range) {
-  function getNodeDescription(node) {
-    if (!node) {
-      return "null";
-    }
-    switch (node.nodeType) {
-      case Node.TEXT_NODE:
-      case Node.COMMENT_NODE:
-      case Node.CDATA_SECTION_NODE:
-        return `${node.nodeName} "${node.data}"`;
-      case Node.ELEMENT_NODE:
-        return `<${node.nodeName.toLowerCase()}>`;
-      default:
-        return `${node.nodeName}`;
-    }
-  }
-  if (range === null) {
-    return "null";
-  }
-  if (range === undefined) {
-    return "undefined";
-  }
-  return range.startContainer == range.endContainer && range.startOffset == range.endOffset
-      ? `(${getNodeDescription(range.startContainer)}, ${range.startOffset})`
-      : `(${getNodeDescription(range.startContainer)}, ${range.startOffset}) - (${getNodeDescription(range.endContainer)}, ${range.endOffset})`;
-}
-
-function getArrayOfRangesDescription(arrayOfRanges) {
-  if (arrayOfRanges === null) {
-    return "null";
-  }
-  if (arrayOfRanges === undefined) {
-    return "undefined";
-  }
-  if (!Array.isArray(arrayOfRanges)) {
-    return "Unknown Object";
-  }
-  if (arrayOfRanges.length === 0) {
-    return "[]";
-  }
-  let result = "[";
-  for (let range of arrayOfRanges) {
-    result += `{${getRangeDescription(range)}},`;
-  }
-  result += "]";
-  return result;
-}
-
-function sendDeleteKey(modifier) {
-  if (!modifier) {
-    return new test_driver.Actions()
-        .keyDown(kDeleteKey)
-        .keyUp(kDeleteKey)
-        .send();
-  }
-  return new test_driver.Actions()
-      .keyDown(modifier)
-      .keyDown(kDeleteKey)
-      .keyUp(kDeleteKey)
-      .keyUp(modifier)
-      .send();
-}
-
-function sendArrowRightKey() {
-  return new test_driver.Actions()
-      .keyDown(kArrowRight)
-      .keyUp(kArrowRight)
-      .send();
-}
-
-function checkGetTargetRangesKeepReturningSameValue(event) {
-  // https://github.com/w3c/input-events/issues/114
-  assert_equals(getArrayOfRangesDescription(event.getTargetRanges()),
-      getArrayOfRangesDescription(event.cachedRanges),
-      `${event.type}.getTargetRanges() should keep returning the same array of ranges even after its propagation finished`);
-}
-
-function checkGetTargetRangesOfBeforeinputOnDeleteSomething(expectedRange) {
-  assert_equals(beforeinput.length, 1,
-      "One beforeinput event should be fired if the key operation deletes something");
-  assert_true(Array.isArray(beforeinput[0].cachedRanges),
-      "beforeinput[0].getTargetRanges() should return an array of StaticRange instances during propagation");
-  // Before checking the length of array of ranges, we should check the first
-  // range first because the first range data is more important than whether
-  // there are additional unexpected ranges.
-  if (beforeinput[0].cachedRanges.length > 0) {
-    assert_equals(
-        getRangeDescription(beforeinput[0].cachedRanges[0]),
-        getRangeDescription(expectedRange),
-        `beforeinput.getTargetRanges() should return expected range (inputType is "${beforeinput[0].inputType}")`);
-    assert_equals(beforeinput[0].cachedRanges.length, 1,
-        "beforeinput.getTargetRanges() should return one range within an array");
-  }
-  assert_equals(beforeinput[0].cachedRanges.length, 1,
-      "One range should be returned from getTargetRanges() when the key operation deletes something");
-  checkGetTargetRangesKeepReturningSameValue(beforeinput[0]);
-}
-
-function checkGetTargetRangesOfInputOnDeleteSomething() {
-  assert_equals(input.length, 1,
-      "One input event should be fired if the key operation deletes something");
-  // https://github.com/w3c/input-events/issues/113
-  assert_true(Array.isArray(input[0].cachedRanges),
-      "input[0].getTargetRanges() should return an array of StaticRange instances during propagation");
-  assert_equals(input[0].cachedRanges.length, 0,
-      "input[0].getTargetRanges() should return empty array during propagation");
-  checkGetTargetRangesKeepReturningSameValue(input[0]);
-}
-
-function checkGetTargetRangesOfInputOnDoNothing() {
-  assert_equals(input.length, 0,
-      "input event shouldn't be fired when the key operation does not cause modifying the DOM tree");
-}
-
-function checkBeforeinputAndInputEventsOnNOOP() {
-  assert_equals(beforeinput.length, 0,
-      "beforeinput event shouldn't be fired when the key operation does not cause modifying the DOM tree");
-  assert_equals(input.length, 0,
-      "input event shouldn't be fired when the key operation does not cause modifying the DOM tree");
-}
+"use strict";
 
 // Simply deletes the next ASCII character of caret position.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p>";
-  selection.collapse(editor.firstChild.firstChild, 2);
+  initializeTest("<p>abc</p>");
+  gSelection.collapse(gEditor.firstChild.firstChild, 2);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>ab</p>");
+  assert_equals(gEditor.innerHTML, "<p>ab</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: editor.firstChild.firstChild,
+    startContainer: gEditor.firstChild.firstChild,
     startOffset: 2,
-    endContainer: editor.firstChild.firstChild,
+    endContainer: gEditor.firstChild.firstChild,
     endOffset: 3,
   });
   checkGetTargetRangesOfInputOnDeleteSomething();
@@ -181,15 +29,14 @@
 
 // Simply deletes the next ASCII character of caret position.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p>";
-  selection.collapse(editor.firstChild.firstChild, 1);
+  initializeTest("<p>abc</p>");
+  gSelection.collapse(gEditor.firstChild.firstChild, 1);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>ac</p>");
+  assert_equals(gEditor.innerHTML, "<p>ac</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: editor.firstChild.firstChild,
+    startContainer: gEditor.firstChild.firstChild,
     startOffset: 1,
-    endContainer: editor.firstChild.firstChild,
+    endContainer: gEditor.firstChild.firstChild,
     endOffset: 2,
   });
   checkGetTargetRangesOfInputOnDeleteSomething();
@@ -197,27 +44,25 @@
 
 // Simply deletes the next ASCII character of caret position.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p>";
-  selection.collapse(editor.firstChild.firstChild, 0);
+  initializeTest("<p>abc</p>");
+  gSelection.collapse(gEditor.firstChild.firstChild, 0);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>bc</p>");
+  assert_equals(gEditor.innerHTML, "<p>bc</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: editor.firstChild.firstChild,
+    startContainer: gEditor.firstChild.firstChild,
     startOffset: 0,
-    endContainer: editor.firstChild.firstChild,
+    endContainer: gEditor.firstChild.firstChild,
     endOffset: 1,
   });
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Delete at "<p>[]abc</p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p>";
-  let abc = editor.querySelector("p").firstChild;
-  selection.collapse(abc, 3);
+  initializeTest("<p>abc</p>");
+  let abc = gEditor.querySelector("p").firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abc</p>");
+  assert_equals(gEditor.innerHTML, "<p>abc</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -228,12 +73,11 @@
 }, 'Delete at "<p>abc[]</p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<br></p>";
-  let abc = editor.querySelector("p").firstChild;
-  selection.collapse(abc, 3);
+  initializeTest("<p>abc<br></p>");
+  let abc = gEditor.querySelector("p").firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abc<br></p>");
+  assert_equals(gEditor.innerHTML, "<p>abc<br></p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -244,12 +88,11 @@
 }, 'Delete at "<p>abc[]<br></p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<p><img src="${kImgSrc}"><br></p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p, 1);
+  initializeTest(`<p><img src="${kImgSrc}"><br></p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p, 1);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, `<p><img src="${kImgSrc}"><br></p>`);
+  assert_equals(gEditor.innerHTML, `<p><img src="${kImgSrc}"><br></p>`);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -264,16 +107,15 @@
 // contained by a range of `getTargetRanges()`.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>a<span>b</span>c</p>";
-  let a = editor.querySelector("span").previousSibling;
-  selection.collapse(a, 1);
+  initializeTest("<p>a<span>b</span>c</p>");
+  let a = gEditor.querySelector("span").previousSibling;
+  gSelection.collapse(a, 1);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>ac</p>");
+  assert_equals(gEditor.innerHTML, "<p>ac</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: a,
     startOffset: 1,
-    endContainer: editor.firstChild,
+    endContainer: gEditor.firstChild,
     endOffset: 2,
   });
   checkGetTargetRangesOfInputOnDeleteSomething();
@@ -284,16 +126,15 @@
 // contained by a range of `getTargetRanges()`.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>a<span>b</span>c</p>";
-  let b = editor.querySelector("span").firstChild;
-  selection.collapse(b, 0);
+  initializeTest("<p>a<span>b</span>c</p>");
+  let b = gEditor.querySelector("span").firstChild;
+  gSelection.collapse(b, 0);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>ac</p>");
+  assert_equals(gEditor.innerHTML, "<p>ac</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: editor.firstChild,
+    startContainer: gEditor.firstChild,
     startOffset: 1,
-    endContainer: editor.firstChild,
+    endContainer: gEditor.firstChild,
     endOffset: 2,
   });
   checkGetTargetRangesOfInputOnDeleteSomething();
@@ -304,30 +145,29 @@
 // the range of `getTargetRanges()`, but needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc </p>";
-  selection.collapse(editor.firstChild.firstChild, 2);
+  initializeTest("<p>abc </p>");
+  gSelection.collapse(gEditor.firstChild.firstChild, 2);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<p>ab</p>", "<p>ab </p>"]);
+  assert_in_array(gEditor.innerHTML, ["<p>ab</p>",
+                                      "<p>ab </p>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: editor.firstChild.firstChild,
+    startContainer: gEditor.firstChild.firstChild,
     startOffset: 2,
-    endContainer: editor.firstChild.firstChild,
-    endOffset: editor.firstChild.firstChild.data.length == 2 ? 4 : 3,
+    endContainer: gEditor.firstChild.firstChild,
+    endOffset: gEditor.firstChild.firstChild.data.length == 2 ? 4 : 3,
   });
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Delete at "<p>ab[]c </p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p><p>def</p>";
-  let p1 = editor.firstChild;
+  initializeTest("<p>abc</p><p>def</p>");
+  let p1 = gEditor.firstChild;
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -343,15 +183,14 @@
 // contained by the range of `getTargetRanges()`, but needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc   </p><p>   def</p>";
-  let p1 = editor.firstChild;
+  initializeTest("<p>abc   </p><p>   def</p>");
+  let p1 = gEditor.firstChild;
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -367,15 +206,14 @@
 // contained by the range of `getTargetRanges()`, but needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc   </p><p>   def</p>";
-  let p1 = editor.firstChild;
+  initializeTest("<p>abc   </p><p>   def</p>");
+  let p1 = gEditor.firstChild;
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(abc, 4);
+  gSelection.collapse(abc, 4);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -391,15 +229,14 @@
 // contained by the range of `getTargetRanges()`, but needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc   </p><p>   def</p>";
-  let p1 = editor.firstChild;
+  initializeTest("<p>abc   </p><p>   def</p>");
+  let p1 = gEditor.firstChild;
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(abc, 5);
+  gSelection.collapse(abc, 5);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -415,15 +252,14 @@
 // contained by the range of `getTargetRanges()`, but needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc   </p><p>   def</p>";
-  let p1 = editor.firstChild;
+  initializeTest("<p>abc   </p><p>   def</p>");
+  let p1 = gEditor.firstChild;
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(abc, 6);
+  gSelection.collapse(abc, 6);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -433,37 +269,13 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Delete at "<p>abc   []</p><p>   def</p>"');
 
-// Invisible trailing white-spaces in current block and invisible leading
-// white-spaces in the following block should be deleted for avoiding they
-// becoming visible when the blocks are joined.  Perhaps, they should be
-// contained by the range of `getTargetRanges()`, but needs discussion.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc   </p><p>   def</p>";
-  let p1 = editor.firstChild;
-  let abc = p1.firstChild;
-  let p2 = p1.nextSibling;
-  let def = p2.firstChild;
-  selection.setBaseAndExtent(abc, 6, def, 0);
+  initializeTest("<p>abc</p><p><b>def</b></p>");
+  let abc = gEditor.querySelector("p").firstChild;
+  let def = gEditor.querySelector("b").firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 3,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<p>abc   [</p><p>]   def</p>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p><p><b>def</b></p>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("b").firstChild;
-  selection.collapse(abc, 3);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abc<b>def</b></p>");
+  assert_equals(gEditor.innerHTML, "<p>abc<b>def</b></p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -474,14 +286,13 @@
 }, 'Delete at "<p>abc[]</p><p><b>def</b></p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p><b>abc</b></p><p><b>def</b></p>";
-  let abc = editor.querySelector("p > b").firstChild;
-  let def = editor.querySelector("P + p > b").firstChild;
-  selection.collapse(abc, 3);
+  initializeTest("<p><b>abc</b></p><p><b>def</b></p>");
+  let abc = gEditor.querySelector("p > b").firstChild;
+  let def = gEditor.querySelector("P + p > b").firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<p><b>abc</b><b>def</b></p>",
-                                     "<p><b>abcdef</b></p>"]);
+  assert_in_array(gEditor.innerHTML, ["<p><b>abc</b><b>def</b></p>",
+                                      "<p><b>abcdef</b></p>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -492,13 +303,12 @@
 }, 'Delete at "<p><b>abc[]</b></p><p><b>def</b></p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p><i>abc</i></p><p><b>def</b></p>";
-  let abc = editor.querySelector("i").firstChild;
-  let def = editor.querySelector("b").firstChild;
-  selection.collapse(abc, 3);
+  initializeTest("<p><i>abc</i></p><p><b>def</b></p>");
+  let abc = gEditor.querySelector("i").firstChild;
+  let def = gEditor.querySelector("b").firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p><i>abc</i><b>def</b></p>");
+  assert_equals(gEditor.innerHTML, "<p><i>abc</i><b>def</b></p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -515,15 +325,14 @@
 // the range of `getTargetRanges()`, but needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<pre>abc   </pre><p>   def</p>";
-  let pre = editor.firstChild;
+  initializeTest("<pre>abc   </pre><p>   def</p>");
+  let pre = gEditor.firstChild;
   let abc = pre.firstChild;
   let p = pre.nextSibling;
   let def = p.firstChild;
-  selection.collapse(abc, 6);
+  gSelection.collapse(abc, 6);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<pre>abc   def</pre>");
+  assert_equals(gEditor.innerHTML, "<pre>abc   def</pre>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 6,
@@ -543,15 +352,14 @@
 // implementation cost and runtime cost.  Needs discuss.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<pre>abc   </pre><p>   def</p>";
-  let pre = editor.firstChild;
+  initializeTest("<pre>abc   </pre><p>   def</p>");
+  let pre = gEditor.firstChild;
   let abc = pre.firstChild;
   let p = pre.nextSibling;
   let def = p.firstChild;
-  selection.collapse(abc, 6);
+  gSelection.collapse(abc, 6);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<pre>abc   def</pre>");
+  assert_equals(gEditor.innerHTML, "<pre>abc   def</pre>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 6,
@@ -569,18 +377,17 @@
 // visible leading white-spaces.  But needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc   </p><pre>   def</pre>";
-  let p = editor.firstChild;
+  initializeTest("<p>abc   </p><pre>   def</pre>");
+  let p = gEditor.firstChild;
   let abc = p.firstChild;
   let pre = p.nextSibling;
   let def = pre.firstChild;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<p>abc &nbsp; def</p>",
-                                     "<p>abc&nbsp;&nbsp; def</p>",
-                                     "<p>abc&nbsp; &nbsp;def</p>",
-                                     "<p>abc &nbsp;&nbsp;def</p>"]);
+  assert_in_array(gEditor.innerHTML, ["<p>abc &nbsp; def</p>",
+                                      "<p>abc&nbsp;&nbsp; def</p>",
+                                      "<p>abc&nbsp; &nbsp;def</p>",
+                                      "<p>abc &nbsp;&nbsp;def</p>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -596,15 +403,14 @@
 // But maybe needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<br></p><p>def</p>";
-  let p1 = editor.firstChild;
+  initializeTest("<p>abc<br></p><p>def</p>");
+  let p1 = gEditor.firstChild;
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -615,15 +421,14 @@
 }, 'Delete at "<p>abc[]<br></p><p>def</p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<p><img src="${kImgSrc}"><br></p><p>def</p>`;
-  let p1 = editor.firstChild;
+  initializeTest(`<p><img src="${kImgSrc}"><br></p><p>def</p>`);
+  let p1 = gEditor.firstChild;
   let img = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(p1, 1);
+  gSelection.collapse(p1, 1);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, `<p><img src="${kImgSrc}">def</p>`);
+  assert_equals(gEditor.innerHTML, `<p><img src="${kImgSrc}">def</p>`);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p1,
     startOffset: 1,
@@ -633,106 +438,20 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, `Delete at "<p><img>{}<br></p><p>def</p>"`);
 
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc</p><p>def</p>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p + p").firstChild;
-  selection.setBaseAndExtent(abc, 2, def, 1);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abef</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 2,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<p>ab[c</p><p>d]ef</p>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc </p><p> def</p>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p + p").firstChild;
-  selection.setBaseAndExtent(abc, 2, def, 2);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abef</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 2,
-    endContainer: def,
-    endOffset: 2,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<p>ab[c </p><p> d]ef</p>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc </p><p> def</p>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p + p").firstChild;
-  selection.setBaseAndExtent(abc, 2, def, 0);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abdef</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 2,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<p>ab[c </p><p>] def</p>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc </p><p> def</p>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p + p").firstChild;
-  selection.setBaseAndExtent(abc, 4, def, 0);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<p>abc [</p><p>] def</p>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc </p><p> def</p>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p + p").firstChild;
-  selection.setBaseAndExtent(abc, 4, def, 1);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<p>abc [</p><p> ]def</p>"');
-
 // Deleting from last empty line in the first block should delete the
 // invisible `<br>` element for the last empty line and join the blocks.
 // In this case, the invisible `<br>` element should be contained in the
 // range of `getTargetRanges()`, but needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<br><br></p><p>def</p>";
-  let p1 = editor.firstChild;
+  initializeTest("<p>abc<br><br></p><p>def</p>");
+  let p1 = gEditor.firstChild;
   let abc = p1.firstChild;
   let p2 = p1.nextSibling;
   let def = p2.firstChild;
-  selection.collapse(p1, 2);
+  gSelection.collapse(p1, 2);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abc<br>def</p>");
+  assert_equals(gEditor.innerHTML, "<p>abc<br>def</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p1,
     startOffset: 2,
@@ -745,13 +464,12 @@
 // Deleting visible `<br>` element should be contained by a range of
 // `getTargetRanges()`.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<br>def</p>";
-  let p = editor.querySelector("p");
+  initializeTest("<p>abc<br>def</p>");
+  let p = gEditor.querySelector("p");
   let abc = p.firstChild;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -764,13 +482,12 @@
 // Deleting visible `<br>` element following white-space should not include
 // the preceding white-space in the range.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc <br>def</p>";
-  let p = editor.querySelector("p");
+  initializeTest("<p>abc <br>def</p>");
+  let p = gEditor.querySelector("p");
   let abc = p.firstChild;
-  selection.collapse(abc, 4);
+  gSelection.collapse(abc, 4);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abc def</p>");
+  assert_equals(gEditor.innerHTML, "<p>abc def</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -785,14 +502,13 @@
 // visible and should be deleted for avoiding it.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<br> def</p>";
-  let p = editor.querySelector("p");
+  initializeTest("<p>abc<br> def</p>");
+  let p = gEditor.querySelector("p");
   let abc = p.firstChild;
-  let def = editor.querySelector("br").nextSibling;
-  selection.collapse(abc, 3);
+  let def = gEditor.querySelector("br").nextSibling;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -802,33 +518,13 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Delete at "<p>abc[]<br> def</p>"');
 
-// Deleting visible `<br>` element should be contained by a range of
-// `getTargetRanges()`.  However, when only the `<br>` element is selected,
-// the range shouldn't start from nor end by surrounding text nodes?
-// https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<br>def</p>";
-  selection.setBaseAndExtent(editor.firstChild, 1, editor.firstChild, 2);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: editor.firstChild,
-    startOffset: 1,
-    endContainer: editor.firstChild,
-    endOffset: 2,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<p>abc{<br>}def</p>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = `<p>abc<img src="${kImgSrc}">def</p>`;
-  let p = editor.querySelector("p");
+  initializeTest(`<p>abc<img src="${kImgSrc}">def</p>`);
+  let p = gEditor.querySelector("p");
   let abc = p.firstChild;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abcdef</p>");
+  assert_equals(gEditor.innerHTML, "<p>abcdef</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -841,13 +537,12 @@
 // White-spaces around `<img>` element are visible so that they shouldn't
 // be included into the target ranges.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<p>abc <img src="${kImgSrc}">def</p>`;
-  let p = editor.querySelector("p");
+  initializeTest(`<p>abc <img src="${kImgSrc}">def</p>`);
+  let p = gEditor.querySelector("p");
   let abc = p.firstChild;
-  selection.collapse(abc, 4);
+  gSelection.collapse(abc, 4);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abc def</p>");
+  assert_equals(gEditor.innerHTML, "<p>abc def</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -860,13 +555,12 @@
 // White-spaces around `<img>` element are visible so that they shouldn't
 // be included into the target ranges.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<p>abc<img src="${kImgSrc}"> def</p>`;
-  let p = editor.querySelector("p");
+  initializeTest(`<p>abc<img src="${kImgSrc}"> def</p>`);
+  let p = gEditor.querySelector("p");
   let abc = p.firstChild;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abc def</p>");
+  assert_equals(gEditor.innerHTML, "<p>abc def</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -877,13 +571,12 @@
 }, 'Delete at "<p>abc[]<img> def</p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<p>abc<img src="${kImgSrc}"><img src="${kImgSrc}">def</p>`;
-  let p = editor.querySelector("p");
+  initializeTest(`<p>abc<img src="${kImgSrc}"><img src="${kImgSrc}">def</p>`);
+  let p = gEditor.querySelector("p");
   let abc = p.firstChild;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, `<p>abc<img src="${kImgSrc}">def</p>`);
+  assert_equals(gEditor.innerHTML, `<p>abc<img src="${kImgSrc}">def</p>`);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -894,12 +587,11 @@
 }, 'Delete at "<p>abc[]<img><img>def</p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<p>abc<img src="${kImgSrc}"><img src="${kImgSrc}">def</p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p, 2);
+  initializeTest(`<p>abc<img src="${kImgSrc}"><img src="${kImgSrc}">def</p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p, 2);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, `<p>abc<img src="${kImgSrc}">def</p>`);
+  assert_equals(gEditor.innerHTML, `<p>abc<img src="${kImgSrc}">def</p>`);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 2,
@@ -909,35 +601,13 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Delete at "<p>abc<img>{}<img>def</p>"');
 
-// Different from collapsed range around an atomic content, non-collapsed
-// range may not be shrunken to select only the atomic content for avoid
-// to waste runtime cost.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<p>abc<img src="${kImgSrc}">def</p>`;
-  let p = editor.querySelector("p");
-  let abc = p.firstChild;
-  let def = p.lastChild;
-  selection.setBaseAndExtent(abc, 3, def, 0);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, `<p>abcdef</p>`);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<p>abc[<img>]def</p>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc<hr>def</div>`;
-  let div = editor.querySelector("div");
+  initializeTest(`<div>abc<hr>def</div>`);
+  let div = gEditor.querySelector("div");
   let abc = div.firstChild;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdef</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: div,
     startOffset: 1,
@@ -952,13 +622,12 @@
 // visible.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc <hr>def</div>`;
-  let div = editor.querySelector("div");
+  initializeTest(`<div>abc <hr>def</div>`);
+  let div = gEditor.querySelector("div");
   let abc = div.firstChild;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdef</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: div,
     startOffset: 1,
@@ -973,13 +642,12 @@
 // visible.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc<hr> def</div>`;
-  let div = editor.querySelector("div");
+  initializeTest(`<div>abc<hr> def</div>`);
+  let div = gEditor.querySelector("div");
   let abc = div.firstChild;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdef</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: div,
     startOffset: 1,
@@ -992,13 +660,12 @@
 // Invisible `<br>` element immediately before `<hr>` element should be
 // delete once, and both of them should be included in the target range.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc<br><hr>def</div>`;
-  let div = editor.querySelector("div");
+  initializeTest(`<div>abc<br><hr>def</div>`);
+  let div = gEditor.querySelector("div");
   let abc = div.firstChild;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdef</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: div,
     startOffset: 1,
@@ -1009,13 +676,12 @@
 }, 'Delete at "<div>abc[]<br><hr>def</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div><img src="${kImgSrc}"><br><hr>def</div>`;
-  let div = editor.querySelector("div");
+  initializeTest(`<div><img src="${kImgSrc}"><br><hr>def</div>`);
+  let div = gEditor.querySelector("div");
   let img = div.firstChild;
-  selection.collapse(div, 1);
+  gSelection.collapse(div, 1);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, `<div><img src="${kImgSrc}">def</div>`);
+  assert_equals(gEditor.innerHTML, `<div><img src="${kImgSrc}">def</div>`);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: div,
     startOffset: 1,
@@ -1026,105 +692,14 @@
 }, 'Delete at "<div><img>{}<br><hr>def</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc<hr>def</div>`;
-  let div = editor.querySelector("div");
-  let abc = div.firstChild;
-  let def = div.lastChild;
-  selection.setBaseAndExtent(abc, 3, def, 0);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc[<hr>]def</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc <hr>def</div>`;
-  let div = editor.querySelector("div");
-  let abc = div.firstChild;
-  let def = div.lastChild;
-  selection.setBaseAndExtent(abc, 4, def, 0);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc [<hr>]def</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc <hr> def</div>`;
-  let div = editor.querySelector("div");
-  let abc = div.firstChild;
-  let def = div.lastChild;
-  selection.setBaseAndExtent(abc, 4, def, 0);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc [<hr>] def</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc <hr> def</div>`;
-  let div = editor.querySelector("div");
-  let abc = div.firstChild;
-  let def = div.lastChild;
-  selection.setBaseAndExtent(div, 1, div, 2);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc {<hr>} def</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div>abc<hr> def</div>`;
-  let div = editor.querySelector("div");
-  let abc = div.firstChild;
-  let def = div.lastChild;
-  selection.setBaseAndExtent(abc, 3, def, 0);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdef</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc[<hr>] def</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<p>def<br>ghi</p></div>";
-  let p = editor.querySelector("p");
+  initializeTest("<div>abc<p>def<br>ghi</p></div>");
+  let p = gEditor.querySelector("p");
   let def = p.firstChild;
-  let abc = editor.firstChild.firstChild;
-  selection.collapse(abc, 3);
+  let abc = gEditor.firstChild.firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
-                                     "<div>abcdef<br><p>ghi</p></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
+                                      "<div>abcdef<br><p>ghi</p></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1135,35 +710,15 @@
 }, 'Delete at "<div>abc[]<p>def<br>ghi</p></div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<p>def<br>ghi</p></div>";
-  let p = editor.querySelector("p");
-  let def = p.firstChild;
-  let abc = editor.firstChild.firstChild;
-  selection.setBaseAndExtent(abc, 3, def, 0);
-  await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
-                                     "<div>abcdef<br><p>ghi</p></div>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc[<p>]def<br>ghi</p></div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<br><p>def<br>ghi</p></div>";
-  let div = editor.firstChild;
-  let p = editor.querySelector("p");
+  initializeTest("<div>abc<br><p>def<br>ghi</p></div>");
+  let div = gEditor.firstChild;
+  let p = gEditor.querySelector("p");
   let def = p.firstChild;
   let abc = div.firstChild;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
-                                     "<div>abcdef<br><p>ghi</p></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
+                                      "<div>abcdef<br><p>ghi</p></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: div,
     startOffset: 1,
@@ -1174,16 +729,15 @@
 }, 'Delete at "<div>abc[]<br><p>def<br>ghi</p></div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div><img src="${kImgSrc}"><br><p>def<br>ghi</p></div>`;
-  let div = editor.firstChild;
-  let p = editor.querySelector("p");
+  initializeTest(`<div><img src="${kImgSrc}"><br><p>def<br>ghi</p></div>`);
+  let div = gEditor.firstChild;
+  let p = gEditor.querySelector("p");
   let def = p.firstChild;
   let abc = div.firstChild;
-  selection.collapse(div, 1);
+  gSelection.collapse(div, 1);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, [`<div><img src="${kImgSrc}">def<p>ghi</p></div>`,
-                                     `<div><img src="${kImgSrc}">def<br><p>ghi</p></div>`]);
+  assert_in_array(gEditor.innerHTML, [`<div><img src="${kImgSrc}">def<p>ghi</p></div>`,
+                                      `<div><img src="${kImgSrc}">def<br><p>ghi</p></div>`]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: div,
     startOffset: 1,
@@ -1199,15 +753,14 @@
 // `getTargetRanges()`, but maybe needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc   <p>   def<br>ghi</p></div>";
-  let p = editor.querySelector("p");
+  initializeTest("<div>abc   <p>   def<br>ghi</p></div>");
+  let p = gEditor.querySelector("p");
   let def = p.firstChild;
-  let abc = editor.firstChild.firstChild;
-  selection.collapse(abc, 3);
+  let abc = gEditor.firstChild.firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
-                                     "<div>abcdef<br><p>ghi</p></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
+                                      "<div>abcdef<br><p>ghi</p></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1218,32 +771,12 @@
 }, 'Delete at "<div>abc[]   <p>   def<br>ghi</p></div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc   <p>   def<br>ghi</p></div>";
-  let p = editor.querySelector("p");
-  let def = p.firstChild;
-  let abc = editor.firstChild.firstChild;
-  selection.setBaseAndExtent(abc, abc.length, def, 0);
+  initializeTest("<div>abc<p><b>def</b></p></div>");
+  let abc = gEditor.querySelector("div").firstChild;
+  let def = gEditor.querySelector("b").firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
-                                     "<div>abcdef<br><p>ghi</p></div>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 3,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc   [<p>]   def<br>ghi</p></div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<p><b>def</b></p></div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("b").firstChild;
-  selection.collapse(abc, 3);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abc<b>def</b></div>");
+  assert_equals(gEditor.innerHTML, "<div>abc<b>def</b></div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1254,14 +787,13 @@
 }, 'Delete at "<div>abc[]<p><b>def</b></p></div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><b>abc</b><p><b>def</b></p></div>";
-  let abc = editor.querySelector("b").firstChild;
-  let def = editor.querySelector("p > b").firstChild;
-  selection.collapse(abc, 3);
+  initializeTest("<div><b>abc</b><p><b>def</b></p></div>");
+  let abc = gEditor.querySelector("b").firstChild;
+  let def = gEditor.querySelector("p > b").firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div><b>abc</b><b>def</b></div>",
-                                     "<div><b>abcdef</b></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div><b>abc</b><b>def</b></div>",
+                                      "<div><b>abcdef</b></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1272,13 +804,12 @@
 }, 'Delete at "<div><b>abc[]</b><p><b>def</b></p></div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><i>abc</i><p><b>def</b></p></div>";
-  let abc = editor.querySelector("i").firstChild;
-  let def = editor.querySelector("b").firstChild;
-  selection.collapse(abc, 3);
+  initializeTest("<div><i>abc</i><p><b>def</b></p></div>");
+  let abc = gEditor.querySelector("i").firstChild;
+  let def = gEditor.querySelector("b").firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div><i>abc</i><b>def</b></div>");
+  assert_equals(gEditor.innerHTML, "<div><i>abc</i><b>def</b></div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1289,14 +820,13 @@
 }, 'Delete at "<div><i>abc[]</i><p><b>def</b></p></div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p>abc</p>def</div>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p").nextSibling;
-  selection.collapse(abc, 3);
+  initializeTest("<div><p>abc</p>def</div>");
+  let abc = gEditor.querySelector("p").firstChild;
+  let def = gEditor.querySelector("p").nextSibling;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div><p>abcdef</p></div>",
-                                     "<div><p>abcdef<br></p></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div><p>abcdef</p></div>",
+                                      "<div><p>abcdef<br></p></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1307,15 +837,14 @@
 }, 'Delete at "<div><p>abc[]</p>def</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p>abc<br></p>def</div>";
-  let p = editor.querySelector("p");
+  initializeTest("<div><p>abc<br></p>def</div>");
+  let p = gEditor.querySelector("p");
   let abc = p.firstChild;
   let def = p.nextSibling;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div><p>abcdef</p></div>",
-                                     "<div><p>abcdef<br></p></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div><p>abcdef</p></div>",
+                                      "<div><p>abcdef<br></p></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -1326,15 +855,14 @@
 }, 'Delete at "<div><p>abc[]<br></p>def</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = `<div><p><img src="${kImgSrc}"><br></p>def</div>`;
-  let p = editor.querySelector("p");
+  initializeTest(`<div><p><img src="${kImgSrc}"><br></p>def</div>`);
+  let p = gEditor.querySelector("p");
   let abc = p.firstChild;
   let def = p.nextSibling;
-  selection.collapse(p, 1);
+  gSelection.collapse(p, 1);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, [`<div><p><img src="${kImgSrc}">def</p></div>`,
-                                     `<div><p><img src="${kImgSrc}">def<br></p></div>`]);
+  assert_in_array(gEditor.innerHTML, [`<div><p><img src="${kImgSrc}">def</p></div>`,
+                                      `<div><p><img src="${kImgSrc}">def<br></p></div>`]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p,
     startOffset: 1,
@@ -1344,38 +872,19 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Delete at "<div><p><img>{}<br></p>def</div>"');
 
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p>abc</p>def</div>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p").nextSibling;
-  selection.setBaseAndExtent(abc, 3, def, 0);
-  await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div><p>abcdef</p></div>",
-                                     "<div><p>abcdef<br></p></div>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div><p>abc[</p>]def</div>"');
-
 // Joining child block and parent block should remove invisible trailing
 // white-spaces of the child block and invisible following white-spaces
 // in the parent block, and they should be contained by a range of
 // `getTargetRanges()`, but maybe needs discussion.
 // https://github.com/w3c/input-events/issues/112
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p>abc   </p>   def</div>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p").nextSibling;
-  selection.collapse(abc, 3);
+  initializeTest("<div><p>abc   </p>   def</div>");
+  let abc = gEditor.querySelector("p").firstChild;
+  let def = gEditor.querySelector("p").nextSibling;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div><p>abcdef</p></div>",
-                                     "<div><p>abcdef<br></p></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div><p>abcdef</p></div>",
+                                      "<div><p>abcdef<br></p></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1386,31 +895,12 @@
 }, 'Delete at "<div><p>abc[]   </p>   def</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p>abc   </p>   def</div>";
-  let abc = editor.querySelector("p").firstChild;
-  let def = editor.querySelector("p").nextSibling;
-  selection.setBaseAndExtent(abc, abc.length, def, 0);
+  initializeTest("<div><p><b>abc</b></p>def</div>");
+  let abc = gEditor.querySelector("b").firstChild;
+  let def = gEditor.querySelector("p").nextSibling;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div><p>abcdef</p></div>",
-                                     "<div><p>abcdef<br></p></div>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 3,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div><p>abc   [</p>]   def</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p><b>abc</b></p>def</div>";
-  let abc = editor.querySelector("b").firstChild;
-  let def = editor.querySelector("p").nextSibling;
-  selection.collapse(abc, 3);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div><p><b>abc</b>def</p></div>");
+  assert_equals(gEditor.innerHTML, "<div><p><b>abc</b>def</p></div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1421,14 +911,13 @@
 }, 'Delete at "<div><p><b>abc[]</b></p>def</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p><b>abc</b></p><b>def</b></div>";
-  let abc = editor.querySelector("b").firstChild;
-  let def = editor.querySelector("div > b").firstChild;
-  selection.collapse(abc, 3);
+  initializeTest("<div><p><b>abc</b></p><b>def</b></div>");
+  let abc = gEditor.querySelector("b").firstChild;
+  let def = gEditor.querySelector("div > b").firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div><p><b>abc</b><b>def</b></p></div>",
-                                     "<div><p><b>abcdef</b></p></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div><p><b>abc</b><b>def</b></p></div>",
+                                      "<div><p><b>abcdef</b></p></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1439,13 +928,12 @@
 }, 'Delete at "<div><p><b>abc[]</b></p><b>def</b></div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div><p><b>abc</b></p><i>def</i></div>";
-  let abc = editor.querySelector("b").firstChild;
-  let def = editor.querySelector("i").firstChild;
-  selection.collapse(abc, 3);
+  initializeTest("<div><p><b>abc</b></p><i>def</i></div>");
+  let abc = gEditor.querySelector("b").firstChild;
+  let def = gEditor.querySelector("i").firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div><p><b>abc</b><i>def</i></p></div>");
+  assert_equals(gEditor.innerHTML, "<div><p><b>abc</b><i>def</i></p></div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1456,13 +944,12 @@
 }, 'Delete at "<div><p><b>abc[]</b></p><i>def</i></div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li></ul>ghi</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  selection.collapse(abc, 3);
+  initializeTest("<div>abc<ul><li>def</li></ul>ghi</div>");
+  let abc = gEditor.querySelector("div").firstChild;
+  let def = gEditor.querySelector("li").firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdefghi</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1473,13 +960,12 @@
 }, 'Delete at "<div>abc[]<ul><li>def</li></ul>ghi</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc  <ul><li> def </li></ul>  ghi</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  selection.collapse(abc, 3);
+  initializeTest("<div>abc  <ul><li> def </li></ul>  ghi</div>");
+  let abc = gEditor.querySelector("div").firstChild;
+  let def = gEditor.querySelector("li").firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdefghi</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1490,13 +976,12 @@
 }, 'Delete at "<div>abc[]  <ul><li> def </li></ul>  ghi</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc  <ul><li> def </li></ul>  ghi</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  selection.collapse(abc, abc.length);
+  initializeTest("<div>abc  <ul><li> def </li></ul>  ghi</div>");
+  let abc = gEditor.querySelector("div").firstChild;
+  let def = gEditor.querySelector("li").firstChild;
+  gSelection.collapse(abc, abc.length);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdefghi</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1507,47 +992,12 @@
 }, 'Delete at "<div>abc  []<ul><li> def </li></ul>  ghi</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li></ul>ghi</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  selection.setBaseAndExtent(abc, 3, def, 0);
+  initializeTest("<div>abc<ul><li>def</li></ul>ghi</div>");
+  let def = gEditor.querySelector("li").firstChild;
+  let ghi = gEditor.querySelector("ul").nextSibling;
+  gSelection.collapse(def, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc[<ul><li>]def</li></ul>ghi</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc  <ul><li> def </li></ul>  ghi</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  selection.setBaseAndExtent(abc, abc.length, def, 0);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc  [<ul><li>] def </li></ul>  ghi</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li></ul>ghi</div>";
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("ul").nextSibling;
-  selection.collapse(def, 3);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abc<ul><li>defghi</li></ul></div>");
+  assert_equals(gEditor.innerHTML, "<div>abc<ul><li>defghi</li></ul></div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: def,
     startOffset: 3,
@@ -1558,15 +1008,14 @@
 }, 'Delete at "<div>abc<ul><li>def[]</li></ul>ghi</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc <ul><li>  def  </li></ul> ghi</div>";
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("ul").nextSibling;
-  selection.collapse(def, 5);
+  initializeTest("<div>abc <ul><li>  def  </li></ul> ghi</div>");
+  let def = gEditor.querySelector("li").firstChild;
+  let ghi = gEditor.querySelector("ul").nextSibling;
+  gSelection.collapse(def, 5);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div>abc <ul><li>  defghi</li></ul></div>",
-                                     "<div>abc <ul><li>defghi</li></ul></div>",
-                                     "<div>abc<ul><li>defghi</li></ul></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div>abc <ul><li>  defghi</li></ul></div>",
+                                      "<div>abc <ul><li>defghi</li></ul></div>",
+                                      "<div>abc<ul><li>defghi</li></ul></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: def,
     startOffset: 5,
@@ -1577,15 +1026,14 @@
 }, 'Delete at "<div>abc <ul><li>  def[]  </li></ul> ghi</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc <ul><li>  def  </li></ul> ghi</div>";
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("ul").nextSibling;
-  selection.collapse(def, def.length);
+  initializeTest("<div>abc <ul><li>  def  </li></ul> ghi</div>");
+  let def = gEditor.querySelector("li").firstChild;
+  let ghi = gEditor.querySelector("ul").nextSibling;
+  gSelection.collapse(def, def.length);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div>abc <ul><li>  defghi</li></ul></div>",
-                                     "<div>abc <ul><li>defghi</li></ul></div>",
-                                     "<div>abc<ul><li>defghi</li></ul></div>"]);
+  assert_in_array(gEditor.innerHTML, ["<div>abc <ul><li>  defghi</li></ul></div>",
+                                      "<div>abc <ul><li>defghi</li></ul></div>",
+                                      "<div>abc<ul><li>defghi</li></ul></div>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: def,
     startOffset: 5,
@@ -1596,49 +1044,12 @@
 }, 'Delete at "<div>abc <ul><li>  def  []</li></ul> ghi</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li></ul>ghi</div>";
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("ul").nextSibling;
-  selection.setBaseAndExtent(def, 3, ghi, 0);
+  initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>");
+  let abc = gEditor.querySelector("div").firstChild;
+  let def = gEditor.querySelector("li").firstChild;
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abc<ul><li>defghi</li></ul></div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: def,
-    startOffset: 3,
-    endContainer: ghi,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc<ul><li>def[</li></ul>]ghi</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc <ul><li>  def  </li></ul> ghi</div>";
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("ul").nextSibling;
-  selection.setBaseAndExtent(def, def.length, ghi, 0);
-  await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div>abc <ul><li>  defghi</li></ul></div>",
-                                     "<div>abc <ul><li>defghi</li></ul></div>",
-                                     "<div>abc<ul><li>defghi</li></ul></div>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: def,
-    startOffset: 5,
-    endContainer: ghi,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc <ul><li>  def  [</li></ul>] ghi</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  selection.collapse(abc, 3);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdef<ul><li>ghi</li></ul>jkl</div>");
+  assert_equals(gEditor.innerHTML, "<div>abcdef<ul><li>ghi</li></ul>jkl</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: abc,
     startOffset: 3,
@@ -1649,49 +1060,12 @@
 }, 'Delete at "<div>abc[]<ul><li>def</li><li>ghi</li></ul>jkl</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  selection.setBaseAndExtent(abc, 3, def, 0);
+  initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>");
+  let def = gEditor.querySelector("li").firstChild;
+  let ghi = gEditor.querySelector("li + li").firstChild;
+  gSelection.collapse(def, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abcdef<ul><li>ghi</li></ul>jkl</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: def,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc[<ul><li>]def</li><li>ghi</li></ul>jkl</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let abc = editor.querySelector("div").firstChild;
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("li + li").firstChild;
-  selection.setBaseAndExtent(abc, 3, ghi, 0);
-  await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<div>abcghijkl</div>",
-                                     "<div>abcghijkl<br></div>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: ghi,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc[<ul><li>def</li><li>]ghi</li></ul>jkl</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("li + li").firstChild;
-  selection.collapse(def, 3);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abc<ul><li>defghi</li></ul>jkl</div>");
+  assert_equals(gEditor.innerHTML, "<div>abc<ul><li>defghi</li></ul>jkl</div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: def,
     startOffset: 3,
@@ -1702,30 +1076,12 @@
 }, 'Delete at "<div>abc<ul><li>def[]</li><li>ghi</li></ul>jkl</div>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let def = editor.querySelector("li").firstChild;
-  let ghi = editor.querySelector("li + li").firstChild;
-  selection.setBaseAndExtent(def, 3, ghi, 0);
+  initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>");
+  let ghi = gEditor.querySelector("li + li").firstChild;
+  let jkl = gEditor.querySelector("ul").nextSibling;
+  gSelection.collapse(ghi, 3);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abc<ul><li>defghi</li></ul>jkl</div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: def,
-    startOffset: 3,
-    endContainer: ghi,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc<ul><li>def[</li><li>]ghi</li></ul>jkl</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let ghi = editor.querySelector("li + li").firstChild;
-  let jkl = editor.querySelector("ul").nextSibling;
-  selection.collapse(ghi, 3);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abc<ul><li>def</li><li>ghijkl</li></ul></div>");
+  assert_equals(gEditor.innerHTML, "<div>abc<ul><li>def</li><li>ghijkl</li></ul></div>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: ghi,
     startOffset: 3,
@@ -1735,53 +1091,18 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Delete at "<div>abc<ul><li>def</li><li>ghi[]</li></ul>jkl</div>"');
 
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let ghi = editor.querySelector("li + li").firstChild;
-  let jkl = editor.querySelector("ul").nextSibling;
-  selection.setBaseAndExtent(ghi, 3, jkl, 0);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abc<ul><li>def</li><li>ghijkl</li></ul></div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: ghi,
-    startOffset: 3,
-    endContainer: jkl,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc<ul><li>def</li><li>ghi[</li></ul>]jkl</div>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
-  let def = editor.querySelector("li").firstChild;
-  let jkl = editor.querySelector("ul").nextSibling;
-  selection.setBaseAndExtent(def, 3, jkl, 0);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<div>abc<ul><li>defjkl</li></ul></div>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: def,
-    startOffset: 3,
-    endContainer: jkl,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<div>abc<ul><li>def[</li><li>ghi</li></ul>]jkl</div>"');
-
 // Delete in empty paragraph should remove the empty paragraph.  In this
 // case, it should be treated as joining with the previous paragraph.
 // The target range should include the invisible <br> element in the empty
 // paragraph.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p><br></p><p>abc</p>";
-  let p1 = editor.querySelector("p");
+  initializeTest("<p><br></p><p>abc</p>");
+  let p1 = gEditor.querySelector("p");
   let p2 = p1.nextSibling;
   let abc = p2.firstChild;
-  selection.collapse(p1, 0);
+  gSelection.collapse(p1, 0);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abc</p>");
+  assert_equals(gEditor.innerHTML, "<p>abc</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p1,
     startOffset: 0,
@@ -1791,36 +1112,17 @@
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Delete at "<p>{}<br></p><p>abc</p>"');
 
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p><br></p><p>abc</p>";
-  let p1 = editor.querySelector("p");
-  let p2 = p1.nextSibling;
-  let abc = p2.firstChild;
-  selection.setBaseAndExtent(p1, 0, abc, 0);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abc</p>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: p1,
-    startOffset: 0,
-    endContainer: abc,
-    endOffset: 0,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<p>{<br></p><p>]abc</p>"');
-
 // Delete ignore the empty span and the other things must be same as the
 // previous test.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p><span></span><br></p><p>abc</p>";
-  let p1 = editor.querySelector("p");
+  initializeTest("<p><span></span><br></p><p>abc</p>");
+  let p1 = gEditor.querySelector("p");
   let span = p1.firstChild;
   let p2 = p1.nextSibling;
   let abc = p2.firstChild;
-  selection.collapse(span, 0);
+  gSelection.collapse(span, 0);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p>abc</p>");
+  assert_equals(gEditor.innerHTML, "<p>abc</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p1,
     startOffset: 0,
@@ -1833,15 +1135,14 @@
 // If invisible white-spaces are removed with same action as above tests,
 // the range should be included in the target ranges.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p><br></p><p>  abc</p>";
-  let p1 = editor.querySelector("p");
+  initializeTest("<p><br></p><p>  abc</p>");
+  let p1 = gEditor.querySelector("p");
   let p2 = p1.nextSibling;
   let abc = p2.firstChild;
-  selection.collapse(p1, 0);
+  gSelection.collapse(p1, 0);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<p>  abc</p>",
-                                     "<p>abc</p>"]);
+  assert_in_array(gEditor.innerHTML, ["<p>  abc</p>",
+                                      "<p>abc</p>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p1,
     startOffset: 0,
@@ -1854,14 +1155,13 @@
 // If the next block begins with non-editable content, target range
 // should be at the non-editable content node.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p><br></p><p><span contenteditable=\"false\">abc</span>def</p>";
-  let p1 = editor.querySelector("p");
+  initializeTest("<p><br></p><p><span contenteditable=\"false\">abc</span>def</p>");
+  let p1 = gEditor.querySelector("p");
   let p2 = p1.nextSibling;
-  let span = editor.querySelector("span");
-  selection.collapse(p1, 0);
+  let span = gEditor.querySelector("span");
+  gSelection.collapse(p1, 0);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<p><span contenteditable=\"false\">abc</span>def</p>");
+  assert_equals(gEditor.innerHTML, "<p><span contenteditable=\"false\">abc</span>def</p>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p1,
     startOffset: 0,
@@ -1875,36 +1175,34 @@
 // with start of the text node in the last paragraph.  Otherwise, ends at
 // the non-editable paragraph.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p><br></p><p contenteditable=\"false\">abc</p><p>def</p>";
-  let p1 = editor.querySelector("p");
+  initializeTest("<p><br></p><p contenteditable=\"false\">abc</p><p>def</p>");
+  let p1 = gEditor.querySelector("p");
   let p2 = p1.nextSibling;
   let p3 = p2.nextSibling;
   let def = p3.firstChild;
-  selection.collapse(p3, 0);
+  gSelection.collapse(p3, 0);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<p>def</p>",
-                                     "<p contenteditable=\"false\">abc</p><p>def</p>"]);
+  assert_in_array(gEditor.innerHTML, ["<p>def</p>",
+                                      "<p contenteditable=\"false\">abc</p><p>def</p>"]);
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: p1,
     startOffset: 0,
-    endContainer: p2.isConnected ? editor : p3,
+    endContainer: p2.isConnected ? gEditor : p3,
     endOffset: p2.isConnected ? 1 : 0,
   });
   checkGetTargetRangesOfInputOnDeleteSomething();
 }, 'Delete at "<p>{}<br></p><p contenteditable=\"false\">abc</p><p>def</p>"');
 
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<span contenteditable=\"false\">def</span>ghi</p>";
-  let p = editor.querySelector("p");
+  initializeTest("<p>abc<span contenteditable=\"false\">def</span>ghi</p>");
+  let p = gEditor.querySelector("p");
   let abc = p.firstChild;
-  selection.collapse(abc, 3);
+  gSelection.collapse(abc, 3);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<p>abc<span contenteditable=\"false\">def</span>ghi</p>",
-                                     "<p>abcghi</p>",
-                                     "<p>abcghi<br></p>"]);
-  if (editor.innerHTML === "<p>abc<span contenteditable=\"false\">def</span>ghi</p>") {
+  assert_in_array(gEditor.innerHTML, ["<p>abc<span contenteditable=\"false\">def</span>ghi</p>",
+                                      "<p>abcghi</p>",
+                                      "<p>abcghi<br></p>"]);
+  if (gEditor.innerHTML === "<p>abc<span contenteditable=\"false\">def</span>ghi</p>") {
     checkGetTargetRangesOfBeforeinputOnDeleteSomething({
       startContainer: abc,
       startOffset: 3,
@@ -1925,41 +1223,15 @@
   }
 }, 'Delete at "<p>abc[]<span contenteditable=\"false\">def</span>ghi</p>"');
 
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>abc<span contenteditable=\"false\">def</span>ghi</p>";
-  let p = editor.querySelector("p");
-  let abc = p.firstChild;
-  let ghi = p.lastChild;
-  selection.setBaseAndExtent(abc, 3, ghi, 0);
-  await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<p>abc<span contenteditable=\"false\">def</span>ghi</p>",
-                                     "<p>abcghi</p>",
-                                     "<p>abcghi<br></p>"]);
-  // Don't need to shrink the range for avoiding to waste runtime cost.
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 3,
-    endContainer: ghi,
-    endOffset: 0,
-  });
-  if (editor.innerHTML === "<p>abc<span contenteditable=\"false\">def</span>ghi</p>") {
-    checkGetTargetRangesOfInputOnDoNothing();
-  } else {
-    checkGetTargetRangesOfInputOnDeleteSomething();
-  }
-}, 'Delete at "<p>abc[<span contenteditable=\"false\">def</span>]ghi</p>"');
-
 // If just removes the paragraph, target range should end at the table element.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p><br></p><table><tr><td>cell</td></tr></table>";
-  let cell = editor.querySelector("td");
-  let p = editor.querySelector("p");
-  selection.collapse(p, 0);
+  initializeTest("<p><br></p><table><tr><td>cell</td></tr></table>");
+  let cell = gEditor.querySelector("td");
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p, 0);
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<table><tbody><tr><td>cell</td></tr></tbody></table>",
-                                     "<p><br></p><table><tbody><tr><td>cell</td></tr></tbody></table>"]);
+  assert_in_array(gEditor.innerHTML, ["<table><tbody><tr><td>cell</td></tr></tbody></table>",
+                                      "<p><br></p><table><tbody><tr><td>cell</td></tr></tbody></table>"]);
   if (p.isConnected) {
     checkGetTargetRangesOfBeforeinputOnDeleteSomething({
       startContainer: p,
@@ -1972,7 +1244,7 @@
     checkGetTargetRangesOfBeforeinputOnDeleteSomething({
       startContainer: p,
       startOffset: 0,
-      endContainer: editor,
+      endContainer: gEditor,
       endOffset: 1,
     });
     checkGetTargetRangesOfInputOnDeleteSomething();
@@ -1982,13 +1254,12 @@
 // If table cell won't be joined, target range should be collapsed in the
 // cell.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<table><tr><td><br></td><td>cell2</td></tr></table>";
-  let cell1 = editor.querySelector("td");
+  initializeTest("<table><tr><td><br></td><td>cell2</td></tr></table>");
+  let cell1 = gEditor.querySelector("td");
   let cell2 = cell1.nextSibling;
-  selection.collapse(cell1, 0);
+  gSelection.collapse(cell1, 0);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<table><tbody><tr><td><br></td><td>cell2</td></tr></tbody></table>");
+  assert_equals(gEditor.innerHTML, "<table><tbody><tr><td><br></td><td>cell2</td></tr></tbody></table>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: cell1,
     startOffset: 0,
@@ -1998,71 +1269,14 @@
   checkGetTargetRangesOfInputOnDoNothing();
 }, 'Delete at "<table><tr><td>{}<br></td><td>cell2</td></tr></table>"');
 
-// The table structure shouldn't be modified when deleting cell contents,
-// in this case, getTargetRanges() should return multiple ranges in each
-// cell?
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<table><tr><td>abc</td><td>def</td></tr></table>";
-  let abc = editor.querySelector("td").firstChild;
-  let def = editor.querySelector("td + td").firstChild;
-  selection.setBaseAndExtent(abc, 2, def, 1);
-  await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<table><tbody><tr><td>ab</td><td>ef</td></tr></tbody></table>");
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 2,
-    endContainer: def,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<table><tr><td>ab[c</td><td>d]ef</td></tr></table>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<table><tr><td>abc</td><td>def</td></tr><tr><td>ghi</td><td>jkl</td></tr></table>";
-  let abc = editor.querySelector("td").firstChild;
-  let jkl = editor.querySelector("tr + tr > td + td").firstChild;
-  selection.setBaseAndExtent(abc, 2, jkl, 1);
-  await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<table><tbody><tr><td>ab</td><td></td></tr><tr><td></td><td>kl</td></tr></tbody></table>",
-                                     "<table><tbody><tr><td>ab</td><td><br></td></tr><tr><td><br></td><td>kl</td></tr></tbody></table>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 2,
-    endContainer: jkl,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<table><tr><td>ab[c</td><td>def</td></tr><tr><td>ghi</td><td>j]kl</td></tr></table>"');
-
-promise_test(async () => {
-  reset();
-  editor.innerHTML = "<table><tr><td>abc</td><td>def</td></tr></table><table><tr><td>ghi</td><td>jkl</td></tr></table>";
-  let abc = editor.querySelector("td").firstChild;
-  let jkl = editor.querySelector("table + table td + td").firstChild;
-  selection.setBaseAndExtent(abc, 2, jkl, 1);
-  await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<table><tbody><tr><td>ab</td><td></td></tr></tbody></table><table><tbody><tr><td></td><td>kl</td></tr></tbody></table>",
-                                     "<table><tbody><tr><td>ab</td><td><br></td></tr></tbody></table><table><tbody><tr><td><br></td><td>kl</td></tr></tbody></table>"]);
-  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
-    startContainer: abc,
-    startOffset: 2,
-    endContainer: jkl,
-    endOffset: 1,
-  });
-  checkGetTargetRangesOfInputOnDeleteSomething();
-}, 'Delete at "<table><tr><td>ab[c</td><td>def</td></tr></table><table><tr><td>ghi</td><td>j]kl</td></tr></table>"');
-
 // If table caption won't be deleted, target range should be collapsed in the
 // caption element.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<table><caption><br></caption><tr><td>cell</td></tr></table>";
-  let caption = editor.querySelector("caption");
-  selection.collapse(caption, 0);
+  initializeTest("<table><caption><br></caption><tr><td>cell</td></tr></table>");
+  let caption = gEditor.querySelector("caption");
+  gSelection.collapse(caption, 0);
   await sendDeleteKey();
-  assert_equals(editor.innerHTML, "<table><caption><br></caption><tbody><tr><td>cell</td></tr></tbody></table>");
+  assert_equals(gEditor.innerHTML, "<table><caption><br></caption><tbody><tr><td>cell</td></tr></tbody></table>");
   checkGetTargetRangesOfBeforeinputOnDeleteSomething({
     startContainer: caption,
     startOffset: 0,
@@ -2075,17 +1289,16 @@
 // If caret is not adjacent of deleting character, browser may not delete the
 // character, but update the caret position for next deletion.
 promise_test(async () => {
-  reset();
-  editor.innerHTML = "<p>hello&#x5E9;&#x5DC;&#x5D5;&#x5DD;</p>";
-  let text1 = editor.querySelector("p").firstChild;
+  initializeTest("<p>hello&#x5E9;&#x5DC;&#x5D5;&#x5DD;</p>");
+  let text1 = gEditor.querySelector("p").firstChild;
   let text2 = text1.nextSibling;
-  selection.collapse(text1, 4);
+  gSelection.collapse(text1, 4);
   await sendArrowRightKey();
   await sendDeleteKey();
-  assert_in_array(editor.innerHTML, ["<p>hello\u05E9\u05DC\u05D5\u05DD</p>",
-                                     "<p>hello\u05DC\u05D5\u05DD</p>",
-                                     "<p>hello\u05E9\u05DC\u05D5</p>"]);
-  if (editor.innerHTML === "<p>hello\u05E9\u05DC\u05D5\u05DD</p>") {
+  assert_in_array(gEditor.innerHTML, ["<p>hello\u05E9\u05DC\u05D5\u05DD</p>",
+                                      "<p>hello\u05DC\u05D5\u05DD</p>",
+                                      "<p>hello\u05E9\u05DC\u05D5</p>"]);
+  if (gEditor.innerHTML === "<p>hello\u05E9\u05DC\u05D5\u05DD</p>") {
     checkGetTargetRangesOfBeforeinputOnDeleteSomething({
       startContainer: text2 ? text2 : text1,
       startOffset: text2 ? 0 : 5,
@@ -2093,7 +1306,7 @@
       endOffset: text2 ? 0 : 5,
     });
     checkGetTargetRangesOfInputOnDoNothing();
-  } else if (editor.innerHTML === "<p>hello\u05DC\u05D5\u05DD</p>") {
+  } else if (gEditor.innerHTML === "<p>hello\u05DC\u05D5\u05DD</p>") {
     checkGetTargetRangesOfBeforeinputOnDeleteSomething({
       startContainer: text2 ? text2: text1,
       startOffset: text2 ? 0 : 5,
@@ -2129,15 +1342,14 @@
 }
 
 promise_test(async () => {
-  reset();
   const kText = "abc def ghi";
-  editor.innerHTML = `<p>${kText}</p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc ".length);
+  initializeTest(`<p>${kText}</p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc ".length);
   await sendDeleteKey(kShift);
   let startOffset = getFirstDifferentOffset(p.firstChild.data, kText);
   let length = kText.length - p.firstChild.data.length;
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
@@ -2153,15 +1365,14 @@
 }, 'Shift + Delete at "<p>abc []def ghi</p>"');
 
 promise_test(async () => {
-  reset();
   const kText = "abc def ghi";
-  editor.innerHTML = `<p>${kText}</p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc ".length);
+  initializeTest(`<p>${kText}</p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc ".length);
   await sendDeleteKey(kControl);
   let startOffset = getFirstDifferentOffset(p.firstChild.data, kText);
   let length = kText.length - p.firstChild.data.length;
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
@@ -2177,15 +1388,14 @@
 }, 'Control + Delete at "<p>abc []def ghi</p>"');
 
 promise_test(async () => {
-  reset();
   const kText = "abc def ghi";
-  editor.innerHTML = `<p>${kText}</p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc ".length);
+  initializeTest(`<p>${kText}</p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc ".length);
   await sendDeleteKey(kAlt);
   let startOffset = getFirstDifferentOffset(p.firstChild.data, kText);
   let length = kText.length - p.firstChild.data.length;
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
@@ -2201,15 +1411,14 @@
 }, 'Alt + Delete at "<p>abc []def ghi</p>"');
 
 promise_test(async () => {
-  reset();
   const kText = "abc def ghi";
-  editor.innerHTML = `<p>${kText}</p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc ".length);
+  initializeTest(`<p>${kText}</p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc ".length);
   await sendDeleteKey(kMeta);
   let startOffset = getFirstDifferentOffset(p.firstChild.data, kText);
   let length = kText.length - p.firstChild.data.length;
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
@@ -2225,18 +1434,17 @@
 }, 'Meta + Delete at "<p>abc []def ghi</p>"');
 
 promise_test(async () => {
-  reset();
   const kText = "abc def";
-  editor.innerHTML = `<p>${kText}   </p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc ".length);
+  initializeTest(`<p>${kText}   </p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc ".length);
   await sendDeleteKey(kShift);
   let visibleText = p.firstChild.data.replace(/%s+$/, "");
   let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length);
   let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText);
   let length = kText.length + 3 - p.firstChild.data.length;
   // If invisible white-spaces are deleted, they should be contained in the target range.
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length) + invisibleWhiteSpaces}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
@@ -2252,18 +1460,17 @@
 }, 'Shift + Delete at "<p>abc []def   </p>"');
 
 promise_test(async () => {
-  reset();
   const kText = "abc def";
-  editor.innerHTML = `<p>${kText}   </p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc ".length);
+  initializeTest(`<p>${kText}   </p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc ".length);
   await sendDeleteKey(kControl);
   let visibleText = p.firstChild.data.replace(/%s+$/, "");
   let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length);
   let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText);
   let length = kText.length + 3 - p.firstChild.data.length;
   // If invisible white-spaces are deleted, they should be contained in the target range.
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length) + invisibleWhiteSpaces}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
@@ -2279,18 +1486,17 @@
 }, 'Control + Delete at "<p>abc []def   </p>"');
 
 promise_test(async () => {
-  reset();
   const kText = "abc def";
-  editor.innerHTML = `<p>${kText}   </p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc ".length);
+  initializeTest(`<p>${kText}   </p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc ".length);
   await sendDeleteKey(kAlt);
   let visibleText = p.firstChild.data.replace(/%s+$/, "");
   let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length);
   let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText);
   let length = kText.length + 3 - p.firstChild.data.length;
   // If invisible white-spaces are deleted, they should be contained in the target range.
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length) + invisibleWhiteSpaces}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
@@ -2306,18 +1512,17 @@
 }, 'Alt + Delete at "<p>abc []def   </p>"');
 
 promise_test(async () => {
-  reset();
   const kText = "abc def";
-  editor.innerHTML = `<p>${kText}   s</p>`;
-  let p = editor.querySelector("p");
-  selection.collapse(p.firstChild, "abc ".length);
+  initializeTest(`<p>${kText}   s</p>`);
+  let p = gEditor.querySelector("p");
+  gSelection.collapse(p.firstChild, "abc ".length);
   await sendDeleteKey(kMeta);
   let visibleText = p.firstChild.data.replace(/%s+$/, "");
   let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length);
   let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText);
   let length = kText.length + 3 - p.firstChild.data.length;
   // If invisible white-spaces are deleted, they should be contained in the target range.
-  assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
+  assert_equals(gEditor.innerHTML.replace(/&nbsp;/g, " "),
       `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length) + invisibleWhiteSpaces}</p>`);
   if (startOffset === kText.length) {
     checkBeforeinputAndInputEventsOnNOOP();
diff --git a/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html b/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html
new file mode 100644
index 0000000..2e16d2a4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html
@@ -0,0 +1,648 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<meta name="variant" content="?Backspace">
+<meta name="variant" content="?Delete">
+<meta name="variant" content="?TypingA">
+<title>InputEvent.getTargetRanges() with non-collapsed selection</title>
+<div contenteditable></div>
+<script src="input-events-get-target-ranges.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script>
+"use strict";
+
+let action = location.search.substring(1);
+function run() {
+  switch (action) {
+    case "Backspace":
+      return sendBackspaceKey();
+    case "Delete":
+      return sendDeleteKey();
+    case "TypingA":
+      return sendKeyA();
+    default:
+      throw "Unhandled variant";
+  }
+}
+
+let insertedHTML = action === "TypingA" ? "a" : "";
+
+// If text node is selected, target range should be shrunken to the edge of
+// text node.
+promise_test(async () => {
+  initializeTest("<p>abc</p>");
+  let p = gEditor.firstChild;
+  let abc = p.firstChild;
+  gSelection.setBaseAndExtent(p, 0, p, 1);
+  await run();
+  assert_equals(gEditor.innerHTML, `<p>${insertedHTML !== "" ? insertedHTML : "<br>"}</p>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 0,
+    endContainer: abc,
+    endOffset: 3,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<p>{abc}</p>"`);
+
+promise_test(async () => {
+  initializeTest("<p>abc<br></p>");
+  let p = gEditor.firstChild;
+  let abc = p.firstChild;
+  gSelection.setBaseAndExtent(p, 0, p, 1);
+  await run();
+  assert_in_array(gEditor.innerHTML, [`<p>${insertedHTML !== "" ? insertedHTML : "<br>"}</p>`,
+                                      `<p>${insertedHTML}<br></p>`]);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 0,
+    endContainer: abc,
+    endOffset: 3,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<p>{abc}<br></p>"`);
+
+promise_test(async () => {
+  initializeTest(`<p><img src="${kImgSrc}"></p>`);
+  let p = gEditor.firstChild;
+  gSelection.setBaseAndExtent(p, 0, p, 1);
+  await run();
+  assert_equals(gEditor.innerHTML, `<p>${insertedHTML !== "" ? insertedHTML : "<br>"}</p>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: p,
+    startOffset: 0,
+    endContainer: p,
+    endOffset: 1,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<p>{<img>}</p>"`);
+
+promise_test(async () => {
+  initializeTest(`<p><img src="${kImgSrc}"><br></p>`);
+  let p = gEditor.firstChild;
+  gSelection.setBaseAndExtent(p, 0, p, 1);
+  await run();
+  assert_in_array(gEditor.innerHTML, [`<p>${insertedHTML !== "" ? insertedHTML : "<br>"}</p>`,
+                                      `<p>${insertedHTML}<br></p>`]);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: p,
+    startOffset: 0,
+    endContainer: p,
+    endOffset: 1,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<p>{<img>}<br></p>"`);
+
+promise_test(async () => {
+  initializeTest("<p> abc </p>");
+  let p = gEditor.firstChild;
+  let abc = p.firstChild;
+  gSelection.setBaseAndExtent(p, 0, p, 1);
+  await run();
+  assert_equals(gEditor.innerHTML, `<p>${insertedHTML !== "" ? insertedHTML : "<br>"}</p>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 0,
+    endContainer: abc,
+    endOffset: 5,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<p>{ abc }</p>"`);
+
+// Invisible leading white-spaces in current block and invisible trailing
+// white-spaces in the previous block should be deleted for avoiding they
+// becoming visible when the blocks are joined.  Perhaps, they should be
+// contained by the range of `getTargetRanges()`, but needs discussion.
+// https://github.com/w3c/input-events/issues/112
+promise_test(async () => {
+  initializeTest("<p>abc   </p><p>   def</p>");
+  let p1 = gEditor.firstChild;
+  let abc = p1.firstChild;
+  let p2 = p1.nextSibling;
+  let def = p2.firstChild;
+  gSelection.setBaseAndExtent(abc, 6, def, 0);
+  await run();
+  assert_equals(gEditor.innerHTML, `<p>abc${insertedHTML}def</p>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 3,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<p>abc   [</p><p>]   def</p>"`);
+
+promise_test(async () => {
+  initializeTest("<p>abc</p><p>def</p>");
+  let abc = gEditor.querySelector("p").firstChild;
+  let def = gEditor.querySelector("p + p").firstChild;
+  gSelection.setBaseAndExtent(abc, 2, def, 1);
+  await run();
+  assert_equals(gEditor.innerHTML, `<p>ab${insertedHTML}ef</p>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 2,
+    endContainer: def,
+    endOffset: 1,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<p>ab[c</p><p>d]ef</p>"`);
+
+promise_test(async () => {
+  initializeTest("<p>abc </p><p> def</p>");
+  let abc = gEditor.querySelector("p").firstChild;
+  let def = gEditor.querySelector("p + p").firstChild;
+  gSelection.setBaseAndExtent(abc, 2, def, 2);
+  await run();
+  assert_equals(gEditor.innerHTML, `<p>ab${insertedHTML}ef</p>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 2,
+    endContainer: def,
+    endOffset: 2,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<p>ab[c </p><p> d]ef</p>"`);
+
+promise_test(async () => {
+  initializeTest("<p>abc </p><p> def</p>");
+  let abc = gEditor.querySelector("p").firstChild;
+  let def = gEditor.querySelector("p + p").firstChild;
+  gSelection.setBaseAndExtent(abc, 2, def, 0);
+  await run();
+  assert_equals(gEditor.innerHTML, `<p>ab${insertedHTML}def</p>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 2,
+    endContainer: def,
+    endOffset: 1,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<p>ab[c </p><p>] def</p>"`);
+
+promise_test(async () => {
+  initializeTest("<p>abc </p><p> def</p>");
+  let abc = gEditor.querySelector("p").firstChild;
+  let def = gEditor.querySelector("p + p").firstChild;
+  gSelection.setBaseAndExtent(abc, 4, def, 0);
+  await run();
+  assert_equals(gEditor.innerHTML, `<p>abc${insertedHTML}def</p>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 1,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<p>abc [</p><p>] def</p>"`);
+
+promise_test(async () => {
+  initializeTest("<p>abc </p><p> def</p>");
+  let abc = gEditor.querySelector("p").firstChild;
+  let def = gEditor.querySelector("p + p").firstChild;
+  gSelection.setBaseAndExtent(abc, 4, def, 1);
+  await run();
+  assert_equals(gEditor.innerHTML, `<p>abc${insertedHTML}def</p>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 1,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<p>abc [</p><p> ]def</p>"`);
+
+// Different from collapsed range around an atomic content, non-collapsed
+// range may not be shrunken to select only the atomic content for avoid
+// to waste runtime cost.
+promise_test(async () => {
+  initializeTest(`<p>abc<img src="${kImgSrc}">def</p>`);
+  let p = gEditor.querySelector("p");
+  let abc = p.firstChild;
+  let def = p.lastChild;
+  gSelection.setBaseAndExtent(abc, 3, def, 0);
+  await run();
+  assert_equals(gEditor.innerHTML, `<p>abc${insertedHTML}def</p>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 0,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<p>abc[<img>]def</p>"`);
+
+promise_test(async () => {
+  initializeTest(`<div>abc<hr>def</div>`);
+  let div = gEditor.querySelector("div");
+  let abc = div.firstChild;
+  let def = div.lastChild;
+  gSelection.setBaseAndExtent(abc, 3, def, 0);
+  await run();
+  assert_equals(gEditor.innerHTML, `<div>abc${insertedHTML}def</div>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 0,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc[<hr>]def</div>"`);
+
+promise_test(async () => {
+  initializeTest(`<div>abc <hr>def</div>`);
+  let div = gEditor.querySelector("div");
+  let abc = div.firstChild;
+  let def = div.lastChild;
+  gSelection.setBaseAndExtent(abc, 4, def, 0);
+  await run();
+  assert_equals(gEditor.innerHTML, `<div>abc${insertedHTML}def</div>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 0,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc [<hr>]def</div>"`);
+
+promise_test(async () => {
+  initializeTest(`<div>abc <hr> def</div>`);
+  let div = gEditor.querySelector("div");
+  let abc = div.firstChild;
+  let def = div.lastChild;
+  gSelection.setBaseAndExtent(abc, 4, def, 0);
+  await run();
+  assert_equals(gEditor.innerHTML, `<div>abc${insertedHTML}def</div>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 1,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc [<hr>] def</div>"`);
+
+promise_test(async () => {
+  initializeTest(`<div>abc <hr> def</div>`);
+  let div = gEditor.querySelector("div");
+  let abc = div.firstChild;
+  let def = div.lastChild;
+  gSelection.setBaseAndExtent(div, 1, div, 2);
+  await run();
+  assert_equals(gEditor.innerHTML, `<div>abc${insertedHTML}def</div>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 1,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc {<hr>} def</div>"`);
+
+// Deleting visible `<br>` element should be contained by a range of
+// `getTargetRanges()`.  However, when only the `<br>` element is selected,
+// the range shouldn't start from nor end by surrounding text nodes?
+// https://github.com/w3c/input-events/issues/112
+promise_test(async () => {
+  initializeTest("<p>abc<br>def</p>");
+  gSelection.setBaseAndExtent(gEditor.firstChild, 1, gEditor.firstChild, 2);
+  await run();
+  assert_equals(gEditor.innerHTML, `<p>abc${insertedHTML}def</p>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: gEditor.firstChild,
+    startOffset: 1,
+    endContainer: gEditor.firstChild,
+    endOffset: 2,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<p>abc{<br>}def</p>"`);
+
+promise_test(async () => {
+  initializeTest("<div>abc<p>def<br>ghi</p></div>");
+  let p = gEditor.querySelector("p");
+  let def = p.firstChild;
+  let abc = gEditor.firstChild.firstChild;
+  gSelection.setBaseAndExtent(abc, 3, def, 0);
+  await run();
+  assert_in_array(gEditor.innerHTML, [`<div>abc${insertedHTML}def<p>ghi</p></div>`,
+                                      `<div>abc${insertedHTML}def<br><p>ghi</p></div>`]);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 0,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc[<p>]def<br>ghi</p></div>"`);
+
+promise_test(async () => {
+  initializeTest("<div>abc   <p>   def<br>ghi</p></div>");
+  let p = gEditor.querySelector("p");
+  let def = p.firstChild;
+  let abc = gEditor.firstChild.firstChild;
+  gSelection.setBaseAndExtent(abc, abc.length, def, 0);
+  await run();
+  assert_in_array(gEditor.innerHTML, [`<div>abc${insertedHTML}def<p>ghi</p></div>`,
+                                      `<div>abc${insertedHTML}def<br><p>ghi</p></div>`]);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 3,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc   [<p>]   def<br>ghi</p></div>"`);
+
+promise_test(async () => {
+  initializeTest("<div><p>abc</p>def</div>");
+  let abc = gEditor.querySelector("p").firstChild;
+  let def = gEditor.querySelector("p").nextSibling;
+  gSelection.setBaseAndExtent(abc, 3, def, 0);
+  await run();
+  assert_in_array(gEditor.innerHTML, [`<div><p>abc${insertedHTML}def</p></div>`,
+                                      `<div><p>abc${insertedHTML}def<br></p></div>`]);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 0,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div><p>abc[</p>]def</div>"`);
+
+promise_test(async () => {
+  initializeTest("<div><p>abc   </p>   def</div>");
+  let abc = gEditor.querySelector("p").firstChild;
+  let def = gEditor.querySelector("p").nextSibling;
+  gSelection.setBaseAndExtent(abc, abc.length, def, 0);
+  await run();
+  assert_in_array(gEditor.innerHTML, [`<div><p>abc${insertedHTML}def</p></div>`,
+                                      `<div><p>abc${insertedHTML}def<br></p></div>`]);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 3,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div><p>abc   [</p>]   def</div>"`);
+
+promise_test(async () => {
+  initializeTest("<div>abc<ul><li>def</li></ul>ghi</div>");
+  let abc = gEditor.querySelector("div").firstChild;
+  let def = gEditor.querySelector("li").firstChild;
+  gSelection.setBaseAndExtent(abc, 3, def, 0);
+  await run();
+  assert_equals(gEditor.innerHTML, `<div>abc${insertedHTML}defghi</div>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 0,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc[<ul><li>]def</li></ul>ghi</div>"`);
+
+promise_test(async () => {
+  initializeTest("<div>abc  <ul><li> def </li></ul>  ghi</div>");
+  let abc = gEditor.querySelector("div").firstChild;
+  let def = gEditor.querySelector("li").firstChild;
+  gSelection.setBaseAndExtent(abc, abc.length, def, 0);
+  await run();
+  assert_equals(gEditor.innerHTML, `<div>abc${insertedHTML}defghi</div>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 1,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc  [<ul><li>] def </li></ul>  ghi</div>"`);
+
+promise_test(async () => {
+  initializeTest("<div>abc<ul><li>def</li></ul>ghi</div>");
+  let def = gEditor.querySelector("li").firstChild;
+  let ghi = gEditor.querySelector("ul").nextSibling;
+  gSelection.setBaseAndExtent(def, 3, ghi, 0);
+  await run();
+  assert_equals(gEditor.innerHTML, `<div>abc<ul><li>def${insertedHTML}ghi</li></ul></div>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: def,
+    startOffset: 3,
+    endContainer: ghi,
+    endOffset: 0,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc<ul><li>def[</li></ul>]ghi</div>"`);
+
+promise_test(async () => {
+  initializeTest("<div>abc <ul><li>  def  </li></ul> ghi</div>");
+  let def = gEditor.querySelector("li").firstChild;
+  let ghi = gEditor.querySelector("ul").nextSibling;
+  gSelection.setBaseAndExtent(def, def.length, ghi, 0);
+  await run();
+  assert_in_array(gEditor.innerHTML, [`<div>abc <ul><li>  def${insertedHTML}ghi</li></ul></div>`,
+                                      `<div>abc <ul><li>def${insertedHTML}ghi</li></ul></div>`,
+                                      `<div>abc<ul><li>def${insertedHTML}ghi</li></ul></div>`]);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: def,
+    startOffset: 5,
+    endContainer: ghi,
+    endOffset: 1,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc <ul><li>  def  [</li></ul>] ghi</div>"`);
+
+promise_test(async () => {
+  initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>");
+  let abc = gEditor.querySelector("div").firstChild;
+  let def = gEditor.querySelector("li").firstChild;
+  gSelection.setBaseAndExtent(abc, 3, def, 0);
+  await run();
+  assert_equals(gEditor.innerHTML, `<div>abc${insertedHTML}def<ul><li>ghi</li></ul>jkl</div>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: def,
+    endOffset: 0,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc[<ul><li>]def</li><li>ghi</li></ul>jkl</div>"`);
+
+promise_test(async () => {
+  initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>");
+  let abc = gEditor.querySelector("div").firstChild;
+  let def = gEditor.querySelector("li").firstChild;
+  let ghi = gEditor.querySelector("li + li").firstChild;
+  gSelection.setBaseAndExtent(abc, 3, ghi, 0);
+  await run();
+  assert_in_array(gEditor.innerHTML, [`<div>abc${insertedHTML}ghijkl</div>`,
+                                      `<div>abc${insertedHTML}ghijkl<br></div>`]);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: ghi,
+    endOffset: 0,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc[<ul><li>def</li><li>]ghi</li></ul>jkl</div>"`);
+
+promise_test(async () => {
+  initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>");
+  let def = gEditor.querySelector("li").firstChild;
+  let ghi = gEditor.querySelector("li + li").firstChild;
+  gSelection.setBaseAndExtent(def, 3, ghi, 0);
+  await run();
+  assert_equals(gEditor.innerHTML, `<div>abc<ul><li>def${insertedHTML}ghi</li></ul>jkl</div>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: def,
+    startOffset: 3,
+    endContainer: ghi,
+    endOffset: 0,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc<ul><li>def[</li><li>]ghi</li></ul>jkl</div>"`);
+
+promise_test(async () => {
+  initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>");
+  let ghi = gEditor.querySelector("li + li").firstChild;
+  let jkl = gEditor.querySelector("ul").nextSibling;
+  gSelection.setBaseAndExtent(ghi, 3, jkl, 0);
+  await run();
+  assert_equals(gEditor.innerHTML, `<div>abc<ul><li>def</li><li>ghi${insertedHTML}jkl</li></ul></div>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: ghi,
+    startOffset: 3,
+    endContainer: jkl,
+    endOffset: 0,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc<ul><li>def</li><li>ghi[</li></ul>]jkl</div>"`);
+
+promise_test(async () => {
+  initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>");
+  let def = gEditor.querySelector("li").firstChild;
+  let jkl = gEditor.querySelector("ul").nextSibling;
+  gSelection.setBaseAndExtent(def, 3, jkl, 0);
+  await run();
+  assert_equals(gEditor.innerHTML, `<div>abc<ul><li>def${insertedHTML}jkl</li></ul></div>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: def,
+    startOffset: 3,
+    endContainer: jkl,
+    endOffset: 0,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<div>abc<ul><li>def[</li><li>ghi</li></ul>]jkl</div>"`);
+
+promise_test(async () => {
+  initializeTest("<p>abc</p><p><br></p>");
+  let p1 = gEditor.querySelector("p");
+  let abc = p1.firstChild;
+  let p2 = p1.nextSibling;
+  gSelection.setBaseAndExtent(abc, 3, p2, 0);
+  await run();
+  assert_in_array(gEditor.innerHTML, [`<p>abc${insertedHTML}</p>`,
+                                      `<p>abc${insertedHTML}<br></p>`]);
+  if (gEditor.innerHTML === "<p>abc</p>") {
+    // Include the invisible `<br>` element if it's deleted.
+    checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+      startContainer: abc,
+      startOffset: 3,
+      endContainer: p2,
+      endOffset: 1,
+    });
+  } else {
+    checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+      startContainer: abc,
+      startOffset: 3,
+      endContainer: p2,
+      endOffset: 0,
+    });
+  }
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<p>abc[</p><p>}<br></p>"`);
+
+promise_test(async () => {
+  initializeTest("<p>abc<span contenteditable=\"false\">def</span>ghi</p>");
+  let p = gEditor.querySelector("p");
+  let abc = p.firstChild;
+  let ghi = p.lastChild;
+  gSelection.setBaseAndExtent(abc, 3, ghi, 0);
+  await run();
+  assert_in_array(gEditor.innerHTML, ["<p>abc<span contenteditable=\"false\">def</span>ghi</p>",
+                                      `<p>abc${insertedHTML}ghi</p>`,
+                                      `<p>abc${insertedHTML}ghi<br></p>`]);
+  // Don't need to shrink the range for avoiding to waste runtime cost.
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 3,
+    endContainer: ghi,
+    endOffset: 0,
+  });
+  if (gEditor.innerHTML === "<p>abc<span contenteditable=\"false\">def</span>ghi</p>") {
+    checkGetTargetRangesOfInputOnDoNothing();
+  } else {
+    checkGetTargetRangesOfInputOnDeleteSomething();
+  }
+}, `${action} at "<p>abc[<span contenteditable=\"false\">def</span>]ghi</p>"`);
+
+// The table structure shouldn't be modified when deleting cell contents,
+// in this case, getTargetRanges() should return multiple ranges in each
+// cell?
+promise_test(async () => {
+  initializeTest("<table><tr><td>abc</td><td>def</td></tr></table>");
+  let abc = gEditor.querySelector("td").firstChild;
+  let def = gEditor.querySelector("td + td").firstChild;
+  gSelection.setBaseAndExtent(abc, 2, def, 1);
+  await run();
+  assert_equals(gEditor.innerHTML, `<table><tbody><tr><td>ab${insertedHTML}</td><td>ef</td></tr></tbody></table>`);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 2,
+    endContainer: def,
+    endOffset: 1,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<table><tr><td>ab[c</td><td>d]ef</td></tr></table>"`);
+
+promise_test(async () => {
+  initializeTest("<table><tr><td>abc</td><td>def</td></tr><tr><td>ghi</td><td>jkl</td></tr></table>");
+  let abc = gEditor.querySelector("td").firstChild;
+  let jkl = gEditor.querySelector("tr + tr > td + td").firstChild;
+  gSelection.setBaseAndExtent(abc, 2, jkl, 1);
+  await run();
+  assert_in_array(gEditor.innerHTML, [`<table><tbody><tr><td>ab${insertedHTML}</td><td></td></tr><tr><td></td><td>kl</td></tr></tbody></table>`,
+                                      `<table><tbody><tr><td>ab${insertedHTML}</td><td><br></td></tr><tr><td><br></td><td>kl</td></tr></tbody></table>`]);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 2,
+    endContainer: jkl,
+    endOffset: 1,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<table><tr><td>ab[c</td><td>def</td></tr><tr><td>ghi</td><td>j]kl</td></tr></table>"`);
+
+promise_test(async () => {
+  initializeTest("<table><tr><td>abc</td><td>def</td></tr></table><table><tr><td>ghi</td><td>jkl</td></tr></table>");
+  let abc = gEditor.querySelector("td").firstChild;
+  let jkl = gEditor.querySelector("table + table td + td").firstChild;
+  gSelection.setBaseAndExtent(abc, 2, jkl, 1);
+  await run();
+  assert_in_array(gEditor.innerHTML, [`<table><tbody><tr><td>ab${insertedHTML}</td><td></td></tr></tbody></table><table><tbody><tr><td></td><td>kl</td></tr></tbody></table>`,
+                                      `<table><tbody><tr><td>ab${insertedHTML}</td><td><br></td></tr></tbody></table><table><tbody><tr><td><br></td><td>kl</td></tr></tbody></table>`]);
+  checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+    startContainer: abc,
+    startOffset: 2,
+    endContainer: jkl,
+    endOffset: 1,
+  });
+  checkGetTargetRangesOfInputOnDeleteSomething();
+}, `${action} at "<table><tr><td>ab[c</td><td>def</td></tr></table><table><tr><td>ghi</td><td>j]kl</td></tr></table>"`);
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges.js b/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges.js
new file mode 100644
index 0000000..4d09c4d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges.js
@@ -0,0 +1,223 @@
+"use strict";
+
+const kBackspaceKey = "\uE003";
+const kDeleteKey = "\uE017";
+const kArrowRight = "\uE014";
+const kArrowLeft = "\uE012";
+const kShift = "\uE008";
+const kMeta = "\uE03d";
+const kControl = "\uE009";
+const kAlt = "\uE00A";
+const kKeyA = "a";
+
+const kImgSrc =
+  "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==";
+
+let gSelection = getSelection();
+let gEditor = document.querySelector("div[contenteditable]");
+let gBeforeinput = [];
+let gInput = [];
+gEditor.addEventListener("beforeinput", e => {
+  // NOTE: Blink makes `getTargetRanges()` return empty range after propagation,
+  //       but this test wants to check the result during propagation.
+  //       Therefore, we need to cache the result, but will assert if
+  //       `getTargetRanges()` returns different ranges after checking the
+  //       cached ranges.
+  e.cachedRanges = e.getTargetRanges();
+  gBeforeinput.push(e);
+});
+gEditor.addEventListener("input", e => {
+  e.cachedRanges = e.getTargetRanges();
+  gInput.push(e);
+});
+
+function initializeTest(aInnerHTML) {
+  gEditor.innerHTML = aInnerHTML;
+  gEditor.focus();
+  gBeforeinput = [];
+  gInput = [];
+}
+
+function getRangeDescription(range) {
+  function getNodeDescription(node) {
+    if (!node) {
+      return "null";
+    }
+    switch (node.nodeType) {
+      case Node.TEXT_NODE:
+      case Node.COMMENT_NODE:
+      case Node.CDATA_SECTION_NODE:
+        return `${node.nodeName} "${node.data}"`;
+      case Node.ELEMENT_NODE:
+        return `<${node.nodeName.toLowerCase()}>`;
+      default:
+        return `${node.nodeName}`;
+    }
+  }
+  if (range === null) {
+    return "null";
+  }
+  if (range === undefined) {
+    return "undefined";
+  }
+  return range.startContainer == range.endContainer &&
+    range.startOffset == range.endOffset
+    ? `(${getNodeDescription(range.startContainer)}, ${range.startOffset})`
+    : `(${getNodeDescription(range.startContainer)}, ${
+        range.startOffset
+      }) - (${getNodeDescription(range.endContainer)}, ${range.endOffset})`;
+}
+
+function getArrayOfRangesDescription(arrayOfRanges) {
+  if (arrayOfRanges === null) {
+    return "null";
+  }
+  if (arrayOfRanges === undefined) {
+    return "undefined";
+  }
+  if (!Array.isArray(arrayOfRanges)) {
+    return "Unknown Object";
+  }
+  if (arrayOfRanges.length === 0) {
+    return "[]";
+  }
+  let result = "[";
+  for (let range of arrayOfRanges) {
+    result += `{${getRangeDescription(range)}},`;
+  }
+  result += "]";
+  return result;
+}
+
+function sendDeleteKey(modifier) {
+  if (!modifier) {
+    return new test_driver.Actions()
+      .keyDown(kDeleteKey)
+      .keyUp(kDeleteKey)
+      .send();
+  }
+  return new test_driver.Actions()
+    .keyDown(modifier)
+    .keyDown(kDeleteKey)
+    .keyUp(kDeleteKey)
+    .keyUp(modifier)
+    .send();
+}
+
+function sendBackspaceKey(modifier) {
+  if (!modifier) {
+    return new test_driver.Actions()
+      .keyDown(kBackspaceKey)
+      .keyUp(kBackspaceKey)
+      .send();
+  }
+  return new test_driver.Actions()
+    .keyDown(modifier)
+    .keyDown(kBackspaceKey)
+    .keyUp(kBackspaceKey)
+    .keyUp(modifier)
+    .send();
+}
+
+function sendKeyA() {
+  return new test_driver.Actions()
+    .keyDown(kKeyA)
+    .keyUp(kKeyA)
+    .send();
+}
+
+function sendArrowLeftKey() {
+  return new test_driver.Actions()
+    .keyDown(kArrowLeft)
+    .keyUp(kArrowLeft)
+    .send();
+}
+
+function sendArrowRightKey() {
+  return new test_driver.Actions()
+    .keyDown(kArrowRight)
+    .keyUp(kArrowRight)
+    .send();
+}
+
+function checkGetTargetRangesKeepReturningSameValue(event) {
+  // https://github.com/w3c/input-events/issues/114
+  assert_equals(
+    getArrayOfRangesDescription(event.getTargetRanges()),
+    getArrayOfRangesDescription(event.cachedRanges),
+    `${event.type}.getTargetRanges() should keep returning the same array of ranges even after its propagation finished`
+  );
+}
+
+function checkGetTargetRangesOfBeforeinputOnDeleteSomething(expectedRange) {
+  assert_equals(
+    gBeforeinput.length,
+    1,
+    "One beforeinput event should be fired if the key operation deletes something"
+  );
+  assert_true(
+    Array.isArray(gBeforeinput[0].cachedRanges),
+    "gBeforeinput[0].getTargetRanges() should return an array of StaticRange instances during propagation"
+  );
+  // Before checking the length of array of ranges, we should check the first
+  // range first because the first range data is more important than whether
+  // there are additional unexpected ranges.
+  if (gBeforeinput[0].cachedRanges.length > 0) {
+    assert_equals(
+      getRangeDescription(gBeforeinput[0].cachedRanges[0]),
+      getRangeDescription(expectedRange),
+      `gBeforeinput[0].getTargetRanges() should return expected range (inputType is "${gBeforeinput[0].inputType}")`
+    );
+    assert_equals(
+      gBeforeinput[0].cachedRanges.length,
+      1,
+      "gBeforeinput[0].getTargetRanges() should return one range within an array"
+    );
+  }
+  assert_equals(
+    gBeforeinput[0].cachedRanges.length,
+    1,
+    "One range should be returned from getTargetRanges() when the key operation deletes something"
+  );
+  checkGetTargetRangesKeepReturningSameValue(gBeforeinput[0]);
+}
+
+function checkGetTargetRangesOfInputOnDeleteSomething() {
+  assert_equals(
+    gInput.length,
+    1,
+    "One input event should be fired if the key operation deletes something"
+  );
+  // https://github.com/w3c/input-events/issues/113
+  assert_true(
+    Array.isArray(gInput[0].cachedRanges),
+    "gInput[0].getTargetRanges() should return an array of StaticRange instances during propagation"
+  );
+  assert_equals(
+    gInput[0].cachedRanges.length,
+    0,
+    "gInput[0].getTargetRanges() should return empty array during propagation"
+  );
+  checkGetTargetRangesKeepReturningSameValue(gInput[0]);
+}
+
+function checkGetTargetRangesOfInputOnDoNothing() {
+  assert_equals(
+    gInput.length,
+    0,
+    "input event shouldn't be fired when the key operation does not cause modifying the DOM tree"
+  );
+}
+
+function checkBeforeinputAndInputEventsOnNOOP() {
+  assert_equals(
+    gBeforeinput.length,
+    0,
+    "beforeinput event shouldn't be fired when the key operation does not cause modifying the DOM tree"
+  );
+  assert_equals(
+    gInput.length,
+    0,
+    "input event shouldn't be fired when the key operation does not cause modifying the DOM tree"
+  );
+}
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-image/getusermedia.https.html b/third_party/blink/web_tests/external/wpt/mediacapture-image/getusermedia.https.html
new file mode 100644
index 0000000..033501c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-image/getusermedia.https.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>getUserMedia</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+"use strict";
+
+  [
+    { video: { pan: { min: 1 } } },
+    { video: { pan: { max: 1 } } },
+    { video: { pan: { exact: 1 } } },
+    { video: { tilt: { min: 1 } } },
+    { video: { tilt: { max: 1 } } },
+    { video: { tilt: { exact: 1 } } },
+    { video: { zoom: { min: 1 } } },
+    { video: { zoom: { max: 1 } } },
+    { video: { zoom: { exact: 1 } } }
+  ].forEach(constraints =>
+    promise_test(t => {
+      const promise = navigator.mediaDevices.getUserMedia(constraints);
+      return promise_rejects_js(t, TypeError, promise);
+    }, `getUserMedia(${JSON.stringify(constraints)}) must fail with TypeError`)
+  );
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/commitStyles-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/commitStyles-expected.txt
deleted file mode 100644
index aed99e6..0000000
--- a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/commitStyles-expected.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-This is a testharness.js-based test.
-PASS Commits styles
-PASS Commits styles for an animation that has been removed
-PASS Commits shorthand styles
-PASS Commits logical properties
-FAIL Commits logical properties as physical properties assert_equals: expected "20px" but got "10px"
-PASS Commits values calculated mid-interval
-PASS Commits variable references as their computed values
-PASS Commits custom variables
-PASS Commits em units as pixel values
-PASS Commits relative line-height
-PASS Commits transforms
-PASS Commits transforms as a transform list
-PASS Commits matrix-interpolated relative transforms
-PASS Commits "none" transform
-PASS Commits the intermediate value of an animation in the middle of stack
-PASS Commit composites on top of the underlying value
-PASS Triggers mutation observers when updating style
-PASS Does NOT trigger mutation observers when the change to style is redundant
-PASS Throws if the target element is a pseudo element
-PASS Throws if the target element is not something with a style attribute
-PASS Throws if the target effect is display:none
-PASS Throws if the target effect's ancestor is display:none
-PASS Treats display:contents as rendered
-PASS Treats display:contents in a display:none subtree as not rendered
-PASS Throws if the target effect is disconnected
-PASS Checks the pseudo element condition before the not rendered condition
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/timing-model/timelines/update-and-send-events-replacement-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/timing-model/timelines/update-and-send-events-replacement-expected.txt
index f84fd411..8c99926e 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/timing-model/timelines/update-and-send-events-replacement-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/timing-model/timelines/update-and-send-events-replacement-expected.txt
@@ -17,9 +17,9 @@
 PASS Removes an animation after updating its effect to one with different properties
 PASS Removes an animation when another animation uses a shorthand
 PASS Removes an animation that uses a shorthand
-FAIL Removes an animation by another animation using logical properties assert_equals: expected "removed" but got "active"
-FAIL Removes an animation using logical properties assert_equals: expected "removed" but got "active"
-FAIL Removes an animation by another animation using logical properties after updating the context assert_equals: expected "removed" but got "active"
+PASS Removes an animation by another animation using logical properties
+PASS Removes an animation using logical properties
+PASS Removes an animation by another animation using logical properties after updating the context
 PASS Removes an animation after updating another animation's effect's target
 PASS Removes an animation after updating its effect's target
 PASS Removes an animation after updating another animation's effect to one with a different target
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt
index 312b0a7d..edbaad6 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt
@@ -6,8 +6,8 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [45, 0, 60, 110],
-        [0, 0, 55, 110]
+        [45, 0, 60, 90],
+        [0, 0, 55, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-reference-invalidation-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-reference-invalidation-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-reference-invalidation-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-reference-invalidation-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-add-to-document-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-add-to-document-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-add-to-document-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-add-to-document-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-property-change-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-property-change-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-property-change-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-property-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-style-change-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-style-change-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-style-change-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-style-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/css-rule-hover-highlights-selectors.js b/third_party/blink/web_tests/http/tests/devtools/elements/css-rule-hover-highlights-selectors.js
index c7064da6e..0ddcbf8e 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/css-rule-hover-highlights-selectors.js
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/css-rule-hover-highlights-selectors.js
@@ -97,12 +97,15 @@
 
   function drawHighlightProxy() {
     window._highlightsForTest = [];
-    var oldDrawHighlight = drawHighlight;
-    drawHighlight = proxy;
+    var oldDispatch = dispatch;
+    dispatch = proxy;
 
-    function proxy(highlight, context) {
-      window._highlightsForTest.push(highlight);
-      oldDrawHighlight(highlight, context);
+    function proxy(message) {
+      const functionName = message[0];
+      if (functionName === 'drawHighlight') {
+        window._highlightsForTest.push(message[1]);
+      }
+      oldDispatch(message);
     }
   }
 
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt
index 443d522..0e209ad 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt
@@ -6,8 +6,8 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [45, 0, 60, 110],
-        [0, 0, 55, 110]
+        [45, 0, 60, 90],
+        [0, 0, 55, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-reference-invalidation-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-reference-invalidation-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-reference-invalidation-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-reference-invalidation-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-add-to-document-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-add-to-document-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-add-to-document-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-add-to-document-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-property-change-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-property-change-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-property-change-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-property-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-style-change-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-style-change-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-style-change-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-style-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/window-postmessage-clone-deep-array-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/window-postmessage-clone-deep-array-expected.txt
deleted file mode 100644
index 6cdf2ba8..0000000
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/window-postmessage-clone-deep-array-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Tests that we support cloning deep(ish) arrays.
-FAIL: 'postMessage(deepArray)' should not throw but threw RangeError: Maximum call stack size exceeded
-PASS: eventData is done of type string
-
diff --git a/third_party/blink/web_tests/webshare/share-error.html b/third_party/blink/web_tests/webshare/share-error.html
index 566337e..a9fe0c3 100644
--- a/third_party/blink/web_tests/webshare/share-error.html
+++ b/third_party/blink/web_tests/webshare/share-error.html
@@ -63,4 +63,26 @@
   });
 }, 'share consumes user activation');
 
+share_test(mock => {
+  mock.pushShareResult('the title', 'the message', 'https://example.com/',
+                       blink.mojom.ShareError.CANCELED);
+  return callWithKeyDown(async () => {
+    const data = {
+          title: 'the title',
+          text: 'the message',
+          url: 'https://example.com/'
+    };
+    const first = navigator.share(data);
+    await assertRejectsWithError(
+          navigator.share(data),
+          'InvalidStateError');
+    await assertRejectsWithError(
+          navigator.share(data),
+          'InvalidStateError');
+    return assertRejectsWithError(
+          first,
+          'AbortError');
+  } );
+}, 'only one share at a time');
+
 </script>
diff --git a/tools/android/dependency_analysis/print_class_dependencies.py b/tools/android/dependency_analysis/print_class_dependencies.py
index c815df3d..aaa1377 100755
--- a/tools/android/dependency_analysis/print_class_dependencies.py
+++ b/tools/android/dependency_analysis/print_class_dependencies.py
@@ -53,20 +53,17 @@
         return chrome_names.shorten_build_target(build_target)
 
 
-def print_with_indent(indent: int, message: str,
-                      bullet_point: str = '') -> None:
-    indents = ' ' * indent
-    bullet_point_text = f'{bullet_point} ' if bullet_point else ''
-    print(f'{indents}{bullet_point_text}{message}')
-
-
 def is_allowed_dependency(build_target: str) -> bool:
     return any(build_target.startswith(p) for p in ALLOWED_PREFIXES)
 
 
-def print_class_nodes_grouped_by_target(
-        class_nodes: List[class_dependency.JavaClass], print_mode: PrintMode,
-        bullet_point: str, ignore_modularized: bool):
+def print_class_nodes(class_nodes: List[class_dependency.JavaClass],
+                      print_mode: PrintMode, class_name: str, direction: str):
+    ignore_modularized = direction == OUTBOUND and print_mode.ignore_modularized
+    bullet_point = '<-' if direction == INBOUND else '->'
+
+    print_backlog: List[Tuple[int, str]] = []
+
     # TODO(crbug.com/1124836): This is not quite correct because
     # sets considered equal can be converted to different strings. Fix this by
     # making JavaClass.build_targets return a List instead of a Set.
@@ -87,25 +84,30 @@
                 get_build_target_name_to_display(target, print_mode)
                 for target in class_node.build_targets
             ]
-            print_with_indent(4, f'[{", ".join(build_target_names)}]')
+            build_target_names_string = ", ".join(build_target_names)
+            print_backlog.append((4, f'[{build_target_names_string}]'))
             last_build_target = build_target
-        print_with_indent(
-            8, get_class_name_to_display(class_node.name, print_mode),
-            bullet_point)
+        display_name = get_class_name_to_display(class_node.name, print_mode)
+        print_backlog.append((8, f'{bullet_point} {display_name}'))
+
+    # Print header
     if ignore_modularized:
-        if suspect_dependencies:
-            print(f'{suspect_dependencies} dependencies above may need to be '
-                  'broken, ignored others.')
+        cleared = len(class_nodes) - suspect_dependencies
+        print(
+            f'{suspect_dependencies} outbound dependencies from {class_name} '
+            f'may need to be broken (omitted {cleared} cleared dependencies):')
+    else:
+        if direction == INBOUND:
+            print(
+                f'{len(class_nodes)} inbound dependencies into {class_name}:')
         else:
-            print('No suspect dependencies, ignored all.')
+            print(
+                f'{len(class_nodes)} outbound dependencies from {class_name}:')
 
-
-def print_class_nodes(class_nodes: List[class_dependency.JavaClass],
-                      print_mode: PrintMode, direction: str):
-    ignore_modularized = direction == OUTBOUND and print_mode.ignore_modularized
-    bullet_point = '<-' if direction == INBOUND else '->'
-    print_class_nodes_grouped_by_target(class_nodes, print_mode, bullet_point,
-                                        ignore_modularized)
+    # Print build targets and dependencies
+    for indent, message in print_backlog:
+        indents = ' ' * indent
+        print(f'{indents}{message}')
 
 
 def print_class_dependencies_for_key(
@@ -116,17 +118,12 @@
     class_name = get_class_name_to_display(node.name, print_mode)
 
     if print_mode.inbound:
-        print(
-            f'{len(node.inbound)} inbound dependency(ies) into {class_name}:')
         print_class_nodes(graph.sorted_nodes_by_name(node.inbound), print_mode,
-                          INBOUND)
+                          class_name, INBOUND)
 
     if print_mode.outbound:
-        print(
-            f'{len(node.outbound)} outbound dependency(ies) from {class_name}:'
-        )
         print_class_nodes(graph.sorted_nodes_by_name(node.outbound),
-                          print_mode, OUTBOUND)
+                          print_mode, class_name, OUTBOUND)
 
 
 def main():
@@ -200,7 +197,6 @@
             fully_qualified_class_name = valid_keys[0]
             class_name = get_class_name_to_display(fully_qualified_class_name,
                                                    print_mode)
-            print(f'Printing class dependencies for {class_name}:')
             print_class_dependencies_for_key(class_graph,
                                              fully_qualified_class_name,
                                              print_mode)
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 7295aee..d784086c 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -29323,36 +29323,30 @@
   <int value="3540" label="IdentifiabilityStudyReserved3540"/>
   <int value="3541" label="V8WheelEvent_DeltaMode_AttributeGetter"/>
   <int value="3542" label="IdentifiabilityStudyReserved3542"/>
-  <int value="3543" label="V8HIDDevice_ProductId_AttributeGetter"/>
-  <int value="3544" label="V8HIDDevice_ProductName_AttributeGetter"/>
-  <int value="3545" label="V8HIDDevice_VendorId_AttributeGetter"/>
+  <int value="3543" label="IdentifiabilityStudyReserved3543"/>
+  <int value="3544" label="IdentifiabilityStudyReserved3544"/>
+  <int value="3545" label="IdentifiabilityStudyReserved3545"/>
   <int value="3546"
       label="V8BeforeInstallPromptEvent_Platforms_AttributeGetter"/>
-  <int value="3547" label="V8HIDReportItem_HasNull_AttributeGetter"/>
-  <int value="3548" label="V8HIDReportItem_IsAbsolute_AttributeGetter"/>
-  <int value="3549" label="V8HIDReportItem_IsArray_AttributeGetter"/>
-  <int value="3550" label="V8HIDReportItem_IsRange_AttributeGetter"/>
-  <int value="3551" label="V8HIDReportItem_LogicalMaximum_AttributeGetter"/>
-  <int value="3552" label="V8HIDReportItem_LogicalMinimum_AttributeGetter"/>
-  <int value="3553" label="V8HIDReportItem_PhysicalMaximum_AttributeGetter"/>
-  <int value="3554" label="V8HIDReportItem_PhysicalMinimum_AttributeGetter"/>
-  <int value="3555" label="V8HIDReportItem_ReportCount_AttributeGetter"/>
-  <int value="3556" label="V8HIDReportItem_ReportSize_AttributeGetter"/>
-  <int value="3557" label="V8HIDReportItem_UnitExponent_AttributeGetter"/>
-  <int value="3558"
-      label="V8HIDReportItem_UnitFactorCurrentExponent_AttributeGetter"/>
-  <int value="3559"
-      label="V8HIDReportItem_UnitFactorLengthExponent_AttributeGetter"/>
-  <int value="3560"
-      label="V8HIDReportItem_UnitFactorLuminousIntensityExponent_AttributeGetter"/>
-  <int value="3561"
-      label="V8HIDReportItem_UnitFactorMassExponent_AttributeGetter"/>
-  <int value="3562"
-      label="V8HIDReportItem_UnitFactorTemperatureExponent_AttributeGetter"/>
-  <int value="3563"
-      label="V8HIDReportItem_UnitFactorTimeExponent_AttributeGetter"/>
-  <int value="3564" label="V8HIDReportItem_UsageMaximum_AttributeGetter"/>
-  <int value="3565" label="V8HIDReportItem_UsageMinimum_AttributeGetter"/>
+  <int value="3547" label="IdentifiabilityStudyReserved3547"/>
+  <int value="3548" label="IdentifiabilityStudyReserved3548"/>
+  <int value="3549" label="IdentifiabilityStudyReserved3549"/>
+  <int value="3550" label="IdentifiabilityStudyReserved3550"/>
+  <int value="3551" label="IdentifiabilityStudyReserved3551"/>
+  <int value="3552" label="IdentifiabilityStudyReserved3552"/>
+  <int value="3553" label="IdentifiabilityStudyReserved3553"/>
+  <int value="3554" label="IdentifiabilityStudyReserved3554"/>
+  <int value="3555" label="IdentifiabilityStudyReserved3555"/>
+  <int value="3556" label="IdentifiabilityStudyReserved3556"/>
+  <int value="3557" label="IdentifiabilityStudyReserved3557"/>
+  <int value="3558" label="IdentifiabilityStudyReserved3558"/>
+  <int value="3559" label="IdentifiabilityStudyReserved3559"/>
+  <int value="3560" label="IdentifiabilityStudyReserved3560"/>
+  <int value="3561" label="IdentifiabilityStudyReserved3561"/>
+  <int value="3562" label="IdentifiabilityStudyReserved3562"/>
+  <int value="3563" label="IdentifiabilityStudyReserved3563"/>
+  <int value="3564" label="IdentifiabilityStudyReserved3564"/>
+  <int value="3565" label="IdentifiabilityStudyReserved3565"/>
   <int value="3566" label="V8BaseAudioContext_SampleRate_AttributeGetter"/>
   <int value="3567" label="IdentifiabilityStudyReserved3567"/>
   <int value="3568" label="IdentifiabilityStudyReserved3568"/>
@@ -62167,6 +62161,7 @@
       label="IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES"/>
   <int value="110" label="IDC_CONTENT_CONTEXT_GENERATE_QR_CODE"/>
   <int value="111" label="IDC_CONTENT_CLIPBOARD_HISTORY_MENU"/>
+  <int value="112" label="IDC_CONTENT_CONTEXT_COPYLINKTOTEXT"/>
 </enum>
 
 <enum name="ReopenTabPromoStepAtDismissal">
diff --git a/tools/metrics/histograms/histograms_xml/ash/histograms.xml b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
index 0ce87ee..55c358d6 100644
--- a/tools/metrics/histograms/histograms_xml/ash/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
@@ -184,6 +184,29 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown"
+    units="Items Shown" expires_after="2021-09-01">
+  <owner>andrewxu@chromium.org</owner>
+  <owner>newcomer@chromium.org</owner>
+  <summary>
+    The number of clipboard history items shown in the clipboard history
+    contextual menu. Recorded when the clipboard history contextual menu model
+    is built.
+  </summary>
+</histogram>
+
+<histogram name="Ash.ClipboardHistory.ContextMenu.UserJourneyTime" units="ms"
+    expires_after="2021-09-01">
+  <owner>andrewxu@chromium.org</owner>
+  <owner>newcomer@chromium.org</owner>
+  <summary>
+    The length of the user journey of the context menu version of clipboard
+    history. Measured as time before the menu is constructed, to time of the
+    menu being closed. Recorded every time the menu is closed, regardless of
+    whether the user selected one of the menu options.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Ash.ContextualNudgeDismissContext"
     enum="ContextualNudgeDismissContext" expires_after="M86">
 <!-- Name completed by histogram_suffixes name="ContextualNudgesNames" -->
diff --git a/tools/metrics/histograms/histograms_xml/extensions/histograms.xml b/tools/metrics/histograms/histograms_xml/extensions/histograms.xml
index 52cdf11..8af3433 100644
--- a/tools/metrics/histograms/histograms_xml/extensions/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/extensions/histograms.xml
@@ -1573,6 +1573,18 @@
   </summary>
 </histogram>
 
+<histogram name="Extensions.ForceInstalledReadyTime" units="ms"
+    expires_after="2021-01-24">
+  <owner>snijhara@google.com</owner>
+  <owner>burunduk@chromium.org</owner>
+  <owner>swapnilgupta@google.com</owner>
+  <owner>managed-devices@google.com</owner>
+  <summary>
+    The amount of time elapsed for enterprise policy forced extensions to become
+    ready for use.
+  </summary>
+</histogram>
+
 <histogram
     name="Extensions.ForceInstalledSessionsWithNonMisconfigurationFailureOccured"
     enum="Boolean" expires_after="2021-01-24">
diff --git a/tools/metrics/histograms/histograms_xml/payment/histograms.xml b/tools/metrics/histograms/histograms_xml/payment/histograms.xml
index 3fb4d9d..510fc01 100644
--- a/tools/metrics/histograms/histograms_xml/payment/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/payment/histograms.xml
@@ -22,7 +22,7 @@
 <histograms>
 
 <histogram name="PaymentRequest.CheckoutFunnel"
-    enum="PaymentRequestCheckoutFunnelSteps" expires_after="M90">
+    enum="PaymentRequestCheckoutFunnelSteps" expires_after="2021-08-01">
   <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
@@ -35,14 +35,14 @@
 </histogram>
 
 <histogram name="PaymentRequest.CheckoutFunnel.Aborted"
-    enum="PaymentRequestAbortReason" expires_after="M85">
+    enum="PaymentRequestAbortReason" expires_after="2021-08-01">
   <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>The reason that lead to an abort of the Payment Request.</summary>
 </histogram>
 
 <histogram name="PaymentRequest.CheckoutFunnel.NoShow"
-    enum="PaymentRequestNoShowReason" expires_after="2020-05-24">
+    enum="PaymentRequestNoShowReason" expires_after="2021-08-01">
   <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
@@ -51,7 +51,7 @@
 </histogram>
 
 <histogram name="PaymentRequest.Events" units="bitfield value"
-    expires_after="2021-01-31">
+    expires_after="2021-08-01">
   <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
@@ -61,8 +61,8 @@
 </histogram>
 
 <histogram name="PaymentRequest.JourneyLoggerHasRecorded" enum="Boolean"
-    expires_after="2021-01-31">
-  <owner>sahel@chromium.org</owner>
+    expires_after="2021-08-01">
+  <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
     Whether a journey logger has recorded an events bit field or not.
@@ -70,8 +70,8 @@
 </histogram>
 
 <histogram name="PaymentRequest.JourneyLoggerHasRecordedMultipleTimes"
-    enum="Boolean" expires_after="M85">
-  <owner>sahel@chromium.org</owner>
+    enum="Boolean" expires_after="2021-08-01">
+  <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
     When a journey logger attempts to record multiple events bit fields.
@@ -79,8 +79,8 @@
 </histogram>
 
 <histogram name="PaymentRequest.MissingContactFields"
-    enum="PaymentRequestMissingContactFields" expires_after="2021-03-01">
-  <owner>sahel@chromium.org</owner>
+    enum="PaymentRequestMissingContactFields" expires_after="2021-08-01">
+  <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
     A bitfield representing different missing fields of the contact section in
@@ -92,8 +92,8 @@
 </histogram>
 
 <histogram name="PaymentRequest.MissingPaymentFields"
-    enum="PaymentRequestMissingPaymentFields" expires_after="2021-03-01">
-  <owner>sahel@chromium.org</owner>
+    enum="PaymentRequestMissingPaymentFields" expires_after="2021-08-01">
+  <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
     A bitfield representing different missing fields of the payment info section
@@ -105,8 +105,8 @@
 </histogram>
 
 <histogram name="PaymentRequest.MissingShippingFields"
-    enum="PaymentRequestMissingShippingFields" expires_after="2021-03-01">
-  <owner>sahel@chromium.org</owner>
+    enum="PaymentRequestMissingShippingFields" expires_after="2021-08-01">
+  <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
     A bitfield representing different missing fields of the shipping section in
@@ -118,7 +118,7 @@
 </histogram>
 
 <histogram name="PaymentRequest.NumberOfSuggestionsShown" units="units"
-    expires_after="2021-01-24">
+    expires_after="2021-08-01">
   <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
@@ -127,8 +127,8 @@
 </histogram>
 
 <histogram name="PaymentRequest.RefetchIconForInstalledApp" enum="Boolean"
-    expires_after="2021-06-24">
-  <owner>sahel@chromium.org</owner>
+    expires_after="2021-08-01">
+  <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
     True when chrome crawls to refetch the missing icon of an already installed
@@ -138,7 +138,7 @@
 
 <histogram
     name="PaymentRequest.SecurePaymentConfirmationCredentialIdSizeInBytes"
-    units="bytes" expires_after="M93">
+    units="bytes" expires_after="2021-08-01">
   <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
@@ -148,15 +148,15 @@
 </histogram>
 
 <histogram name="PaymentRequest.ServiceWorkerStatusCodeTimeout" enum="Boolean"
-    expires_after="2021-05-24">
-  <owner>sahel@chromium.org</owner>
+    expires_after="2021-08-01">
+  <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>True when a service worker times out 5 mins after request.</summary>
 </histogram>
 
 <histogram name="PaymentRequest.TimeToCheckout.Completed" units="ms"
-    expires_after="2021-01-31">
-  <owner>sahel@chromium.org</owner>
+    expires_after="2021-08-01">
+  <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
     Records the time between a payment request .show() and its completion.
@@ -164,10 +164,10 @@
 </histogram>
 
 <histogram name="PaymentRequest.TimeToCheckout.Completed.Shown" units="ms"
-    expires_after="2021-01-31">
+    expires_after="2021-08-01">
 <!-- Name completed by histogram_suffixes name="PaymentRequestCompletedInstrument" -->
 
-  <owner>sahel@chromium.org</owner>
+  <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
     Records the time between a payment request .show() and its completion when
@@ -176,10 +176,10 @@
 </histogram>
 
 <histogram name="PaymentRequest.TimeToCheckout.Completed.SkippedShow"
-    units="ms" expires_after="2021-01-31">
+    units="ms" expires_after="2021-08-01">
 <!-- Name completed by histogram_suffixes name="PaymentRequestCompletedInstrument" -->
 
-  <owner>sahel@chromium.org</owner>
+  <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
     Records the time between a payment request .show() and its completion when
@@ -188,8 +188,8 @@
 </histogram>
 
 <histogram name="PaymentRequest.TimeToCheckout.OtherAborted" units="ms"
-    expires_after="2021-01-31">
-  <owner>sahel@chromium.org</owner>
+    expires_after="2021-08-01">
+  <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
     Records the time between a payment request .show() and its termination by
@@ -198,10 +198,10 @@
 </histogram>
 
 <histogram name="PaymentRequest.TimeToCheckout.UserAborted" units="ms"
-    expires_after="2021-01-31">
+    expires_after="2021-08-01">
 <!-- Name completed by histogram_suffixes name="PaymentRequestPaymentSheetShowStatus" -->
 
-  <owner>sahel@chromium.org</owner>
+  <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
     Records the time between a payment request .show() and its termination by
@@ -210,8 +210,8 @@
 </histogram>
 
 <histogram name="PaymentRequest.TransactionAmount.Completed"
-    enum="PaymentRequestTransactionSize" expires_after="2021-01-24">
-  <owner>sahel@chromium.org</owner>
+    enum="PaymentRequestTransactionSize" expires_after="2021-08-01">
+  <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
     Records the transaction amounts completed using payment request API after
@@ -220,8 +220,8 @@
 </histogram>
 
 <histogram name="PaymentRequest.TransactionAmount.Triggered"
-    enum="PaymentRequestTransactionSize" expires_after="2021-01-31">
-  <owner>sahel@chromium.org</owner>
+    enum="PaymentRequestTransactionSize" expires_after="2021-08-01">
+  <owner>danyao@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
     Records the transaction amounts triggered using payment request API after
diff --git a/tools/metrics/histograms/histograms_xml/service/histograms.xml b/tools/metrics/histograms/histograms_xml/service/histograms.xml
index ada65b3..336ee7f 100644
--- a/tools/metrics/histograms/histograms_xml/service/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/service/histograms.xml
@@ -22,8 +22,9 @@
 <histograms>
 
 <histogram name="ServiceWorker.AbortPaymentEvent.Time" units="ms"
-    expires_after="M85">
-  <owner>nhiroki@chromium.org</owner>
+    expires_after="2021-08-01">
+  <owner>danyao@chromium.org</owner>
+  <owner>web-payments-team@google.com</owner>
   <owner>jinho.bang@samsung.com</owner>
   <summary>
     The time taken between dispatching an AbortPaymentEvent to a Service Worker
@@ -169,8 +170,9 @@
 </histogram>
 
 <histogram name="ServiceWorker.CanMakePaymentEvent.Time" units="ms"
-    expires_after="2020-04-19">
-  <owner>nhiroki@chromium.org</owner>
+    expires_after="2021-08-01">
+  <owner>danyao@chromium.org</owner>
+  <owner>web-payments-team@google.com</owner>
   <owner>jinho.bang@samsung.com</owner>
   <summary>
     The time taken between dispatching an CanMakePaymentEvent to a Service
@@ -661,8 +663,9 @@
 </histogram>
 
 <histogram name="ServiceWorker.PaymentRequestEvent.Time" units="ms"
-    expires_after="M81">
-  <owner>nhiroki@chromium.org</owner>
+    expires_after="2021-08-01">
+  <owner>danyao@chromium.org</owner>
+  <owner>web-payments-team@google.com</owner>
   <owner>jinho.bang@samsung.com</owner>
   <summary>
     The time taken between dispatching an PaymentRequestEvent to a Service
diff --git a/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm b/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm
index 382b23ab..06ea2a1 100644
--- a/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm
+++ b/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm
@@ -12,6 +12,7 @@
 #include "ui/accelerated_widget_mac/ca_renderer_layer_tree.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/mac/io_surface.h"
 #include "ui/gl/ca_renderer_layer_params.h"
 #include "ui/gl/gl_image_io_surface.h"
@@ -457,9 +458,10 @@
 
       // Validate the clip and sorting context layer.
       EXPECT_TRUE([clip_and_sorting_layer masksToBounds]);
-      EXPECT_EQ(gfx::ConvertRectToDIP(properties.scale_factor,
-                                      gfx::Rect(properties.clip_rect.size())),
-                gfx::Rect([clip_and_sorting_layer bounds]));
+      EXPECT_EQ(
+          gfx::ToFlooredRectDeprecated(gfx::ConvertRectToDips(
+              gfx::Rect(properties.clip_rect.size()), properties.scale_factor)),
+          gfx::Rect([clip_and_sorting_layer bounds]));
       EXPECT_EQ(gfx::ToFlooredPoint(gfx::ConvertPointToDips(
                     properties.clip_rect.origin(), properties.scale_factor)),
                 gfx::Point([clip_and_sorting_layer position]));
@@ -484,9 +486,10 @@
       EXPECT_EQ(gfx::ToFlooredPoint(gfx::ConvertPointToDips(
                     properties.rect.origin(), properties.scale_factor)),
                 gfx::Point([content_layer position]));
-      EXPECT_EQ(gfx::ConvertRectToDIP(properties.scale_factor,
-                                      gfx::Rect(properties.rect.size())),
-                gfx::Rect([content_layer bounds]));
+      EXPECT_EQ(
+          gfx::ToFlooredRectDeprecated(gfx::ConvertRectToDips(
+              gfx::Rect(properties.rect.size()), properties.scale_factor)),
+          gfx::Rect([content_layer bounds]));
       EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]);
       EXPECT_EQ(properties.opacity, [content_layer opacity]);
       if ([content_layer respondsToSelector:(@selector(contentsScale))])
diff --git a/ui/compositor/dip_util.cc b/ui/compositor/dip_util.cc
index 62d0fcf0..e6f3854 100644
--- a/ui/compositor/dip_util.cc
+++ b/ui/compositor/dip_util.cc
@@ -23,11 +23,6 @@
 
 namespace ui {
 
-gfx::Rect ConvertRectToDIP(const Layer* layer,
-                           const gfx::Rect& rect_in_pixel) {
-  return gfx::ConvertRectToDIP(layer->device_scale_factor(), rect_in_pixel);
-}
-
 gfx::Rect ConvertRectToPixel(const Layer* layer,
                              const gfx::Rect& rect_in_dip) {
   return gfx::ConvertRectToPixel(layer->device_scale_factor(), rect_in_dip);
diff --git a/ui/compositor/dip_util.h b/ui/compositor/dip_util.h
index 1138043..7e9ac91 100644
--- a/ui/compositor/dip_util.h
+++ b/ui/compositor/dip_util.h
@@ -6,7 +6,6 @@
 #define UI_COMPOSITOR_DIP_UTIL_H_
 
 #include "ui/compositor/compositor_export.h"
-#include "ui/gfx/geometry/point_f.h"
 
 namespace gfx {
 class Rect;
@@ -15,11 +14,6 @@
 namespace ui {
 class Layer;
 
-// Utility functions that convert point/size/rect between
-// DIP and pixel coordinates system.
-COMPOSITOR_EXPORT gfx::Rect ConvertRectToDIP(
-    const Layer* layer,
-    const gfx::Rect& rect_in_pixel);
 COMPOSITOR_EXPORT gfx::Rect ConvertRectToPixel(
     const Layer* layer,
     const gfx::Rect& rect_in_dip);
diff --git a/ui/display/OWNERS b/ui/display/OWNERS
index c833c75..40316a6 100644
--- a/ui/display/OWNERS
+++ b/ui/display/OWNERS
@@ -1,6 +1,9 @@
 afakhry@chromium.org
+dcastagna@chromium.org
 dnicoara@chromium.org
-marcheu@chromium.org
 oshima@chromium.org
 
+# Legacy OWNER(s)
+marcheu@chromium.org
+
 # COMPONENT: UI>Systems>Display
diff --git a/ui/gfx/geometry/dip_util.cc b/ui/gfx/geometry/dip_util.cc
index 67a888a5..1cf202b 100644
--- a/ui/gfx/geometry/dip_util.cc
+++ b/ui/gfx/geometry/dip_util.cc
@@ -95,6 +95,23 @@
   return ScaleSize(size_in_dips, device_scale_factor);
 }
 
+RectF ConvertRectToDips(const Rect& rect_in_pixels, float device_scale_factor) {
+#if defined(OS_MAC)
+  // Device scale factor on MacOSX is always an integer.
+  DCHECK(IsIntegerInFloat(device_scale_factor));
+#endif
+  return ScaleRect(RectF(rect_in_pixels), 1.f / device_scale_factor);
+}
+
+RectF ConvertRectToDips(const RectF& rect_in_pixels,
+                        float device_scale_factor) {
+#if defined(OS_MAC)
+  // Device scale factor on MacOSX is always an integer.
+  DCHECK(IsIntegerInFloat(device_scale_factor));
+#endif
+  return ScaleRect(rect_in_pixels, 1.f / device_scale_factor);
+}
+
 Insets ConvertInsetsToDIP(float scale_factor,
                           const gfx::Insets& insets_in_pixel) {
 #if defined(OS_MAC)
@@ -106,17 +123,6 @@
   return insets_in_pixel.Scale(1.f / scale_factor);
 }
 
-Rect ConvertRectToDIP(float scale_factor, const Rect& rect_in_pixel) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(scale_factor));
-#endif
-  if (scale_factor == 1.f)
-    return rect_in_pixel;
-  return ToFlooredRectDeprecated(
-      ScaleRect(RectF(rect_in_pixel), 1.f / scale_factor));
-}
-
 Insets ConvertInsetsToPixel(float scale_factor,
                             const gfx::Insets& insets_in_dip) {
 #if defined(OS_MAC)
diff --git a/ui/gfx/geometry/dip_util.h b/ui/gfx/geometry/dip_util.h
index 1df9fae..df1d6bd0 100644
--- a/ui/gfx/geometry/dip_util.h
+++ b/ui/gfx/geometry/dip_util.h
@@ -13,6 +13,7 @@
 class Point;
 class PointF;
 class Rect;
+class RectF;
 class Size;
 class SizeF;
 
@@ -52,11 +53,14 @@
 GEOMETRY_EXPORT gfx::SizeF ConvertSizeToPixels(const gfx::SizeF& size_in_dips,
                                                float device_scale_factor);
 
+GEOMETRY_EXPORT gfx::RectF ConvertRectToDips(const gfx::Rect& rect_in_pixels,
+                                             float scale_factor);
+GEOMETRY_EXPORT gfx::RectF ConvertRectToDips(const gfx::RectF& rect_in_pixels,
+                                             float scale_factor);
+
 GEOMETRY_EXPORT gfx::Insets ConvertInsetsToDIP(
     float scale_factor,
     const gfx::Insets& insets_in_pixel);
-GEOMETRY_EXPORT gfx::Rect ConvertRectToDIP(float scale_factor,
-                                           const gfx::Rect& rect_in_pixel);
 
 GEOMETRY_EXPORT gfx::Insets ConvertInsetsToPixel(
     float scale_factor,
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index c582031..203e295 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -4,6 +4,7 @@
 
 visibility = [ "//ui/ozone/*" ]
 
+import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/linux/gtk/gtk.gni")
 import("//build/config/linux/pkg_config.gni")
 import("//gpu/vulkan/features.gni")
@@ -212,6 +213,11 @@
     ]
   }
 
+  # TODO(crbug.com/1052397): Rename chromeos_is_browser_only to is_lacros.
+  if (chromeos_is_browser_only) {
+    deps += [ "//chromeos/crosapi/cpp" ]
+  }
+
   defines = [ "OZONE_IMPLEMENTATION" ]
 
   if (use_wayland_gbm) {
diff --git a/ui/ozone/platform/wayland/host/DEPS b/ui/ozone/platform/wayland/host/DEPS
new file mode 100644
index 0000000..1741810
--- /dev/null
+++ b/ui/ozone/platform/wayland/host/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  # For Lacros.
+  "+chromeos/crosapi/cpp/crosapi_constants.h",
+]
diff --git a/ui/ozone/platform/wayland/host/wayland_screen.cc b/ui/ozone/platform/wayland/host/wayland_screen.cc
index fcebc07..9beaf27 100644
--- a/ui/ozone/platform/wayland/host/wayland_screen.cc
+++ b/ui/ozone/platform/wayland/host/wayland_screen.cc
@@ -46,8 +46,11 @@
     // crbug.com/1127558.
     if (format == gfx::BufferFormat::RGBA_8888)
       image_format_alpha_ = gfx::BufferFormat::RGBA_8888;
-    if (format == gfx::BufferFormat::RGBX_8888)
-      image_format_no_alpha_ = format;
+
+      // TODO(1128997): |image_format_no_alpha_| should use RGBX_8888 when it's
+      // available, but for some reason Chromium gets broken when it's used.
+      // Though,  we can import RGBX_8888 dma buffer to EGLImage successfully.
+      // Enable that back when the issue is resolved.
 #endif  // !BUILDFLAG(IS_LACROS)
 
     if (!image_format_alpha_ && format == gfx::BufferFormat::BGRA_8888)
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
index b76b2375..43b3557 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -24,6 +24,12 @@
 #include "ui/platform_window/extensions/wayland_extension.h"
 #include "ui/platform_window/wm/wm_drop_handler.h"
 
+#if BUILDFLAG(IS_LACROS)
+// TODO(jamescook): The nogncheck is to work around false-positive failures on
+// the code search bot. Remove after https://crrev.com/c/2432137 lands.
+#include "chromeos/crosapi/cpp/crosapi_constants.h"  // nogncheck
+#endif
+
 namespace ui {
 
 WaylandToplevelWindow::WaylandToplevelWindow(PlatformWindowDelegate* delegate,
@@ -338,7 +344,8 @@
     PlatformWindowInitProperties properties) {
 #if BUILDFLAG(IS_LACROS)
   auto token = base::UnguessableToken::Create();
-  window_unique_id_ = "org.chromium.lacros." + token.ToString();
+  window_unique_id_ =
+      std::string(crosapi::kLacrosAppIdPrefix) + token.ToString();
 #else
   wm_class_class_ = properties.wm_class_class;
 #endif
diff --git a/ui/ozone/platform/x11/x11_screen_ozone.cc b/ui/ozone/platform/x11/x11_screen_ozone.cc
index ac22d0a6..e853636 100644
--- a/ui/ozone/platform/x11/x11_screen_ozone.cc
+++ b/ui/ozone/platform/x11/x11_screen_ozone.cc
@@ -11,6 +11,7 @@
 #include "ui/gfx/font_render_params.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/platform_window/x11/x11_topmost_window_finder.h"
 #include "ui/platform_window/x11/x11_window.h"
@@ -110,11 +111,12 @@
 }
 
 display::Display X11ScreenOzone::GetDisplayMatching(
-    const gfx::Rect& match_rect) const {
+    const gfx::Rect& match_rect_in_pixels) const {
+  gfx::Rect match_rect = gfx::ToEnclosingRect(
+      gfx::ConvertRectToDips(match_rect_in_pixels, GetDeviceScaleFactor()));
   const display::Display* matching_display =
       display::FindDisplayWithBiggestIntersection(
-          x11_display_manager_->displays(),
-          gfx::ConvertRectToDIP(GetDeviceScaleFactor(), match_rect));
+          x11_display_manager_->displays(), match_rect);
   return matching_display ? *matching_display : GetPrimaryDisplay();
 }
 
diff --git a/ui/ozone/platform/x11/x11_screen_ozone.h b/ui/ozone/platform/x11/x11_screen_ozone.h
index de7d392..cd14cf37 100644
--- a/ui/ozone/platform/x11/x11_screen_ozone.h
+++ b/ui/ozone/platform/x11/x11_screen_ozone.h
@@ -46,7 +46,7 @@
   display::Display GetDisplayNearestPoint(
       const gfx::Point& point) const override;
   display::Display GetDisplayMatching(
-      const gfx::Rect& match_rect) const override;
+      const gfx::Rect& match_rect_in_pixels) const override;
   void AddObserver(display::DisplayObserver* observer) override;
   void RemoveObserver(display::DisplayObserver* observer) override;
   std::string GetCurrentWorkspace() override;
diff --git a/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
index b3fe507..3bbb56c 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
@@ -22,6 +22,7 @@
 #include "ui/gfx/font_render_params.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/switches.h"
 #include "ui/platform_window/x11/x11_topmost_window_finder.h"
@@ -121,9 +122,10 @@
         DesktopWindowTreeHostLinux::GetHostForWidget(
             host->GetAcceleratedWidget());
     if (desktop_host) {
-      const gfx::Rect pixel_rect = desktop_host->GetBoundsInPixels();
-      return GetDisplayMatching(
-          gfx::ConvertRectToDIP(GetXDisplayScaleFactor(), pixel_rect));
+      gfx::Rect match_rect_in_pixels = desktop_host->GetBoundsInPixels();
+      gfx::Rect match_rect = gfx::ToEnclosingRect(gfx::ConvertRectToDips(
+          match_rect_in_pixels, GetXDisplayScaleFactor()));
+      return GetDisplayMatching(match_rect);
     }
   }
 
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java
index 3130335..0e0a4d0 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java
@@ -36,18 +36,16 @@
         if (WebLayerFactoryImpl.getClientMajorVersion() < 83) {
             assert params == null;
         }
-        navigate2(uri, params == null ? false : params.mShouldReplaceCurrentEntry, false, false,
-                false);
+        navigate2(uri, params == null ? false : params.mShouldReplaceCurrentEntry, false, false);
     }
 
     @Override
     public void navigate2(String uri, boolean shouldReplaceCurrentEntry,
-            boolean disableIntentProcessing, boolean disableNetworkErrorAutoReload,
-            boolean enableAutoPlay) throws RemoteException {
+            boolean disableIntentProcessing, boolean disableNetworkErrorAutoReload)
+            throws RemoteException {
         StrictModeWorkaround.apply();
         NavigationControllerImplJni.get().navigate(mNativeNavigationController, uri,
-                shouldReplaceCurrentEntry, disableIntentProcessing, disableNetworkErrorAutoReload,
-                enableAutoPlay);
+                shouldReplaceCurrentEntry, disableIntentProcessing, disableNetworkErrorAutoReload);
     }
 
     @Override
@@ -185,7 +183,7 @@
         long getNavigationController(long tab);
         void navigate(long nativeNavigationControllerImpl, String uri,
                 boolean shouldReplaceCurrentEntry, boolean disableIntentProcessing,
-                boolean disableNetworkErrorAutoReload, boolean enableAutoPlay);
+                boolean disableNetworkErrorAutoReload);
         void goBack(long nativeNavigationControllerImpl);
         void goForward(long nativeNavigationControllerImpl);
         boolean canGoBack(long nativeNavigationControllerImpl);
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl
index e2ffa2a1..1e268d9 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl
@@ -41,6 +41,5 @@
   void navigate2(in String uri,
                  in boolean shouldReplaceEntry,
                  in boolean disableIntentProcessing,
-                 in boolean disableNetworkErrorAutoReload,
-                 in boolean enableAutoPlay) = 14;
+                 in boolean disableNetworkErrorAutoReload) = 14;
 }
diff --git a/weblayer/browser/navigation_browsertest.cc b/weblayer/browser/navigation_browsertest.cc
index 283e2848..d05458f 100644
--- a/weblayer/browser/navigation_browsertest.cc
+++ b/weblayer/browser/navigation_browsertest.cc
@@ -9,21 +9,18 @@
 #include "base/test/bind_test_util.h"
 #include "components/variations/net/variations_http_headers.h"
 #include "components/variations/variations_ids_provider.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/test/browser_test_utils.h"
 #include "content/public/test/url_loader_interceptor.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/controllable_http_response.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_response.h"
-#include "weblayer/browser/tab_impl.h"
 #include "weblayer/public/browser.h"
 #include "weblayer/public/navigation.h"
 #include "weblayer/public/navigation_controller.h"
 #include "weblayer/public/navigation_observer.h"
+#include "weblayer/public/tab.h"
 #include "weblayer/shell/browser/shell.h"
 #include "weblayer/test/interstitial_utils.h"
-#include "weblayer/test/test_navigation_observer.h"
 #include "weblayer/test/weblayer_browser_test_utils.h"
 
 namespace weblayer {
@@ -591,63 +588,6 @@
   EXPECT_EQ(custom_ua, new_ua);
 }
 
-IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, AutoPlayDefault) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  GURL url(embedded_test_server()->GetURL("/autoplay.html"));
-  auto* tab = static_cast<TabImpl*>(shell()->tab());
-  NavigateAndWaitForCompletion(url, tab);
-
-  auto* web_contents = tab->web_contents();
-  bool playing = false;
-  // There's no notification to watch that would signal video wasn't autoplayed,
-  // so instead check once through javascript.
-  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
-      web_contents,
-      "window.domAutomationController.send(!document.getElementById('vid')."
-      "paused)",
-      &playing));
-  ASSERT_FALSE(playing);
-}
-
-namespace {
-
-class WaitForMediaPlaying : public content::WebContentsObserver {
- public:
-  explicit WaitForMediaPlaying(content::WebContents* web_contents)
-      : WebContentsObserver(web_contents) {}
-
-  // WebContentsObserver override.
-  void MediaStartedPlaying(const MediaPlayerInfo& info,
-                           const content::MediaPlayerId&) final {
-    run_loop_.Quit();
-    CHECK(info.has_audio);
-    CHECK(info.has_video);
-  }
-
-  void Wait() { run_loop_.Run(); }
-
- private:
-  base::RunLoop run_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(WaitForMediaPlaying);
-};
-
-}  // namespace
-
-IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, AutoPlayEnabled) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  GURL url(embedded_test_server()->GetURL("/autoplay.html"));
-  NavigationController::NavigateParams params;
-  params.enable_auto_play = true;
-  GetNavigationController()->Navigate(url, params);
-
-  auto* tab = static_cast<TabImpl*>(shell()->tab());
-  WaitForMediaPlaying wait_for_media(tab->web_contents());
-  wait_for_media.Wait();
-}
-
 class NavigationBrowserTest2 : public NavigationBrowserTest {
  public:
   void SetUp() override {
diff --git a/weblayer/browser/navigation_controller_impl.cc b/weblayer/browser/navigation_controller_impl.cc
index f14bb94..a04a96c 100644
--- a/weblayer/browser/navigation_controller_impl.cc
+++ b/weblayer/browser/navigation_controller_impl.cc
@@ -153,8 +153,7 @@
     const JavaParamRef<jstring>& url,
     jboolean should_replace_current_entry,
     jboolean disable_intent_processing,
-    jboolean disable_network_error_auto_reload,
-    jboolean enable_auto_play) {
+    jboolean disable_network_error_auto_reload) {
   auto params = std::make_unique<content::NavigationController::LoadURLParams>(
       GURL(base::android::ConvertJavaStringToUTF8(env, url)));
   params->should_replace_current_entry = should_replace_current_entry;
@@ -167,8 +166,6 @@
                                 : ui::PAGE_TRANSITION_LINK;
   if (disable_network_error_auto_reload)
     params->navigation_ui_data = std::make_unique<NavigationUIDataImpl>(true);
-  if (enable_auto_play)
-    params->was_activated = content::mojom::WasActivatedOption::kYes;
 
   DoNavigate(std::move(params));
 }
@@ -241,9 +238,6 @@
     load_params->navigation_ui_data =
         std::make_unique<NavigationUIDataImpl>(true);
   }
-  if (params.enable_auto_play)
-    load_params->was_activated = content::mojom::WasActivatedOption::kYes;
-
   DoNavigate(std::move(load_params));
 }
 
diff --git a/weblayer/browser/navigation_controller_impl.h b/weblayer/browser/navigation_controller_impl.h
index f27153ad..dfebd80 100644
--- a/weblayer/browser/navigation_controller_impl.h
+++ b/weblayer/browser/navigation_controller_impl.h
@@ -53,8 +53,7 @@
                 const base::android::JavaParamRef<jstring>& url,
                 jboolean should_replace_current_entry,
                 jboolean disable_intent_processing,
-                jboolean disable_network_error_auto_reload,
-                jboolean enable_auto_play);
+                jboolean disable_network_error_auto_reload);
   void GoBack(JNIEnv* env) { GoBack(); }
   void GoForward(JNIEnv* env) { GoForward(); }
   bool CanGoBack(JNIEnv* env) { return CanGoBack(); }
diff --git a/weblayer/public/java/org/chromium/weblayer/NavigateParams.java b/weblayer/public/java/org/chromium/weblayer/NavigateParams.java
index 5d030dc..d3933d4 100644
--- a/weblayer/public/java/org/chromium/weblayer/NavigateParams.java
+++ b/weblayer/public/java/org/chromium/weblayer/NavigateParams.java
@@ -16,7 +16,6 @@
             new org.chromium.weblayer_private.interfaces.NavigateParams();
     private boolean mIntentProcessingDisabled;
     private boolean mNetworkErrorAutoReloadDisabled;
-    private boolean mAutoPlayEnabled;
 
     /**
      * A Builder class to help create NavigateParams.
@@ -83,21 +82,6 @@
             mParams.mNetworkErrorAutoReloadDisabled = true;
             return this;
         }
-
-        /**
-         * Enable auto-play for videos in this navigation. Auto-play is disabled by default.
-         *
-         * @since 86
-         */
-        @NonNull
-        public Builder enableAutoPlay() {
-            if (WebLayer.shouldPerformVersionChecks()
-                    && WebLayer.getSupportedMajorVersionInternal() < 86) {
-                throw new UnsupportedOperationException();
-            }
-            mParams.mAutoPlayEnabled = true;
-            return this;
-        }
     }
 
     org.chromium.weblayer_private.interfaces.NavigateParams toInterfaceParams() {
@@ -142,19 +126,4 @@
         }
         return mNetworkErrorAutoReloadDisabled;
     }
-
-    /**
-     * Returns true if auto play for videos is enabled.
-     *
-     * @return Whether auto play for videos is enabled.
-     *
-     * @since 86
-     */
-    public boolean isAutoPlayEnabled() {
-        if (WebLayer.shouldPerformVersionChecks()
-                && WebLayer.getSupportedMajorVersionInternal() < 86) {
-            throw new UnsupportedOperationException();
-        }
-        return mAutoPlayEnabled;
-    }
 }
diff --git a/weblayer/public/java/org/chromium/weblayer/NavigationController.java b/weblayer/public/java/org/chromium/weblayer/NavigationController.java
index ef267d5f..d212ce4 100644
--- a/weblayer/public/java/org/chromium/weblayer/NavigationController.java
+++ b/weblayer/public/java/org/chromium/weblayer/NavigationController.java
@@ -63,8 +63,7 @@
                 mNavigationController.navigate2(uri.toString(),
                         params == null ? false : params.getShouldReplaceCurrentEntry(),
                         params == null ? false : params.isIntentProcessingDisabled(),
-                        params == null ? false : params.isNetworkErrorAutoReloadDisabled(),
-                        params == null ? false : params.isAutoPlayEnabled());
+                        params == null ? false : params.isNetworkErrorAutoReloadDisabled());
             }
         } catch (RemoteException e) {
             throw new APICallException(e);
diff --git a/weblayer/public/navigation_controller.h b/weblayer/public/navigation_controller.h
index 56691c87..b4ad035d 100644
--- a/weblayer/public/navigation_controller.h
+++ b/weblayer/public/navigation_controller.h
@@ -20,7 +20,6 @@
   struct NavigateParams {
     bool should_replace_current_entry = false;
     bool disable_network_error_auto_reload = false;
-    bool enable_auto_play = false;
   };
 
   virtual ~NavigationController() = default;
diff --git a/weblayer/test/data/autoplay.html b/weblayer/test/data/autoplay.html
deleted file mode 100644
index 017f63f0f..0000000
--- a/weblayer/test/data/autoplay.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<html>
-<body>
-<video id="vid" controls autoplay>
-  <source src="bear.mp4" type="video/mp4">
-</video>
-</body>
-</html>
diff --git a/weblayer/test/data/bear.mp4 b/weblayer/test/data/bear.mp4
deleted file mode 100644
index 3763b59..0000000
--- a/weblayer/test/data/bear.mp4
+++ /dev/null
Binary files differ