diff --git a/DEPS b/DEPS
index b52e57d81..99cc4fe3 100644
--- a/DEPS
+++ b/DEPS
@@ -175,11 +175,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': 'c65d0069ec56aa972e1fd18c3d3915db5bad3498',
+  'skia_revision': '1eccf285a2991277e7e05054231d45b57c3c4a26',
   # 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': '4c53f9a51444393133ff303952f1296603d44ab7',
+  'v8_revision': 'e7dd8e8dfc38217453b151faafe2088581b4542a',
   # 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.
@@ -187,7 +187,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': '9fac1817ede6b2f1dc61468e2625cbc30d35077c',
+  'angle_revision': 'cbbfa2f28a0ee0ca652416d74b42c58238b24a3a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -238,7 +238,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'c9e75ab1ff4788c19a7b525fe3f35022b2f4a9b6',
+  'catapult_revision': '7c4300cb193f74ea6e0f04e1dbfca78b7140a241',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -246,7 +246,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': 'c7539abc143c72521e494589f3784dbea891408c',
+  'devtools_frontend_revision': 'a1bd2ac157a5de2259f7b0ccb0801e819c7a407e',
   # 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.
@@ -337,7 +337,7 @@
   # revisions.
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:6feb55993083dfd27b93da195c8a82a3a9529848',
+  'gn_version': 'git_revision:a5bcbd726ac7bd342ca6ee3e3a006478fd1f00b5',
 
   # Also, if you change these, update buildtools/DEPS too. Also update the
   # libc++ svn_revision in //buildtools/deps_revisions.gni.
@@ -864,7 +864,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '471dda709844bf3c646cdaefbd522550ad5030b2',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '5abfe3af46e684491975f09d145e880b18dcb9e6',
       'condition': 'checkout_linux',
   },
 
@@ -1483,7 +1483,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '53655df4cde60b121fc530842ba9a6d5dfec1ae1',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '9d2c2dba28656b3c913797daaf99b8bf56ca6103',
+    Var('webrtc_git') + '/src.git' + '@' + '1c34ca7676e882100e29ef47258bfb2e41c30cbf',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1553,7 +1553,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@576e75e99e18ccfc3d6ab087bf29d83c8bf697ff',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e658c3481ce2e8d0a0b40d0b3868493c81811ec4',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/gfx/output_surface_provider_webview.cc b/android_webview/browser/gfx/output_surface_provider_webview.cc
index b4f3392..0d655f8 100644
--- a/android_webview/browser/gfx/output_surface_provider_webview.cc
+++ b/android_webview/browser/gfx/output_surface_provider_webview.cc
@@ -85,8 +85,9 @@
           GpuServiceWebView::GetInstance()->gpu_preferences(),
           std::move(feature_info));
     }
-    shared_context_state_->InitializeGrContext(workarounds,
-                                               nullptr /* gr_shader_cache */);
+    shared_context_state_->InitializeGrContext(
+        GpuServiceWebView::GetInstance()->gpu_preferences(), workarounds,
+        nullptr /* gr_shader_cache */);
   }
 }
 
diff --git a/ash/display/display_prefs_unittest.cc b/ash/display/display_prefs_unittest.cc
index 5d6af550..5d8a90f 100644
--- a/ash/display/display_prefs_unittest.cc
+++ b/ash/display/display_prefs_unittest.cc
@@ -120,7 +120,7 @@
   ~DisplayPrefsTest() override {}
 
   void SetUp() override {
-    disable_provide_local_state();
+    DisableProvideLocalState();
     AshTestBase::SetUp();
     DisplayPrefs::RegisterLocalStatePrefs(local_state_.registry());
     display_prefs()->SetPrefServiceForTest(&local_state_);
diff --git a/ash/shell.cc b/ash/shell.cc
index 3bb26ba..b5e27220d 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -908,8 +908,6 @@
 
   InitializeDisplayManager();
 
-  display_prefs_ = std::make_unique<DisplayPrefs>(local_state_);
-
   // RefreshFontParams depends on display prefs.
   display_manager_->RefreshFontParams();
 
@@ -1169,7 +1167,6 @@
 }
 
 void Shell::InitializeDisplayManager() {
-  bool display_initialized = display_manager_->InitFromCommandLine();
 
   display_manager_->InitConfigurator(
       ui::OzonePlatform::GetInstance()->CreateNativeDisplayDelegate());
@@ -1185,6 +1182,10 @@
   projecting_observer_ =
       std::make_unique<ProjectingObserver>(display_manager_->configurator());
 
+  display_prefs_ = std::make_unique<DisplayPrefs>(local_state_);
+
+  bool display_initialized = display_manager_->InitFromCommandLine();
+
   if (!display_initialized) {
     if (chromeos::IsRunningAsSystemCompositor()) {
       display_change_observer_ =
diff --git a/ash/shell_unittest.cc b/ash/shell_unittest.cc
index c4482f1..57a1281 100644
--- a/ash/shell_unittest.cc
+++ b/ash/shell_unittest.cc
@@ -592,7 +592,7 @@
 // Tests the local state code path.
 class ShellLocalStateTest : public AshTestBase {
  public:
-  ShellLocalStateTest() { disable_provide_local_state(); }
+  ShellLocalStateTest() { DisableProvideLocalState(); }
 
  protected:
   std::unique_ptr<TestingPrefServiceSimple> local_state_;
diff --git a/ash/system/bluetooth/bluetooth_power_controller_unittest.cc b/ash/system/bluetooth/bluetooth_power_controller_unittest.cc
index cb80cac..6375e5b 100644
--- a/ash/system/bluetooth/bluetooth_power_controller_unittest.cc
+++ b/ash/system/bluetooth/bluetooth_power_controller_unittest.cc
@@ -50,7 +50,7 @@
 // of default pref values before the pref service initialization notifications.
 class BluetoothPowerControllerTest : public NoSessionAshTestBase {
  public:
-  BluetoothPowerControllerTest() { disable_provide_local_state(); }
+  BluetoothPowerControllerTest() { DisableProvideLocalState(); }
   ~BluetoothPowerControllerTest() override = default;
 
   void SetUp() override {
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc
index 0a9720c..ddeb61e 100644
--- a/ash/test/ash_test_base.cc
+++ b/ash/test/ash_test_base.cc
@@ -17,6 +17,7 @@
 #include "ash/display/unified_mouse_warp_controller.h"
 #include "ash/display/window_tree_host_manager.h"
 #include "ash/keyboard/keyboard_controller_impl.h"
+#include "ash/public/cpp/ash_prefs.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
@@ -139,7 +140,11 @@
 
   AshTestHelper::InitParams params;
   params.start_session = start_session_;
-  params.provide_local_state = provide_local_state_;
+  if (register_local_state_) {
+    DCHECK(local_state_.get());
+    RegisterLocalStatePrefs(local_state_->registry(), true);
+  }
+  params.local_state = local_state_.get();
   params.config_type = AshTestHelper::kUnitTest;
   ash_test_helper_.SetUp(params);
 
@@ -366,6 +371,11 @@
                                         gfx::Rect());
 }
 
+void AshTestBase::DisableProvideLocalState() {
+  local_state_.reset();
+  register_local_state_ = false;
+}
+
 TestScreenshotDelegate* AshTestBase::GetScreenshotDelegate() {
   return static_cast<TestScreenshotDelegate*>(
       Shell::Get()->screenshot_controller()->screenshot_delegate_.get());
diff --git a/ash/test/ash_test_base.h b/ash/test/ash_test_base.h
index 3290046..95bc2b9 100644
--- a/ash/test/ash_test_base.h
+++ b/ash/test/ash_test_base.h
@@ -23,6 +23,7 @@
 #include "base/test/task_environment.h"
 #include "base/threading/thread.h"
 #include "base/traits_bag.h"
+#include "components/prefs/testing_pref_service.h"
 #include "components/user_manager/user_type.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -211,7 +212,7 @@
   static display::Display::Rotation GetCurrentInternalDisplayRotation();
 
   void set_start_session(bool start_session) { start_session_ = start_session; }
-  void disable_provide_local_state() { provide_local_state_ = false; }
+  void DisableProvideLocalState();
 
   AshTestHelper* ash_test_helper() { return &ash_test_helper_; }
 
@@ -282,9 +283,6 @@
   bool teardown_called_ = false;
   // |SetUp()| doesn't activate session if this is set to false.
   bool start_session_ = true;
-  // |SetUp()| doesn't inject local-state PrefService into Shell if this is
-  // set to false.
-  bool provide_local_state_ = true;
 
   // Must be initialized at construction because some tests rely on AshTestBase
   // methods before AshTestBase::SetUp().
@@ -301,6 +299,16 @@
   // SubclassManagesTaskEnvironment mode.
   base::Optional<base::test::TaskEnvironment> task_environment_;
 
+  // A pref service used for local state. Reset it by
+  // DisableProvideLocalState() if a test provides its own local state.
+  std::unique_ptr<TestingPrefServiceSimple> local_state_ =
+      std::make_unique<TestingPrefServiceSimple>();
+
+  // True to register pref service with ash during SetUp(). Set to false if
+  // a test wants to register and provide the local state data before
+  // creating the ash shell.
+  bool register_local_state_ = true;
+
   // Private again for DISALLOW_COPY_AND_ASSIGN; additional members should be
   // added in the first private section to be before |task_environment_|.
  private:
diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc
index 2adb2112..4fff9d2 100644
--- a/ash/test/ash_test_helper.cc
+++ b/ash/test/ash_test_helper.cc
@@ -16,7 +16,6 @@
 #include "ash/display/screen_ash.h"
 #include "ash/keyboard/keyboard_controller_impl.h"
 #include "ash/keyboard/test_keyboard_ui.h"
-#include "ash/public/cpp/ash_prefs.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/test/test_keyboard_controller_observer.h"
 #include "ash/public/cpp/test/test_new_window_delegate.h"
@@ -43,7 +42,6 @@
 #include "chromeos/dbus/power/power_policy_controller.h"
 #include "chromeos/system/fake_statistics_provider.h"
 #include "components/discardable_memory/public/mojom/discardable_shared_memory_manager.mojom.h"
-#include "components/prefs/testing_pref_service.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 #include "ui/aura/env.h"
@@ -150,7 +148,7 @@
 
   ui::MaterialDesignController::Initialize();
 
-  CreateShell(init_params.provide_local_state, std::move(shell_init_params));
+  CreateShell(std::move(shell_init_params), init_params.local_state);
 
   // Reset aura::Env to eliminate test dependency (https://crbug.com/586514).
   aura::test::EnvTestHelper env_helper(aura::Env::GetInstance());
@@ -304,8 +302,8 @@
   return Shell::Get()->display_manager()->GetSecondaryDisplay();
 }
 
-void AshTestHelper::CreateShell(bool provide_local_state,
-                                base::Optional<ShellInitParams> init_params) {
+void AshTestHelper::CreateShell(base::Optional<ShellInitParams> init_params,
+                                PrefService* local_state) {
   if (init_params == base::nullopt) {
     context_factories_ = std::make_unique<ui::TestContextFactories>(
         /*enable_pixel_output=*/false);
@@ -317,13 +315,8 @@
     init_params->keyboard_ui_factory =
         std::make_unique<TestKeyboardUIFactory>();
   }
-  if (provide_local_state) {
-    auto pref_service = std::make_unique<TestingPrefServiceSimple>();
-    RegisterLocalStatePrefs(pref_service->registry(), true);
-
-    local_state_ = std::move(pref_service);
-    init_params->local_state = local_state_.get();
-  }
+  if (local_state)
+    init_params->local_state = local_state;
 
   Shell::CreateInstance(std::move(*init_params));
 }
diff --git a/ash/test/ash_test_helper.h b/ash/test/ash_test_helper.h
index 0261587..d5cd38a 100644
--- a/ash/test/ash_test_helper.h
+++ b/ash/test/ash_test_helper.h
@@ -77,8 +77,7 @@
   struct InitParams {
     // True if the user should log in.
     bool start_session = true;
-    // True to inject local-state PrefService into the Shell.
-    bool provide_local_state = true;
+    PrefService* local_state = nullptr;
     ConfigType config_type = kUnitTest;
   };
 
@@ -139,8 +138,8 @@
 
  private:
   // Called when running in ash to create Shell.
-  void CreateShell(bool provide_local_state,
-                   base::Optional<ShellInitParams> init_params);
+  void CreateShell(base::Optional<ShellInitParams> init_params,
+                   PrefService* local_state);
 
   std::unique_ptr<chromeos::system::ScopedFakeStatisticsProvider>
       statistics_provider_;
@@ -172,8 +171,6 @@
   std::unique_ptr<TestKeyboardControllerObserver>
       test_keyboard_controller_observer_;
 
-  std::unique_ptr<PrefService> local_state_;
-
   DISALLOW_COPY_AND_ASSIGN(AshTestHelper);
 };
 
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index 98e2435..8a768acf 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -7,6 +7,8 @@
 #include <cmath>
 #include <cstdlib>
 
+#include "ash/public/cpp/ash_pref_names.h"
+#include "ash/public/cpp/ash_prefs.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/test/shell_test_api.h"
@@ -34,6 +36,7 @@
 #include "base/test/bind_test_util.h"
 #include "base/time/time_override.h"
 #include "chromeos/constants/chromeos_switches.h"
+#include "components/prefs/scoped_user_pref_update.h"
 #include "components/user_manager/fake_user_manager.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user_names.h"
@@ -43,6 +46,8 @@
 #include "ui/aura/window.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/test/layer_animator_test_controller.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/codec/jpeg_codec.h"
 #include "ui/views/widget/widget.h"
@@ -2617,4 +2622,48 @@
   EXPECT_EQ(kShellWindowId_WallpaperContainer, GetWallpaperContainerId());
 }
 
+namespace {
+
+class WallpaperControllerPrefTest : public AshTestBase {
+ public:
+  WallpaperControllerPrefTest() = default;
+  ~WallpaperControllerPrefTest() override = default;
+
+  void SetUp() override {
+    {
+      RegisterLocalStatePrefs(local_state_->registry(), true);
+      register_local_state_ = false;
+      DictionaryPrefUpdate update(local_state_.get(),
+                                  prefs::kDisplayProperties);
+
+      base::DictionaryValue* pref_data = update.Get();
+
+      auto property = std::make_unique<base::DictionaryValue>();
+      property->SetInteger("rotation",
+                           static_cast<int>(display::Display::ROTATE_90));
+      property->SetInteger("width", 800);
+      property->SetInteger("height", 600);
+      pref_data->Set("2200000000", std::move(property));
+    }
+    AshTestBase::SetUp();
+  }
+};
+
+}  // namespace
+
+// Make sure that the display and the wallpaper view are rotated correctly at
+// startup.
+TEST_F(WallpaperControllerPrefTest, InitWithPrefs) {
+  auto* wallpaper_view = Shell::GetPrimaryRootWindowController()
+                             ->wallpaper_widget_controller()
+                             ->wallpaper_view();
+  auto* root_window =
+      wallpaper_view->GetWidget()->GetNativeWindow()->GetRootWindow();
+
+  EXPECT_EQ(gfx::Size(600, 800), display::Screen::GetScreen()
+                                     ->GetDisplayNearestWindow(root_window)
+                                     .size());
+  EXPECT_EQ(root_window->bounds().size(), wallpaper_view->bounds().size());
+}
+
 }  // namespace ash
diff --git a/base/android/java/src/org/chromium/base/SysUtils.java b/base/android/java/src/org/chromium/base/SysUtils.java
index af20e64..da5e11a 100644
--- a/base/android/java/src/org/chromium/base/SysUtils.java
+++ b/base/android/java/src/org/chromium/base/SysUtils.java
@@ -177,6 +177,7 @@
             return false;
         }
 
+        // If this logic changes, update the comments above base::SysUtils::IsLowEndDevice.
         sAmountOfPhysicalMemoryKB = detectAmountOfPhysicalMemoryKB();
         boolean isLowEnd = true;
         if (sAmountOfPhysicalMemoryKB <= 0) {
diff --git a/base/system/sys_info.cc b/base/system/sys_info.cc
index 21ddf7ca..5082ec8 100644
--- a/base/system/sys_info.cc
+++ b/base/system/sys_info.cc
@@ -10,10 +10,8 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/command_line.h"
-#include "base/feature_list.h"
 #include "base/location.h"
 #include "base/logging.h"
-#include "base/metrics/field_trial_params.h"
 #include "base/no_destructor.h"
 #include "base/system/sys_info_internal.h"
 #include "base/task/post_task.h"
@@ -24,28 +22,14 @@
 
 namespace base {
 namespace {
-
-// Feature used to control the heuristics used to categorize a device as low
-// end.
-const base::Feature kLowEndDeviceDetectionFeature{
-    "LowEndDeviceDetection", base::FEATURE_DISABLED_BY_DEFAULT};
-
-static const int kLowMemoryDeviceThresholdMBDefault = 512;
-
-int GetLowMemoryDeviceThresholdMB() {
-  static constexpr base::FeatureParam<int> kLowEndDeviceMemoryThresholdMB{
-      &kLowEndDeviceDetectionFeature, "LowEndDeviceMemoryThresholdMB",
-      kLowMemoryDeviceThresholdMBDefault};
-  // If the feature is disabled then |Get| will return the default value.
-  return kLowEndDeviceMemoryThresholdMB.Get();
-}
+static const int kLowMemoryDeviceThresholdMB = 512;
 }  // namespace
 
 // static
 int64_t SysInfo::AmountOfPhysicalMemory() {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableLowEndDeviceMode)) {
-    return GetLowMemoryDeviceThresholdMB() * 1024 * 1024;
+    return kLowMemoryDeviceThresholdMB * 1024 * 1024;
   }
 
   return AmountOfPhysicalMemoryImpl();
@@ -56,10 +40,10 @@
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableLowEndDeviceMode)) {
     // Estimate the available memory by subtracting our memory used estimate
-    // from the fake |GetLowMemoryDeviceThresholdMB()| limit.
+    // from the fake |kLowMemoryDeviceThresholdMB| limit.
     size_t memory_used =
         AmountOfPhysicalMemoryImpl() - AmountOfAvailablePhysicalMemoryImpl();
-    size_t memory_limit = GetLowMemoryDeviceThresholdMB() * 1024 * 1024;
+    size_t memory_limit = kLowMemoryDeviceThresholdMB * 1024 * 1024;
     // std::min ensures no underflow, as |memory_used| can be > |memory_limit|.
     return memory_limit - std::min(memory_used, memory_limit);
   }
@@ -86,7 +70,7 @@
     return false;
 
   int ram_size_mb = SysInfo::AmountOfPhysicalMemoryMB();
-  return (ram_size_mb > 0 && ram_size_mb <= GetLowMemoryDeviceThresholdMB());
+  return (ram_size_mb > 0 && ram_size_mb <= kLowMemoryDeviceThresholdMB);
 }
 
 // static
diff --git a/base/system/sys_info.h b/base/system/sys_info.h
index e5ffb2ae..400d0577 100644
--- a/base/system/sys_info.h
+++ b/base/system/sys_info.h
@@ -182,10 +182,16 @@
   static std::string GetIOSBuildNumber();
 #endif  // defined(OS_IOS)
 
-  // Returns true if this is a low-end device.
-  // Low-end device refers to devices having a very low amount of total
-  // system memory, typically <= 1GB.
-  // See also SysUtils.java, method isLowEndDevice.
+  // Returns true for low-end devices that may require extreme tradeoffs,
+  // including user-visible changes, for acceptable performance.
+  // For general memory optimizations, consider |AmountOfPhysicalMemoryMB|.
+  //
+  // On Android this returns:
+  //   true when memory <= 1GB on Android O and later.
+  //   true when memory <= 512MB on Android N and earlier.
+  // This is not the same as "low-memory" and will be false on a large number of
+  // <=1GB pre-O Android devices. See: |detectLowEndDevice| in SysUtils.java.
+  // On Desktop this returns true when memory <= 512MB.
   static bool IsLowEndDevice();
 
  private:
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 74c11ca..d7b38c92 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -613,25 +613,25 @@
     } else {
       ldflags += [ "-flto=thin" ]
 
+      # Enabling ThinLTO on Chrome OS too, in an effort to reduce the memory
+      # usage in crbug.com/1038040. Note this will increase build time in
+      # Chrome OS.
+
       # Limit the parallelism to avoid too aggressive competition between
       # linker jobs. This is still suboptimal to a potential dynamic
       # resource allocation scheme, but should be good enough.
       if (use_lld) {
         ldflags += [ "-Wl,--thinlto-jobs=" + max_jobs_per_link ]
 
-        # Caching on Chrome OS materially slows the PFQ down; disable it until
-        # we can figure out why. (crbug.com/964328)
-        if (!is_chromeos) {
-          # Limit the size of the ThinLTO cache to the lesser of 10% of
-          # available disk space, 10GB and 100000 files.
-          cache_policy =
-              "cache_size=10%:cache_size_bytes=10g:cache_size_files=100000"
-          ldflags += [
-            "-Wl,--thinlto-cache-dir=" +
-                rebase_path("$root_out_dir/thinlto-cache", root_build_dir),
-            "-Wl,--thinlto-cache-policy,$cache_policy",
-          ]
-        }
+        # Limit the size of the ThinLTO cache to the lesser of 10% of
+        # available disk space, 10GB and 100000 files.
+        cache_policy =
+            "cache_size=10%:cache_size_bytes=10g:cache_size_files=100000"
+        ldflags += [
+          "-Wl,--thinlto-cache-dir=" +
+              rebase_path("$root_out_dir/thinlto-cache", root_build_dir),
+          "-Wl,--thinlto-cache-policy,$cache_policy",
+        ]
       } else {
         ldflags += [ "-Wl,-plugin-opt,jobs=" + max_jobs_per_link ]
       }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 8f1e9093f..3488b15 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8892617276415118848
\ No newline at end of file
+8892458420925474704
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 0e9ebd0a..bf0e387 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8892617275597794992
\ No newline at end of file
+8892526010997090496
\ No newline at end of file
diff --git a/buildtools/DEPS b/buildtools/DEPS
index 0f483469..ace93b4 100644
--- a/buildtools/DEPS
+++ b/buildtools/DEPS
@@ -14,7 +14,7 @@
   #
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:6feb55993083dfd27b93da195c8a82a3a9529848',
+  'gn_version': 'git_revision:a5bcbd726ac7bd342ca6ee3e3a006478fd1f00b5',
 
   # When changing these, also update the svn revisions in deps_revisions.gni
   'clang_format_revision': '96636aa0e9f047f17447f2d45a094d0b59ed7917',
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 2bbadb67..632c224 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -577,7 +577,7 @@
   // ensures that the acks are sent in order.
   if (!state_machine_.did_submit_in_last_frame()) {
     SendDidNotProduceFrame(begin_impl_frame_tracker_.Current(),
-                           FrameSkippedReason::kNoDamage);
+                           FrameSkippedReason::kWaitingOnMain);
   }
 
   begin_impl_frame_tracker_.Finish();
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index 894339c3..02149ef 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -43,6 +43,7 @@
 enum class FrameSkippedReason {
   kRecoverLatency,
   kNoDamage,
+  kWaitingOnMain,
 };
 
 class SchedulerClient {
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 7e6ebca..d011f3b 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2732,9 +2732,10 @@
   if (layer_tree_frame_sink_)
     layer_tree_frame_sink_->DidNotProduceFrame(ack);
 
-  // If a frame was not submitted because there was no damage, then notify the
+  // If a frame was not submitted because there was no damage, or the scheduler
+  // hit the frame-deadline while waiting for the main-thread, notify the
   // trackers.
-  if (reason == FrameSkippedReason::kNoDamage &&
+  if (reason != FrameSkippedReason::kRecoverLatency &&
       impl_thread_phase_ == ImplThreadPhase::INSIDE_IMPL_FRAME) {
     // It is possible that |ack| is for a 'future frame', i.e. for the next
     // frame from the one currently being handled by the compositor (represented
@@ -2746,8 +2747,10 @@
     const auto& args = current_begin_frame_tracker_.Current();
     if (args.frame_id == ack.frame_id) {
       frame_trackers_.NotifyImplFrameCausedNoDamage(ack);
-      if (begin_main_frame_sent_during_impl_)
+      if (begin_main_frame_sent_during_impl_ &&
+          reason == FrameSkippedReason::kNoDamage) {
         frame_trackers_.NotifyMainFrameCausedNoDamage(args);
+      }
     }
   }
 }
diff --git a/chrome/VERSION b/chrome/VERSION
index 0902c5c..21ac043 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=81
 MINOR=0
-BUILD=4013
+BUILD=4015
 PATCH=0
diff --git a/chrome/android/java/res/layout/confirm_import_sync_data.xml b/chrome/android/java/res/layout/confirm_import_sync_data.xml
index a4939e4..e65f6a6 100644
--- a/chrome/android/java/res/layout/confirm_import_sync_data.xml
+++ b/chrome/android/java/res/layout/confirm_import_sync_data.xml
@@ -29,14 +29,14 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:background="?android:attr/selectableItemBackground"
-            app:titleText="@string/sync_import_existing_data" />
+            app:primaryText="@string/sync_import_existing_data" />
 
         <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
             android:id="@+id/sync_keep_separate_choice"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:background="?android:attr/selectableItemBackground"
-            app:titleText="@string/sync_keep_existing_data_separate" />
+            app:primaryText="@string/sync_keep_existing_data_separate" />
 
     </LinearLayout>
 </ScrollView>
diff --git a/chrome/android/java/res/layout/radio_button_group_theme_preference.xml b/chrome/android/java/res/layout/radio_button_group_theme_preference.xml
index 339070c..c246700 100644
--- a/chrome/android/java/res/layout/radio_button_group_theme_preference.xml
+++ b/chrome/android/java/res/layout/radio_button_group_theme_preference.xml
@@ -20,7 +20,7 @@
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:background="?attr/selectableItemBackground"
-          app:titleText="@string/themes_system_default_title"
+          app:primaryText="@string/themes_system_default_title"
           app:descriptionText="@string/themes_system_default_summary" />
 
       <!-- override default padding top and bottom -->
@@ -32,7 +32,7 @@
           android:paddingTop="8dp"
           android:paddingBottom="8dp"
           android:background="?attr/selectableItemBackground"
-          app:titleText="@string/light_mode" />
+          app:primaryText="@string/light_mode" />
 
       <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
           android:id="@+id/dark"
@@ -42,7 +42,7 @@
           android:paddingTop="8dp"
           android:paddingBottom="8dp"
           android:background="?attr/selectableItemBackground"
-          app:titleText="@string/dark_mode" />
+          app:primaryText="@string/dark_mode" />
 
     </org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout>
 
diff --git a/chrome/android/java/res/layout/tri_state_site_settings_preference.xml b/chrome/android/java/res/layout/tri_state_site_settings_preference.xml
index d51eaacf..9b0a653 100644
--- a/chrome/android/java/res/layout/tri_state_site_settings_preference.xml
+++ b/chrome/android/java/res/layout/tri_state_site_settings_preference.xml
@@ -25,7 +25,7 @@
             android:layout_height="wrap_content"
             android:paddingStart="?android:attr/listPreferredItemPaddingStart"
             android:background="?android:attr/selectableItemBackground"
-            app:titleText="@string/website_settings_category_allowed" />
+            app:primaryText="@string/website_settings_category_allowed" />
 
         <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
             android:id="@+id/ask"
@@ -33,7 +33,7 @@
             android:layout_height="wrap_content"
             android:paddingStart="?android:attr/listPreferredItemPaddingStart"
             android:background="?android:attr/selectableItemBackground"
-            app:titleText="@string/website_settings_category_ask" />
+            app:primaryText="@string/website_settings_category_ask" />
 
         <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
             android:id="@+id/blocked"
@@ -41,7 +41,7 @@
             android:layout_height="wrap_content"
             android:paddingStart="?android:attr/listPreferredItemPaddingStart"
             android:background="?android:attr/selectableItemBackground"
-            app:titleText="@string/website_settings_category_blocked" />
+            app:primaryText="@string/website_settings_category_blocked" />
 
     </org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout>
 </LinearLayout>
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 119025e..e9d8918e 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-81.0.4011.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-81.0.4013.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/browser/android/thin_webview/internal/java/src/org/chromium/chrome/browser/thinwebview/internal/ThinWebViewImpl.java b/chrome/browser/android/thin_webview/internal/java/src/org/chromium/chrome/browser/thinwebview/internal/ThinWebViewImpl.java
index 1fb8726..0f2f00a 100644
--- a/chrome/browser/android/thin_webview/internal/java/src/org/chromium/chrome/browser/thinwebview/internal/ThinWebViewImpl.java
+++ b/chrome/browser/android/thin_webview/internal/java/src/org/chromium/chrome/browser/thinwebview/internal/ThinWebViewImpl.java
@@ -56,6 +56,7 @@
 
     @Override
     public void attachWebContents(WebContents webContents, @Nullable View contentView) {
+        if (mNativeThinWebViewImpl == 0) return;
         mWebContents = webContents;
 
         setContentView(contentView);
@@ -66,11 +67,10 @@
 
     @Override
     public void destroy() {
+        if (mNativeThinWebViewImpl == 0) return;
         mCompositorView.destroy();
-        if (mNativeThinWebViewImpl != 0) {
-            ThinWebViewImplJni.get().destroy(mNativeThinWebViewImpl, ThinWebViewImpl.this);
-            mNativeThinWebViewImpl = 0;
-        }
+        ThinWebViewImplJni.get().destroy(mNativeThinWebViewImpl, ThinWebViewImpl.this);
+        mNativeThinWebViewImpl = 0;
     }
 
     @Override
@@ -80,6 +80,7 @@
 
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        if (mNativeThinWebViewImpl == 0) return;
         if (w != oldw || h != oldh) {
             ThinWebViewImplJni.get().sizeChanged(
                     mNativeThinWebViewImpl, ThinWebViewImpl.this, w, h);
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index 9775902..f4b3dff 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -343,11 +343,6 @@
   if (app_id == arc::kPlayStoreAppId) {
     LoadPlayStoreIcon(icon_compression, size_hint_in_dip, icon_effects,
                       std::move(callback));
-  } else if (allow_placeholder_icon) {
-    constexpr bool is_placeholder_icon = true;
-    LoadIconFromResource(icon_compression, size_hint_in_dip,
-                         IDR_APP_DEFAULT_ICON, is_placeholder_icon,
-                         icon_effects, std::move(callback));
   } else {
     arc_icon_once_loader_.LoadIcon(
         app_id, size_hint_in_dip, icon_compression,
diff --git a/chrome/browser/apps/app_service/extension_apps.cc b/chrome/browser/apps/app_service/extension_apps.cc
index 78cc3426..7d59ab6 100644
--- a/chrome/browser/apps/app_service/extension_apps.cc
+++ b/chrome/browser/apps/app_service/extension_apps.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "ash/public/cpp/app_list/app_list_metrics.h"
+#include "ash/public/cpp/multi_user_window_manager.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "base/bind.h"
 #include "base/callback.h"
@@ -29,6 +30,9 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/app_list/extension_app_utils.h"
 #include "chrome/browser/ui/app_list/extension_uninstaller.h"
+#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
+#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
+#include "chrome/browser/ui/ash/session_controller_client_impl.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -714,10 +718,16 @@
     return;
   }
 
-  DCHECK(!instance_registry_->ForOneInstance(
-      app_window->GetNativeWindow(),
-      [](const apps::InstanceUpdate& update) {}));
+  DCHECK(!instance_registry_->Exists(app_window->GetNativeWindow()));
   app_window_to_aura_window_[app_window] = app_window->GetNativeWindow();
+
+  // Attach window to multi-user manager now to let it manage visibility state
+  // of the window correctly.
+  if (SessionControllerClientImpl::IsMultiProfileAvailable()) {
+    MultiUserWindowManagerHelper::GetWindowManager()->SetWindowOwner(
+        app_window->GetNativeWindow(),
+        multi_user_util::GetAccountIdFromProfile(profile_));
+  }
   RegisterInstance(app_window, InstanceState::kStarted);
 }
 
@@ -727,12 +737,14 @@
     return;
   }
 
-  InstanceState state = InstanceState::kUnknown;
-  instance_registry_->ForOneInstance(
-      app_window->GetNativeWindow(),
-      [&state](const apps::InstanceUpdate& update) { state = update.State(); });
+  InstanceState state =
+      instance_registry_->GetState(app_window->GetNativeWindow());
+
+  // If the window is shown, it should be started, running and not hidden.
   state = static_cast<apps::InstanceState>(
       state | apps::InstanceState::kStarted | apps::InstanceState::kRunning);
+  state =
+      static_cast<apps::InstanceState>(state & ~apps::InstanceState::kHidden);
   RegisterInstance(app_window, state);
 }
 
@@ -741,9 +753,9 @@
     return;
   }
 
-  // For hidden |app_window|, the other state bit, running, active, and visible
-  // should be cleared, and the state is set back to the started state.
-  RegisterInstance(app_window, InstanceState::kStarted);
+  // For hidden |app_window|, the other state bit, started, running, active, and
+  // visible should be cleared.
+  RegisterInstance(app_window, InstanceState::kHidden);
 }
 
 void ExtensionApps::OnAppWindowRemoved(extensions::AppWindow* app_window) {
@@ -1244,17 +1256,14 @@
 
 void ExtensionApps::RegisterInstance(extensions::AppWindow* app_window,
                                      InstanceState new_state) {
-  InstanceState state = InstanceState::kUnknown;
-  instance_registry_->ForOneInstance(
-      app_window->GetNativeWindow(),
-      [&state](const apps::InstanceUpdate& update) { state = update.State(); });
+  aura::Window* window = app_window->GetNativeWindow();
 
-  // If |state| has been marked as |new_state|, we don't need to update.
-  if (state == new_state) {
+  // If the current state has been marked as |new_state|, we don't need to
+  // update.
+  if (instance_registry_->GetState(window) == new_state) {
     return;
   }
 
-  aura::Window* window = app_window->GetNativeWindow();
   if (new_state == InstanceState::kDestroyed) {
     DCHECK(base::Contains(app_window_to_aura_window_, app_window));
     window = app_window_to_aura_window_[app_window];
diff --git a/chrome/browser/chromeos/login/webview_login_browsertest.cc b/chrome/browser/chromeos/login/webview_login_browsertest.cc
index 490bd69..5791a4d 100644
--- a/chrome/browser/chromeos/login/webview_login_browsertest.cc
+++ b/chrome/browser/chromeos/login/webview_login_browsertest.cc
@@ -38,6 +38,7 @@
 #include "chrome/browser/chromeos/policy/device_policy_builder.h"
 #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.h"
 #include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
 #include "chrome/browser/ui/login/login_handler.h"
@@ -69,7 +70,6 @@
 #include "crypto/nss_util.h"
 #include "crypto/nss_util_internal.h"
 #include "crypto/scoped_test_nss_db.h"
-#include "crypto/scoped_test_system_nss_key_slot.h"
 #include "media/base/media_switches.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/cookies/canonical_cookie.h"
@@ -603,49 +603,13 @@
  public:
   WebviewClientCertsLoginTest() = default;
 
-  // Installs a testing system slot and imports a client certificate into it.
-  void SetUpClientCertInSystemSlot() {
-    bool system_slot_constructed_successfully = false;
-    base::RunLoop loop;
-    base::PostTaskAndReply(
-        FROM_HERE, {content::BrowserThread::IO},
-        base::BindOnce(&WebviewClientCertsLoginTest::SetUpTestSystemSlotOnIO,
-                       base::Unretained(this),
-                       &system_slot_constructed_successfully),
-        loop.QuitClosure());
-    loop.Run();
-    ASSERT_TRUE(system_slot_constructed_successfully);
-
-    ASSERT_TRUE(ImportSystemSlotClientCert(test_system_slot_->slot()));
-  }
-
- protected:
-  void TearDownOnMainThread() override {
-    TearDownTestSystemSlot();
-    WebviewClientCertsLoginTestBase::TearDownOnMainThread();
+  // Imports a client certificate into the system slot.
+  bool SetUpClientCertInSystemSlot() {
+    return ImportSystemSlotClientCert(system_nss_key_slot_mixin_.slot());
   }
 
  private:
-  void SetUpTestSystemSlotOnIO(bool* out_system_slot_constructed_successfully) {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-    test_system_slot_ = std::make_unique<crypto::ScopedTestSystemNSSKeySlot>();
-    *out_system_slot_constructed_successfully =
-        test_system_slot_->ConstructedSuccessfully();
-  }
-
-  void TearDownTestSystemSlot() {
-    base::RunLoop loop;
-    base::PostTaskAndReply(
-        FROM_HERE, {content::BrowserThread::IO},
-        base::BindOnce(&WebviewClientCertsLoginTest::TearDownTestSystemSlotOnIO,
-                       base::Unretained(this)),
-        loop.QuitClosure());
-    loop.Run();
-  }
-
-  void TearDownTestSystemSlotOnIO() { test_system_slot_.reset(); }
-
-  std::unique_ptr<crypto::ScopedTestSystemNSSKeySlot> test_system_slot_;
+  ScopedTestSystemNSSKeySlotMixin system_nss_key_slot_mixin_{&mixin_host_};
 
   DISALLOW_COPY_AND_ASSIGN(WebviewClientCertsLoginTest);
 };
@@ -658,7 +622,7 @@
 // ASAN/LSAN. crbug.com/1022034
 IN_PROC_BROWSER_TEST_F(WebviewClientCertsLoginTest,
                        DISABLED_SigninFrameNoAuthorityGiven) {
-  ASSERT_NO_FATAL_FAILURE(SetUpClientCertInSystemSlot());
+  ASSERT_TRUE(SetUpClientCertInSystemSlot());
   net::SpawnedTestServer::SSLOptions ssl_options;
   ssl_options.request_client_certificate = true;
   ASSERT_NO_FATAL_FAILURE(StartHttpsServer(ssl_options));
@@ -684,7 +648,7 @@
 // ASAN/LSAN. crbug.com/1022034
 IN_PROC_BROWSER_TEST_F(WebviewClientCertsLoginTest,
                        DISABLED_SigninFrameCertMultipleFiltersAutoSelected) {
-  ASSERT_NO_FATAL_FAILURE(SetUpClientCertInSystemSlot());
+  ASSERT_TRUE(SetUpClientCertInSystemSlot());
   net::SpawnedTestServer::SSLOptions ssl_options;
   ssl_options.request_client_certificate = true;
   ASSERT_NO_FATAL_FAILURE(StartHttpsServer(ssl_options));
@@ -711,7 +675,7 @@
 // ASAN/LSAN. crbug.com/1022034
 IN_PROC_BROWSER_TEST_F(WebviewClientCertsLoginTest,
                        DISABLED_SigninFrameCertNotAutoSelected) {
-  ASSERT_NO_FATAL_FAILURE(SetUpClientCertInSystemSlot());
+  ASSERT_TRUE(SetUpClientCertInSystemSlot());
   net::SpawnedTestServer::SSLOptions ssl_options;
   ssl_options.request_client_certificate = true;
   ASSERT_NO_FATAL_FAILURE(StartHttpsServer(ssl_options));
@@ -732,7 +696,7 @@
 // ASAN/LSAN. crbug.com/1022034
 IN_PROC_BROWSER_TEST_F(WebviewClientCertsLoginTest,
                        DISABLED_SigninFrameAuthorityGiven) {
-  ASSERT_NO_FATAL_FAILURE(SetUpClientCertInSystemSlot());
+  ASSERT_TRUE(SetUpClientCertInSystemSlot());
   net::SpawnedTestServer::SSLOptions ssl_options;
   ssl_options.request_client_certificate = true;
   base::FilePath ca_path =
@@ -763,7 +727,7 @@
 // ASAN/LSAN. crbug.com/1022034
 IN_PROC_BROWSER_TEST_F(WebviewClientCertsLoginTest,
                        DISABLED_SigninFrameAuthorityGivenNoMatchingCert) {
-  ASSERT_NO_FATAL_FAILURE(SetUpClientCertInSystemSlot());
+  ASSERT_TRUE(SetUpClientCertInSystemSlot());
   net::SpawnedTestServer::SSLOptions ssl_options;
   ssl_options.request_client_certificate = true;
   base::FilePath ca_path =
@@ -790,7 +754,7 @@
 // TODO(crbug.com/949511) The test is flaky (timeout) on MSAN.
 IN_PROC_BROWSER_TEST_F(WebviewClientCertsLoginTest,
                        DISABLED_SigninFrameIntermediateAuthorityUnknown) {
-  ASSERT_NO_FATAL_FAILURE(SetUpClientCertInSystemSlot());
+  ASSERT_TRUE(SetUpClientCertInSystemSlot());
   net::SpawnedTestServer::SSLOptions ssl_options;
   ssl_options.request_client_certificate = true;
   base::FilePath ca_path = net::GetTestCertsDirectory().Append(
@@ -818,7 +782,7 @@
 // ASAN/LSAN. crbug.com/1022034
 IN_PROC_BROWSER_TEST_F(WebviewClientCertsLoginTest,
                        DISABLED_SigninFrameIntermediateAuthorityKnown) {
-  ASSERT_NO_FATAL_FAILURE(SetUpClientCertInSystemSlot());
+  ASSERT_TRUE(SetUpClientCertInSystemSlot());
   net::SpawnedTestServer::SSLOptions ssl_options;
   ssl_options.request_client_certificate = true;
   base::FilePath ca_path = net::GetTestCertsDirectory().Append(
@@ -852,7 +816,7 @@
 // deprecated and removed. https://crbug.com/849710.
 IN_PROC_BROWSER_TEST_F(WebviewClientCertsLoginTest,
                        DISABLED_ClientCertRequestedInOtherWebView) {
-  ASSERT_NO_FATAL_FAILURE(SetUpClientCertInSystemSlot());
+  ASSERT_TRUE(SetUpClientCertInSystemSlot());
   net::SpawnedTestServer::SSLOptions ssl_options;
   ssl_options.request_client_certificate = true;
   ASSERT_NO_FATAL_FAILURE(StartHttpsServer(ssl_options));
diff --git a/chrome/browser/chromeos/power/smart_charging/user_charging_event.proto b/chrome/browser/chromeos/power/smart_charging/user_charging_event.proto
index 32061b4..2e112a6 100644
--- a/chrome/browser/chromeos/power/smart_charging/user_charging_event.proto
+++ b/chrome/browser/chromeos/power/smart_charging/user_charging_event.proto
@@ -89,6 +89,9 @@
     optional int32 screen_brightness_percent = 19;
     // Charge voltage in mV.
     optional int32 voltage = 20;
+    // Whether there's any shutdown/suspend action between the last charge
+    // and current event.
+    optional bool halt_from_last_charge = 21;
   }
 
   message Event {
diff --git a/chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.cc b/chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.cc
new file mode 100644
index 0000000..de45b00
--- /dev/null
+++ b/chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.cc
@@ -0,0 +1,62 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.h"
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/task/post_task.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "crypto/scoped_test_system_nss_key_slot.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+ScopedTestSystemNSSKeySlotMixin::ScopedTestSystemNSSKeySlotMixin(
+    InProcessBrowserTestMixinHost* host)
+    : InProcessBrowserTestMixin(host) {}
+
+ScopedTestSystemNSSKeySlotMixin::~ScopedTestSystemNSSKeySlotMixin() = default;
+
+PK11SlotInfo* ScopedTestSystemNSSKeySlotMixin::slot() {
+  return scoped_test_system_nss_key_slot_->slot();
+}
+
+void ScopedTestSystemNSSKeySlotMixin::SetUpOnMainThread() {
+  bool system_slot_initialized_successfully = false;
+  base::RunLoop loop;
+  base::PostTaskAndReply(
+      FROM_HERE, {content::BrowserThread::IO},
+      base::BindOnce(&ScopedTestSystemNSSKeySlotMixin::InitializeOnIo,
+                     base::Unretained(this),
+                     &system_slot_initialized_successfully),
+      loop.QuitClosure());
+  loop.Run();
+  ASSERT_TRUE(system_slot_initialized_successfully);
+}
+
+void ScopedTestSystemNSSKeySlotMixin::TearDownOnMainThread() {
+  base::RunLoop loop;
+  base::PostTaskAndReply(
+      FROM_HERE, {content::BrowserThread::IO},
+      base::BindOnce(&ScopedTestSystemNSSKeySlotMixin::DestroyOnIo,
+                     base::Unretained(this)),
+      loop.QuitClosure());
+  loop.Run();
+}
+
+void ScopedTestSystemNSSKeySlotMixin::InitializeOnIo(bool* out_success) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  scoped_test_system_nss_key_slot_ =
+      std::make_unique<crypto::ScopedTestSystemNSSKeySlot>();
+  *out_success = scoped_test_system_nss_key_slot_->ConstructedSuccessfully();
+}
+
+void ScopedTestSystemNSSKeySlotMixin::DestroyOnIo() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  scoped_test_system_nss_key_slot_.reset();
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.h b/chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.h
new file mode 100644
index 0000000..2793c90
--- /dev/null
+++ b/chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.h
@@ -0,0 +1,55 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_SCOPED_TEST_SYSTEM_NSS_KEY_SLOT_MIXIN_H_
+#define CHROME_BROWSER_CHROMEOS_SCOPED_TEST_SYSTEM_NSS_KEY_SLOT_MIXIN_H_
+
+#include <pk11pub.h>
+#include <memory>
+
+#include "chrome/test/base/mixin_based_in_process_browser_test.h"
+
+namespace crypto {
+class ScopedTestSystemNSSKeySlot;
+}
+
+namespace chromeos {
+
+// Owns a persistent NSS software database in a temporary directory and the
+// association of the system slot with this database.
+//
+// This mixin performs the blocking initialization/destruction in the
+// {SetUp|TearDown}OnMainThread methods.
+class ScopedTestSystemNSSKeySlotMixin final : public InProcessBrowserTestMixin {
+ public:
+  explicit ScopedTestSystemNSSKeySlotMixin(InProcessBrowserTestMixinHost* host);
+  ScopedTestSystemNSSKeySlotMixin(const ScopedTestSystemNSSKeySlotMixin&) =
+      delete;
+  ScopedTestSystemNSSKeySlotMixin& operator=(
+      const ScopedTestSystemNSSKeySlotMixin&) = delete;
+  ~ScopedTestSystemNSSKeySlotMixin() override;
+
+  crypto::ScopedTestSystemNSSKeySlot* scoped_test_system_nss_key_slot() {
+    return scoped_test_system_nss_key_slot_.get();
+  }
+  const crypto::ScopedTestSystemNSSKeySlot* scoped_test_system_nss_key_slot()
+      const {
+    return scoped_test_system_nss_key_slot_.get();
+  }
+  PK11SlotInfo* slot();
+
+  void SetUpOnMainThread() override;
+  void TearDownOnMainThread() override;
+
+ private:
+  void InitializeOnIo(bool* out_success);
+  void DestroyOnIo();
+
+  std::unique_ptr<crypto::ScopedTestSystemNSSKeySlot>
+      scoped_test_system_nss_key_slot_;
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_SCOPED_TEST_SYSTEM_NSS_KEY_SLOT_MIXIN_H_
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index ff9cfd9..40812d3 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -242,7 +242,6 @@
     // We'll crash the renderer after it's loaded.
     case FINAL_STATUS_RENDERER_CRASHED:
     case FINAL_STATUS_CANCELLED:
-    case FINAL_STATUS_NAVIGATION_UNCOMMITTED:
     case FINAL_STATUS_NON_EMPTY_BROWSING_INSTANCE:
       return false;
     default:
@@ -1894,14 +1893,6 @@
   PrerenderTestURL(url, FINAL_STATUS_UNSUPPORTED_SCHEME, 0);
 }
 
-// Checks that a prerender that creates an audio stream (via a WebAudioDevice)
-// is cancelled.
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderWebAudioDevice) {
-  DisableLoadEventCheck();
-  PrerenderTestURL("/prerender/prerender_web_audio_device.html",
-                   FINAL_STATUS_CREATING_AUDIO_STREAM, 0);
-}
-
 // Checks that deferred redirects in a synchronous XHR abort the prerender.
 IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderDeferredSynchronousXHR) {
   // Disable load event checks because they race with cancellation.
diff --git a/chrome/browser/prerender/prerender_final_status.h b/chrome/browser/prerender/prerender_final_status.h
index ef00240f..4673f4a 100644
--- a/chrome/browser/prerender/prerender_final_status.h
+++ b/chrome/browser/prerender/prerender_final_status.h
@@ -60,10 +60,10 @@
   FINAL_STATUS_OPEN_URL = 40,
   // Obsolete: FINAL_STATUS_WOULD_HAVE_BEEN_USED = 41,
   FINAL_STATUS_REGISTER_PROTOCOL_HANDLER = 42,
-  FINAL_STATUS_CREATING_AUDIO_STREAM = 43,
+  // Obsolete: FINAL_STATUS_CREATING_AUDIO_STREAM = 43,
   // Obsolete: FINAL_STATUS_PAGE_BEING_CAPTURED = 44,
   FINAL_STATUS_BAD_DEFERRED_REDIRECT = 45,
-  FINAL_STATUS_NAVIGATION_UNCOMMITTED = 46,
+  // Obsolete: FINAL_STATUS_NAVIGATION_UNCOMMITTED = 46,
   // Obsolete: FINAL_STATUS_NEW_NAVIGATION_ENTRY = 47,
   // Obsolete: FINAL_STATUS_COOKIE_STORE_NOT_LOADED = 48,
   // Obsolete: FINAL_STATUS_COOKIE_CONFLICT = 49,
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index 02e3312..7bf6df9 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -198,13 +198,9 @@
   last_prerender_start_time_ =
       GetCurrentTimeTicks() -
       base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs);
-
-  MediaCaptureDevicesDispatcher::GetInstance()->AddObserver(this);
 }
 
 PrerenderManager::~PrerenderManager() {
-  MediaCaptureDevicesDispatcher::GetInstance()->RemoveObserver(this);
-
   // The earlier call to KeyedService::Shutdown() should have
   // emptied these vectors already.
   DCHECK(active_prerenders_.empty());
@@ -394,22 +390,10 @@
     return nullptr;
   }
 
-  if (WebContents* new_web_contents =
-          prerender_data->contents()->prerender_contents()) {
-    if (web_contents == new_web_contents)
-      return nullptr;  // Do not swap in to ourself.
-
-    // We cannot swap in if there is no last committed entry, because we would
-    // show a blank page under an existing entry from the current tab.  Even if
-    // there is a pending entry, it may not commit.
-    // TODO(creis): If there is a pending navigation and no last committed
-    // entry, we might be able to transfer the network request instead.
-    if (!new_web_contents->GetController().CanPruneAllButLastCommitted()) {
-      // Abort this prerender so it is not used later. http://crbug.com/292121
-      prerender_data->contents()->Destroy(FINAL_STATUS_NAVIGATION_UNCOMMITTED);
-      return nullptr;
-    }
-  }
+  WebContents* prerender_web_contents =
+      prerender_data->contents()->prerender_contents();
+  if (prerender_web_contents && web_contents == prerender_web_contents)
+    return nullptr;  // Do not swap in to ourself.
 
   // Do not swap if the target WebContents is not the only WebContents in its
   // current BrowsingInstance.
@@ -1226,21 +1210,6 @@
       "Consider whether a failed prerender should fallback to preconnect");
 }
 
-void PrerenderManager::OnCreatingAudioStream(int render_process_id,
-                                             int render_frame_id) {
-  content::RenderFrameHost* render_frame_host =
-      content::RenderFrameHost::FromID(render_process_id, render_frame_id);
-  WebContents* tab = WebContents::FromRenderFrameHost(render_frame_host);
-  if (!tab)
-    return;
-
-  PrerenderContents* prerender_contents = GetPrerenderContents(tab);
-  if (!prerender_contents)
-    return;
-
-  prerender_contents->Destroy(FINAL_STATUS_CREATING_AUDIO_STREAM);
-}
-
 void PrerenderManager::RecordNetworkBytesConsumed(Origin origin,
                                                   int64_t prerender_bytes) {
   if (!IsNoStatePrefetchEnabled())
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h
index cda0b3fc..05e0cc0 100644
--- a/chrome/browser/prerender/prerender_manager.h
+++ b/chrome/browser/prerender/prerender_manager.h
@@ -18,7 +18,6 @@
 #include "base/time/clock.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/prerender/prerender_config.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/prerender/prerender_final_status.h"
@@ -74,8 +73,7 @@
 // views of web pages. All methods must be called on the UI thread unless
 // indicated otherwise.
 class PrerenderManager : public content::RenderProcessHostObserver,
-                         public KeyedService,
-                         public MediaCaptureDevicesDispatcher::Observer {
+                         public KeyedService {
  public:
   enum PrerenderManagerMode {
     // Deprecated: Enables all types of prerendering for any origin.
@@ -292,10 +290,6 @@
   // Record a final status of a prerendered page in a histogram.
   void RecordFinalStatus(Origin origin, FinalStatus final_status) const;
 
-  // MediaCaptureDevicesDispatcher::Observer
-  void OnCreatingAudioStream(int render_process_id,
-                             int render_frame_id) override;
-
   const Config& config() const { return config_; }
   Config& mutable_config() { return config_; }
 
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.html
index 18576d7..8d9f287 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.html
@@ -30,8 +30,8 @@
       <div slot="bottom-buttons" class="flex layout horizontal">
         <div class="flex"></div>
         <oobe-text-button id="next-button" inverse on-tap="onNextTap_"
-            disabled="[[buttonsDisabled]]" hidden="[[moreContents]]">
-          <div id="button-text" i18n-content="assistantReadyButton"></div>
+            disabled="[[buttonsDisabled]]" hidden="[[moreContents]]"
+            text-key="assistantReadyButton">
         </oobe-text-button>
       </div>
     </oobe-dialog>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.js
index 1618aed..efa7e33f 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.js
@@ -13,7 +13,7 @@
 Polymer({
   is: 'assistant-get-more',
 
-  behaviors: [OobeDialogHostBehavior],
+  behaviors: [OobeI18nBehavior, OobeDialogHostBehavior],
 
   properties: {
     /**
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.html
index 1bf10bd7..086e4350 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.html
@@ -31,12 +31,10 @@
       <div slot="bottom-buttons" class="flex layout horizontal">
         <div class="flex"></div>
         <oobe-text-button id="skip-button" on-tap="onSkipTap_"
-            disabled="[[buttonsDisabled]]">
-          <div i18n-content="assistantOptinSkipButton"></div>
+            disabled="[[buttonsDisabled]]" text-key="assistantOptinSkipButton">
         </oobe-text-button>
-        <oobe-text-button id="retry-button" inverse
-            on-tap="onRetryTap_" disabled="[[buttonsDisabled]]">
-          <div i18n-content="assistantOptinRetryButton"></div>
+        <oobe-text-button id="retry-button" inverse on-tap="onRetryTap_"
+            disabled="[[buttonsDisabled]]" text-key="assistantOptinRetryButton">
         </oobe-text-button>
       </div>
     </oobe-dialog>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.js
index c9e0479..3f798fc5 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.js
@@ -12,7 +12,7 @@
 Polymer({
   is: 'assistant-loading',
 
-  behaviors: [OobeDialogHostBehavior],
+  behaviors: [OobeI18nBehavior, OobeDialogHostBehavior],
 
   properties: {
     /**
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin.js
index 13a30f6..a074a46 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin.js
@@ -4,6 +4,7 @@
 
 // <include src="../login/hd-iron-icon.js">
 // <include src="../login/oobe_types.js">
+// <include src="../login/oobe_i18n_behavior.js">
 // <include src="../login/oobe_buttons.js">
 // <include src="../login/oobe_dialog_host_behavior.js">
 // <include src="../login/oobe_dialog.js">
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js
index 9f85f90..98d5658 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js
@@ -20,7 +20,7 @@
 Polymer({
   is: 'assistant-optin-flow',
 
-  behaviors: [OobeDialogHostBehavior],
+  behaviors: [OobeI18nBehavior, OobeDialogHostBehavior],
 
   /**
    * Indicates the type of the opt-in flow.
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.html
index a41a1c0..f6b91d2 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.html
@@ -24,7 +24,7 @@
         <div class="flex"></div>
         <oobe-next-button id="next-button" inverse on-tap="onNextTap_"
             disabled="[[buttonsDisabled]]" hidden="[[moreContents]]">
-          <div id="next-button-text"></div>
+          <div id="next-button-text" slot="text"></div>
         </oobe-next-button>
       </div>
     </oobe-dialog>
@@ -39,9 +39,8 @@
         </div>
       </div>
       <div slot="button-container">
-        <oobe-text-button inverse id="overlay-close-button">
-          <div i18n-content="assistantOptinOKButton" id="close-button-text">
-          </div>
+        <oobe-text-button inverse id="overlay-close-button"
+            text-key="assistantOptinOKButton">
         </oobe-text-button>
       </div>
     </cr-dialog>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.js
index 1d37337..cda37a7 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.js
@@ -13,7 +13,7 @@
 Polymer({
   is: 'assistant-third-party',
 
-  behaviors: [OobeDialogHostBehavior],
+  behaviors: [OobeI18nBehavior, OobeDialogHostBehavior],
 
   properties: {
     /**
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.html
index 7e8084b..cd983ad 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.html
@@ -36,11 +36,11 @@
         <div class="flex"></div>
         <oobe-text-button id="skip-button" on-tap="onSkipTap_"
             disabled="[[buttonsDisabled]]">
-          <div id="skip-button-text"></div>
+          <div id="skip-button-text" slot="text"></div>
         </oobe-text-button>
         <oobe-text-button id="next-button" inverse on-tap="onNextTap_"
             disabled="[[buttonsDisabled]]">
-          <div id="next-button-text"></div>
+          <div id="next-button-text" slot="text"></div>
         </oobe-text-button>
       </div>
     </oobe-dialog>
@@ -53,9 +53,8 @@
         </div>
       </div>
       <div slot="button-container">
-        <oobe-text-button inverse id="overlay-close-button">
-          <div i18n-content="assistantOptinOKButton" id="close-button-text">
-          </div>
+        <oobe-text-button inverse id="overlay-close-button"
+            text-key="assistantOptinOKButton">
         </oobe-text-button>
       </div>
     </cr-dialog>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.js
index aa6ab1c..1269b0b 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.js
@@ -14,7 +14,7 @@
 Polymer({
   is: 'assistant-value-prop',
 
-  behaviors: [OobeDialogHostBehavior],
+  behaviors: [OobeI18nBehavior, OobeDialogHostBehavior],
 
   properties: {
     /**
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html
index e0938d1b..76bff179 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html
@@ -82,16 +82,14 @@
       <div slot="bottom-buttons" class="flex layout horizontal">
         <div class="flex"></div>
         <oobe-text-button id="skip-button" on-tap="onSkipTap_"
-            disabled="[[buttonsDisabled]]">
-          <div i18n-content="assistantOptinNoThanksButton"></div>
+            disabled="[[buttonsDisabled]]"
+            text-key="assistantOptinNoThanksButton">
         </oobe-text-button>
         <oobe-text-button id="later-button" on-tap="onSkipTap_"
-            disabled="[[buttonsDisabled]]">
-          <div i18n-content="assistantOptinLaterButton"></div>
+            disabled="[[buttonsDisabled]]" text-key="assistantOptinLaterButton">
         </oobe-text-button>
         <oobe-text-button id="agree-button" inverse on-tap="onAgreeTap_"
-            disabled="[[buttonsDisabled]]">
-          <div i18n-content="assistantOptinAgreeButton"></div>
+            disabled="[[buttonsDisabled]]" text-key="assistantOptinAgreeButton">
         </oobe-text-button>
       </div>
     </oobe-dialog>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.js
index 595a8dc..e695fb75 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.js
@@ -14,7 +14,7 @@
 Polymer({
   is: 'assistant-voice-match',
 
-  behaviors: [OobeDialogHostBehavior],
+  behaviors: [OobeI18nBehavior, OobeDialogHostBehavior],
 
   /**
    * Current recording index.
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/css/wallpaper_manager.css b/chrome/browser/resources/chromeos/wallpaper_manager/css/wallpaper_manager.css
index f9a6316..567e5c0 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/css/wallpaper_manager.css
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/css/wallpaper_manager.css
@@ -441,6 +441,7 @@
   font-family: 'Roboto';
   font-size: 13px;
   font-weight: 500;
+  line-height: 13px;
 }
 
 :not(.no-images) .top-header-contents {
diff --git a/chrome/browser/ui/android/widget/BUILD.gn b/chrome/browser/ui/android/widget/BUILD.gn
index 3de40ce..95cdb33 100644
--- a/chrome/browser/ui/android/widget/BUILD.gn
+++ b/chrome/browser/ui/android/widget/BUILD.gn
@@ -22,6 +22,7 @@
     "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonLayout.java",
     "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescription.java",
     "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayout.java",
+    "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditText.java",
     "java/src/org/chromium/chrome/browser/ui/widget/RoundedCornerImageView.java",
     "java/src/org/chromium/chrome/browser/ui/widget/TintedDrawable.java",
     "java/src/org/chromium/chrome/browser/ui/widget/ViewResourceFrameLayout.java",
@@ -100,6 +101,7 @@
     "java/src/org/chromium/chrome/browser/ui/widget/PromoDialogTest.java",
     "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonLayoutTest.java",
     "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayoutTest.java",
+    "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditTextTest.java",
     "java/src/org/chromium/chrome/browser/ui/widget/WrappingLayoutTest.java",
     "java/src/org/chromium/chrome/browser/ui/widget/highlight/ViewHighlighterTest.java",
   ]
diff --git a/chrome/browser/ui/android/widget/java/res/layout/radio_button_with_description.xml b/chrome/browser/ui/android/widget/java/res/layout/radio_button_with_description.xml
index 4c8d4a7..e63c9ce5 100644
--- a/chrome/browser/ui/android/widget/java/res/layout/radio_button_with_description.xml
+++ b/chrome/browser/ui/android/widget/java/res/layout/radio_button_with_description.xml
@@ -18,7 +18,7 @@
         android:background="@null" />
 
     <TextView
-        android:id="@+id/title"
+        android:id="@+id/primary"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_toEndOf="@id/radio_button"
@@ -29,8 +29,8 @@
         android:id="@+id/description"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_alignStart="@id/title"
-        android:layout_below="@id/title"
+        android:layout_alignStart="@id/primary"
+        android:layout_below="@id/primary"
         android:textAppearance="@style/TextAppearance.BlackHint2"
         android:visibility="gone" />
 </merge>
diff --git a/chrome/browser/ui/android/widget/java/res/layout/radio_button_with_edit_text.xml b/chrome/browser/ui/android/widget/java/res/layout/radio_button_with_edit_text.xml
new file mode 100644
index 0000000..7c77fb0
--- /dev/null
+++ b/chrome/browser/ui/android/widget/java/res/layout/radio_button_with_edit_text.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<!-- RadioButtonWithEditText extends RelativeLayout -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <RadioButton
+        android:id="@+id/radio_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginEnd="16dp"
+        android:clickable="false"
+        android:focusable="false"
+        android:background="@null" />
+
+    <!-- Hint will be added programmatically so we're ignoring the lint warning. -->
+    <EditText
+        tools:ignore="LabelFor"
+        android:id="@+id/edit_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_toEndOf="@id/radio_button"
+        android:layout_centerVertical="true"
+        android:textAppearance="@style/TextAppearance.BlackTitle1"
+        android:inputType="text" />
+
+    <!-- This TextView is hidden if it has no text, so the initial visibility should be "gone". -->
+    <TextView
+        android:id="@+id/description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignStart="@id/edit_text"
+        android:layout_below="@id/edit_text"
+        android:textAppearance="@style/TextAppearance.BlackHint2"
+        android:visibility="gone" />
+</merge>
diff --git a/chrome/browser/ui/android/widget/java/res/values/attrs.xml b/chrome/browser/ui/android/widget/java/res/values/attrs.xml
index e67e3de..04def68 100644
--- a/chrome/browser/ui/android/widget/java/res/values/attrs.xml
+++ b/chrome/browser/ui/android/widget/java/res/values/attrs.xml
@@ -23,10 +23,15 @@
     </declare-styleable>
 
     <declare-styleable name="RadioButtonWithDescription">
-        <attr name="titleText" format="string" />
+        <attr name="primaryText" format="string" />
         <attr name="descriptionText" format="string" />
     </declare-styleable>
 
+    <declare-styleable name="RadioButtonWithEditText">
+        <attr name="android:hint"/>
+        <attr name="android:inputType" />
+    </declare-styleable>
+
     <declare-styleable name="RoundedCornerImageView">
         <attr name="cornerRadiusTopStart" format="dimension" />
         <attr name="cornerRadiusTopEnd" format="dimension" />
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescription.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescription.java
index aef401f..48caa71 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescription.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescription.java
@@ -17,10 +17,30 @@
 import android.widget.RadioButton;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
+
 import java.util.List;
 
 /**
- * A RadioButton with a title and descriptive text to the right.
+ * <p>
+ * A RadioButton with a primary and descriptive text to the right.
+ * The radio button is designed to be contained in a group, with {@link
+ * RadioButtonWithDescriptionLayout} as the parent view. By default, the object will be inflated
+ * from {@link R.layout.radio_button_with_description).
+ * </p>
+ *
+ * <p>
+ * The primary of the text and an optional description to be contained in the group may be set in
+ * XML. Sample declaration in XML:
+ * <pre> {@code
+ *   <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+ *      android:id="@+id/system_default"
+ *      android:layout_width="match_parent"
+ *      android:layout_height="wrap_content"
+ *      android:background="?attr/selectableItemBackground"
+ *      app:primaryText="@string/feature_foo_option_one"
+ *      app:descriptionText="@string/feature_foo_option_one_description" />
+ * } </pre>
+ * </p>
  */
 public class RadioButtonWithDescription extends RelativeLayout implements OnClickListener {
     /**
@@ -35,8 +55,9 @@
     }
 
     private RadioButton mRadioButton;
-    private TextView mTitle;
+    private TextView mPrimary;
     private TextView mDescription;
+
     private ButtonCheckedStateChangedListener mButtonCheckedStateChangedListener;
 
     private List<RadioButtonWithDescription> mGroup;
@@ -49,11 +70,9 @@
      */
     public RadioButtonWithDescription(Context context, AttributeSet attrs) {
         super(context, attrs);
-        LayoutInflater.from(context).inflate(R.layout.radio_button_with_description, this, true);
+        LayoutInflater.from(context).inflate(getLayoutResource(), this, true);
 
-        mRadioButton = (RadioButton) findViewById(R.id.radio_button);
-        mTitle = (TextView) findViewById(R.id.title);
-        mDescription = (TextView) findViewById(R.id.description);
+        setViewsInternal();
 
         if (attrs != null) applyAttributes(attrs);
 
@@ -76,12 +95,53 @@
         setFocusable(true);
     }
 
-    private void applyAttributes(AttributeSet attrs) {
+    /**
+     * Set the view elements that included in xml internally.
+     */
+    protected void setViewsInternal() {
+        mRadioButton = getRadioButtonView();
+        mPrimary = getPrimaryTextView();
+        mDescription = getDescriptionTextView();
+    }
+
+    /**
+     * @return The layout resource id used for inflating this {@link RadioButtonWithDescription}.
+     */
+    protected int getLayoutResource() {
+        return R.layout.radio_button_with_description;
+    }
+
+    /**
+     * @return RadioButton View inside this {@link RadioButtonWithDescription}.
+     */
+    protected RadioButton getRadioButtonView() {
+        return (RadioButton) findViewById(R.id.radio_button);
+    }
+
+    /**
+     * @return TextView displayed as primary inside this {@link RadioButtonWithDescription}.
+     */
+    protected TextView getPrimaryTextView() {
+        return (TextView) findViewById(R.id.primary);
+    }
+
+    /**
+     * @return TextView displayed as description inside this {@link RadioButtonWithDescription}.
+     */
+    protected TextView getDescriptionTextView() {
+        return (TextView) findViewById(R.id.description);
+    }
+
+    /**
+     * Apply the customized AttributeSet to current view.
+     * @param attrs AttributeSet that will be applied to current view.
+     */
+    protected void applyAttributes(AttributeSet attrs) {
         TypedArray a = getContext().getTheme().obtainStyledAttributes(
                 attrs, R.styleable.RadioButtonWithDescription, 0, 0);
 
-        String titleText = a.getString(R.styleable.RadioButtonWithDescription_titleText);
-        if (titleText != null) mTitle.setText(titleText);
+        String primaryText = a.getString(R.styleable.RadioButtonWithDescription_primaryText);
+        if (primaryText != null) mPrimary.setText(primaryText);
 
         String descriptionText =
                 a.getString(R.styleable.RadioButtonWithDescription_descriptionText);
@@ -89,7 +149,7 @@
             mDescription.setText(descriptionText);
             mDescription.setVisibility(View.VISIBLE);
         } else {
-            ((LayoutParams) mTitle.getLayoutParams()).addRule(RelativeLayout.CENTER_VERTICAL);
+            ((LayoutParams) mPrimary.getLayoutParams()).addRule(RelativeLayout.CENTER_VERTICAL);
         }
 
         a.recycle();
@@ -111,17 +171,17 @@
     }
 
     /**
-     * Sets the text shown in the title section.
+     * Sets the text shown in the primary section.
      */
-    public void setTitleText(CharSequence text) {
-        mTitle.setText(text);
+    public void setPrimaryText(CharSequence text) {
+        mPrimary.setText(text);
     }
 
     /**
-     * @return The text shown in the title section.
+     * @return The text shown in the primary section.
      */
-    public CharSequence getTitleText() {
-        return mTitle.getText();
+    public CharSequence getPrimaryText() {
+        return mPrimary.getText();
     }
 
     /**
@@ -131,10 +191,10 @@
         mDescription.setText(text);
 
         if (TextUtils.isEmpty(text)) {
-            ((LayoutParams) mTitle.getLayoutParams()).addRule(RelativeLayout.CENTER_VERTICAL);
+            ((LayoutParams) mPrimary.getLayoutParams()).addRule(RelativeLayout.CENTER_VERTICAL);
             mDescription.setVisibility(View.GONE);
         } else {
-            ((LayoutParams) mTitle.getLayoutParams()).removeRule(RelativeLayout.CENTER_VERTICAL);
+            ((LayoutParams) mPrimary.getLayoutParams()).removeRule(RelativeLayout.CENTER_VERTICAL);
             mDescription.setVisibility(View.VISIBLE);
         }
     }
@@ -187,11 +247,11 @@
         // LinearLayout (no id):
         // |-> RadioButtonWithDescription (id=sync_confirm_import_choice)
         // |   |-> RadioButton            (id=radio_button)
-        // |   |-> TextView               (id=title)
+        // |   |-> TextView               (id=primary)
         // |   \-> TextView               (id=description)
         // \-> RadioButtonWithDescription (id=sync_keep_separate_choice)
         //     |-> RadioButton            (id=radio_button)
-        //     |-> TextView               (id=title)
+        //     |-> TextView               (id=primary)
         //     \-> TextView               (id=description)
         //
         // This causes the automagic state saving and recovery to do the wrong thing and restore all
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayout.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayout.java
index 39e0ada..34febbf 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayout.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayout.java
@@ -10,16 +10,15 @@
 import android.view.ViewGroup;
 import android.widget.RadioGroup;
 
-import androidx.annotation.Nullable;
-
 import java.util.ArrayList;
 import java.util.List;
 
 /**
+ * <p>
  * Manages a group of exclusive RadioButtonWithDescriptions, automatically inserting a margin in
  * between the rows to prevent them from squishing together. Has the option to set an accessory view
  * on any given RadioButtonWithDescription. Only one accessory view per layout is supported.
- *
+ * <pre>
  * -------------------------------------------------
  * | O | MESSAGE #1                                |
  *        description_1                            |
@@ -27,41 +26,27 @@
  * | O | MESSAGE #N                                |
  *        description_n                            |
  * -------------------------------------------------
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * To declare in XML, define a RadioButtonWithDescriptionLayout that contains
+ * RadioButtonWithDescription and/or RadioButtonWithEditText children.
+ * For example:
+ * <pre>{@code
+ *  <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout
+ *       android:layout_width="match_parent"
+ *       android:layout_height="match_parent" >
+ *       <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+ *           ... />
+ *       <org.chromium.chrome.browser.ui.widget.RadioButtonWithEditText
+ *           ... />
+ *   </org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout>
+ * }</pre>
+ * </p>
  */
 public final class RadioButtonWithDescriptionLayout
         extends RadioGroup implements RadioButtonWithDescription.ButtonCheckedStateChangedListener {
-    /** Encapsulates information required to build a layout. */
-    public static class Option {
-        private final CharSequence mTitle;
-        private final CharSequence mDescription;
-        private final Object mTag;
-
-        /**
-         * @param title The title for this option. This will be displayed as the main text for the
-         *              checkbox.
-         * @param description The description for this option. This will be displayed as the subtext
-         *                    under the main text for the checkbox.
-         * @param tag The tag for this option. This can be used to identify the checkbox in event
-         *            listeners.
-         */
-        public Option(CharSequence title, CharSequence description, @Nullable Object tag) {
-            mTitle = title;
-            mDescription = description;
-            mTag = tag;
-        }
-
-        public CharSequence getTitle() {
-            return mTitle;
-        }
-
-        public CharSequence getDescription() {
-            return mDescription;
-        }
-
-        public Object getTag() {
-            return mTag;
-        }
-    }
 
     private final int mMarginBetweenRows;
     private final List<RadioButtonWithDescription> mRadioButtonsWithDescriptions;
@@ -110,24 +95,14 @@
     }
 
     /**
-     * Given a set of {@link Option} creates a set of {@link RadioButtonWithDescription}. When lists
-     * are built this way, there are two options for getting the checkbox you want in an event
-     * listener callback.
-     * 1. Set a tag on the Option and use that with {@link View#findViewWithTag).
-     * 2. Use the id passed through to {@link RadioGroup.OnCheckedChangeListener#onCheckedChanged}.
-     *    with {@link View#findViewById}.
-     *
-     * @param options List of options to add to this group.
+     * Add group of {@link RadioButtonWithDescription} into current layout. For buttons that already
+     * exist in other radio button group, the radio button group will be transferred into the group
+     * inside current layout.
+     * @param buttons List of {@link RadioButtonWithDescription} to add to this group.
      */
-    public void addOptions(List<Option> options) {
-        for (Option option : options) {
-            RadioButtonWithDescription b = new RadioButtonWithDescription(getContext(), null);
-            b.setTitleText(option.getTitle());
-            b.setDescriptionText(option.getDescription());
-            b.setTag(option.getTag());
-
+    public void addButtons(List<RadioButtonWithDescription> buttons) {
+        for (RadioButtonWithDescription b : buttons) {
             setupButton(b);
-
             addView(b, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
         }
 
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayoutTest.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayoutTest.java
index 8f89753..e8e00f4 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayoutTest.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayoutTest.java
@@ -9,6 +9,7 @@
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.UiThreadTestRule;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup.MarginLayoutParams;
 import android.widget.TextView;
@@ -20,8 +21,9 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.chrome.browser.ui.widget.test.R;
 
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -34,11 +36,11 @@
     private static final String ZERO_MARGIN_ASSERT_MESSAGE =
             "The last item should have a zero margin";
     private static final String TITLE_MATCH_ASSERT_MESSAGE =
-            "Title set through addOptions should match the view's title.";
+            "Primary text set through addButtons should match the view's primary text.";
     private static final String DESCRIPTION_MATCH_ASSERT_MESSAGE =
-            "Description set through addOptions should match the view's description.";
+            "Description set through addButtons should match the view's description.";
     private static final String TAG_MATCH_ASSERT_MESSAGE =
-            "Tag set through addOptions should match the view's tag.";
+            "Tag set through addButtons should match the view's tag.";
 
     @Rule
     public UiThreadTestRule mRule = new UiThreadTestRule();
@@ -56,12 +58,12 @@
     public void testMargins() {
         RadioButtonWithDescriptionLayout layout = new RadioButtonWithDescriptionLayout(mContext);
 
-        // Add one set of options.
-        List<RadioButtonWithDescriptionLayout.Option> options = new ArrayList<>();
-        options.add(new RadioButtonWithDescriptionLayout.Option("a", "a_desc", "a_tag"));
-        options.add(new RadioButtonWithDescriptionLayout.Option("b", "b_desc", "b_tag"));
-        options.add(new RadioButtonWithDescriptionLayout.Option("c", "c_desc", "c_tag"));
-        layout.addOptions(options);
+        // Add one set of buttons.
+        List<RadioButtonWithDescription> buttons =
+                Arrays.asList(createRadioButtonWithDescription("a", "a_desc", "a_tag"),
+                        createRadioButtonWithDescription("b", "b_desc", "b_tag"),
+                        createRadioButtonWithDescription("c", "c_desc", "c_tag"));
+        layout.addButtons(buttons);
         Assert.assertEquals(3, layout.getChildCount());
 
         // Test the margins.
@@ -76,12 +78,12 @@
             }
         }
 
-        // Add more options.
-        List<RadioButtonWithDescriptionLayout.Option> moreOptions = new ArrayList<>();
-        moreOptions.add(new RadioButtonWithDescriptionLayout.Option("d", "d_desc", null));
-        moreOptions.add(new RadioButtonWithDescriptionLayout.Option("e", "e_desc", null));
-        moreOptions.add(new RadioButtonWithDescriptionLayout.Option("f", "f_desc", null));
-        layout.addOptions(moreOptions);
+        // Add more buttons.
+        List<RadioButtonWithDescription> moreButtons =
+                Arrays.asList(createRadioButtonWithDescription("d", "d_desc", null),
+                        createRadioButtonWithDescription("e", "e_desc", null),
+                        createRadioButtonWithDescription("f", "f_desc", null));
+        layout.addButtons(moreButtons);
         Assert.assertEquals(6, layout.getChildCount());
 
         // Test the margins.
@@ -100,49 +102,49 @@
     @Test
     @SmallTest
     @UiThreadTest
-    public void testAddOptions() {
+    public void testAddButtons() {
         RadioButtonWithDescriptionLayout layout = new RadioButtonWithDescriptionLayout(mContext);
 
         // Add one set of options.
-        List<RadioButtonWithDescriptionLayout.Option> options = new ArrayList<>();
-        options.add(new RadioButtonWithDescriptionLayout.Option("a", "a_desc", "a_tag"));
-        options.add(new RadioButtonWithDescriptionLayout.Option("b", "b_desc", "b_tag"));
-        options.add(new RadioButtonWithDescriptionLayout.Option("c", "c_desc", "c_tag"));
-        layout.addOptions(options);
+        List<RadioButtonWithDescription> buttons =
+                Arrays.asList(createRadioButtonWithDescription("a", "a_desc", "a_tag"),
+                        createRadioButtonWithDescription("b", "b_desc", "b_tag"),
+                        createRadioButtonWithDescription("c", "c_desc", "c_tag"));
+        layout.addButtons(buttons);
         Assert.assertEquals(3, layout.getChildCount());
 
         for (int i = 0; i < layout.getChildCount(); i++) {
             RadioButtonWithDescription b = (RadioButtonWithDescription) layout.getChildAt(i);
-            Assert.assertEquals(
-                    TITLE_MATCH_ASSERT_MESSAGE, options.get(i).getTitle(), b.getTitleText());
-            Assert.assertEquals(DESCRIPTION_MATCH_ASSERT_MESSAGE, options.get(i).getDescription(),
-                    b.getDescriptionText());
-            Assert.assertEquals(TAG_MATCH_ASSERT_MESSAGE, options.get(i).getTag(), b.getTag());
+            Assert.assertEquals(TITLE_MATCH_ASSERT_MESSAGE, buttons.get(i).getPrimaryText(),
+                    b.getPrimaryText());
+            Assert.assertEquals(DESCRIPTION_MATCH_ASSERT_MESSAGE,
+                    buttons.get(i).getDescriptionText(), b.getDescriptionText());
+            Assert.assertEquals(TAG_MATCH_ASSERT_MESSAGE, buttons.get(i).getTag(), b.getTag());
         }
 
         // Add even more options, but without tags.
-        List<RadioButtonWithDescriptionLayout.Option> moreOptions = new ArrayList<>();
-        moreOptions.add(new RadioButtonWithDescriptionLayout.Option("d", "d_desc", null));
-        moreOptions.add(new RadioButtonWithDescriptionLayout.Option("e", "e_desc", null));
-        moreOptions.add(new RadioButtonWithDescriptionLayout.Option("f", "f_desc", null));
-        layout.addOptions(moreOptions);
+        List<RadioButtonWithDescription> moreButtons =
+                Arrays.asList(createRadioButtonWithDescription("d", "d_desc", null),
+                        createRadioButtonWithDescription("e", "e_desc", null),
+                        createRadioButtonWithDescription("f", "f_desc", null));
+        layout.addButtons(moreButtons);
         Assert.assertEquals(6, layout.getChildCount());
         for (int i = 0; i < 3; i++) {
             RadioButtonWithDescription b = (RadioButtonWithDescription) layout.getChildAt(i);
-            Assert.assertEquals(
-                    TITLE_MATCH_ASSERT_MESSAGE, options.get(i).getTitle(), b.getTitleText());
-            Assert.assertEquals(DESCRIPTION_MATCH_ASSERT_MESSAGE, options.get(i).getDescription(),
-                    b.getDescriptionText());
-            Assert.assertEquals(TAG_MATCH_ASSERT_MESSAGE, options.get(i).getTag(), b.getTag());
+            Assert.assertEquals(TITLE_MATCH_ASSERT_MESSAGE, buttons.get(i).getPrimaryText(),
+                    b.getPrimaryText());
+            Assert.assertEquals(DESCRIPTION_MATCH_ASSERT_MESSAGE,
+                    buttons.get(i).getDescriptionText(), b.getDescriptionText());
+            Assert.assertEquals(TAG_MATCH_ASSERT_MESSAGE, buttons.get(i).getTag(), b.getTag());
         }
         for (int i = 3; i < 6; i++) {
             RadioButtonWithDescription b = (RadioButtonWithDescription) layout.getChildAt(i);
-            Assert.assertEquals(TITLE_MATCH_ASSERT_MESSAGE, moreOptions.get(i - 3).getTitle(),
-                    b.getTitleText());
+            Assert.assertEquals(TITLE_MATCH_ASSERT_MESSAGE, moreButtons.get(i - 3).getPrimaryText(),
+                    b.getPrimaryText());
             Assert.assertEquals(DESCRIPTION_MATCH_ASSERT_MESSAGE,
-                    moreOptions.get(i - 3).getDescription(), b.getDescriptionText());
+                    moreButtons.get(i - 3).getDescriptionText(), b.getDescriptionText());
             Assert.assertEquals(
-                    TAG_MATCH_ASSERT_MESSAGE, moreOptions.get(i - 3).getTag(), b.getTag());
+                    TAG_MATCH_ASSERT_MESSAGE, moreButtons.get(i - 3).getTag(), b.getTag());
         }
     }
 
@@ -154,11 +156,11 @@
                 new RadioButtonWithDescriptionLayout(mContext);
 
         // Add one set of options.
-        List<RadioButtonWithDescriptionLayout.Option> options = new ArrayList<>();
-        options.add(new RadioButtonWithDescriptionLayout.Option("a", "a_desc", null));
-        options.add(new RadioButtonWithDescriptionLayout.Option("b", "b_desc", null));
-        options.add(new RadioButtonWithDescriptionLayout.Option("c", "c_desc", null));
-        layout.addOptions(options);
+        List<RadioButtonWithDescription> buttons =
+                Arrays.asList(createRadioButtonWithDescription("a", "a_desc", null),
+                        createRadioButtonWithDescription("b", "b_desc", null),
+                        createRadioButtonWithDescription("c", "c_desc", null));
+        layout.addButtons(buttons);
         Assert.assertEquals(3, layout.getChildCount());
 
         // Nothing should be selected by default.
@@ -175,11 +177,11 @@
         }
 
         // Add even more options.
-        List<RadioButtonWithDescriptionLayout.Option> moreOptions = new ArrayList<>();
-        moreOptions.add(new RadioButtonWithDescriptionLayout.Option("d", "d_desc", null));
-        moreOptions.add(new RadioButtonWithDescriptionLayout.Option("e", "e_desc", null));
-        moreOptions.add(new RadioButtonWithDescriptionLayout.Option("f", "f_desc", null));
-        layout.addOptions(moreOptions);
+        List<RadioButtonWithDescription> moreButtons =
+                Arrays.asList(createRadioButtonWithDescription("d", "d_desc", null),
+                        createRadioButtonWithDescription("e", "e_desc", null),
+                        createRadioButtonWithDescription("f", "f_desc", null));
+        layout.addButtons(moreButtons);
         Assert.assertEquals(6, layout.getChildCount());
 
         // Second child should still be checked.
@@ -196,11 +198,11 @@
         final RadioButtonWithDescriptionLayout layout =
                 new RadioButtonWithDescriptionLayout(mContext);
 
-        List<RadioButtonWithDescriptionLayout.Option> options = new ArrayList<>();
-        options.add(new RadioButtonWithDescriptionLayout.Option("a", "a_desc", null));
-        options.add(new RadioButtonWithDescriptionLayout.Option("b", "b_desc", null));
-        options.add(new RadioButtonWithDescriptionLayout.Option("c", "c_desc", null));
-        layout.addOptions(options);
+        List<RadioButtonWithDescription> buttons =
+                Arrays.asList(createRadioButtonWithDescription("a", "a_desc", null),
+                        createRadioButtonWithDescription("b", "b_desc", null),
+                        createRadioButtonWithDescription("c", "c_desc", null));
+        layout.addButtons(buttons);
 
         RadioButtonWithDescription firstButton = (RadioButtonWithDescription) layout.getChildAt(0);
         final TextView accessoryTextView = new TextView(mContext);
@@ -217,11 +219,11 @@
         final RadioButtonWithDescriptionLayout layout =
                 new RadioButtonWithDescriptionLayout(mContext);
 
-        List<RadioButtonWithDescriptionLayout.Option> options = new ArrayList<>();
-        options.add(new RadioButtonWithDescriptionLayout.Option("a", "a_desc", null));
-        options.add(new RadioButtonWithDescriptionLayout.Option("b", "b_desc", null));
-        options.add(new RadioButtonWithDescriptionLayout.Option("c", "c_desc", null));
-        layout.addOptions(options);
+        List<RadioButtonWithDescription> buttons =
+                Arrays.asList(createRadioButtonWithDescription("a", "a_desc", null),
+                        createRadioButtonWithDescription("b", "b_desc", null),
+                        createRadioButtonWithDescription("c", "c_desc", null));
+        layout.addButtons(buttons);
 
         RadioButtonWithDescription firstButton = (RadioButtonWithDescription) layout.getChildAt(0);
         RadioButtonWithDescription lastButton =
@@ -235,4 +237,64 @@
         Assert.assertEquals("The accessory view should be at the new position it was placed at.",
                 accessoryTextView, layout.getChildAt(layout.getChildCount() - 1));
     }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testCombinedRadioButtons() {
+        // Test if radio buttons are set up correctly when there are multiple classes in the same
+        // layout.
+        final RadioButtonWithDescriptionLayout layout =
+                new RadioButtonWithDescriptionLayout(mContext);
+
+        RadioButtonWithDescription b1 = createRadioButtonWithDescription("a", "a_desc", null);
+        RadioButtonWithDescription b2 = createRadioButtonWithDescription("b", "b_desc", null);
+
+        RadioButtonWithEditText b3 = new RadioButtonWithEditText(mContext, null);
+        b3.setPrimaryText("c");
+        b3.setDescriptionText("c_desc");
+
+        List<RadioButtonWithDescription> buttons = Arrays.asList(b1, b2, b3);
+        layout.addButtons(buttons);
+
+        layout.selectChildAtIndexForTesting(3);
+        for (int i = 0; i < layout.getChildCount(); i++) {
+            RadioButtonWithDescription b = (RadioButtonWithDescription) layout.getChildAt(i);
+            Assert.assertEquals(TITLE_MATCH_ASSERT_MESSAGE, buttons.get(i).getPrimaryText(),
+                    b.getPrimaryText());
+            Assert.assertEquals(DESCRIPTION_MATCH_ASSERT_MESSAGE,
+                    buttons.get(i).getDescriptionText(), b.getDescriptionText());
+            Assert.assertEquals(i == 3, b.isChecked());
+        }
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testInflateFromLayout() {
+        View content = LayoutInflater.from(mContext).inflate(
+                R.layout.radio_button_with_description_layout_test, null, false);
+
+        RadioButtonWithDescriptionLayout layout =
+                content.findViewById(R.id.test_radio_button_layout);
+        RadioButtonWithDescription b1 = content.findViewById(R.id.test_radio_description_1);
+        RadioButtonWithDescription b2 = content.findViewById(R.id.test_radio_description_2);
+        RadioButtonWithEditText b3 = content.findViewById(R.id.test_radio_edit_text_1);
+        RadioButtonWithEditText b4 = content.findViewById(R.id.test_radio_edit_text_2);
+
+        Assert.assertNotNull(layout);
+        Assert.assertNotNull(b1);
+        Assert.assertNotNull(b2);
+        Assert.assertNotNull(b3);
+        Assert.assertNotNull(b4);
+    }
+
+    private RadioButtonWithDescription createRadioButtonWithDescription(
+            String primary, String description, Object tag) {
+        RadioButtonWithDescription b = new RadioButtonWithDescription(mContext, null);
+        b.setPrimaryText(primary);
+        b.setDescriptionText(description);
+        b.setTag(tag);
+        return b;
+    }
 }
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditText.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditText.java
new file mode 100644
index 0000000..3d430f67
--- /dev/null
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditText.java
@@ -0,0 +1,167 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.ui.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>
+ * A radio button that contains a text edit box. The text value inside the entry box could used to
+ * represent the value when this radio button is selected. This class also supports the
+ * functionality of adding a description the same as {@link RadioButtonWithDescription}.
+ *
+ * By default, this class is inflated from {@link R.layout.radio_button_with_edit_text}.
+ * </p>
+ *
+ * <p>
+ * There is no default hint provided in the EditText. User could set the hint message through {@link
+ * #setHint} API, or through android:hint attribute in xml definition.
+ * </p>
+ *
+ * <p>
+ * This class also provides an interface {@link RadioButtonWithEditText.OnLongClickListener} to
+ * observe the text changing in its entry box. To use, implement the interface {@link
+ * RadioButtonWithEditText.OnLongClickListener} and call {@link
+ * RadioButtonWithEditText#addTextChangeListener(OnTextChangeListener)} to start listening to
+ * changes in the EditText.
+ * </p>
+ *
+ * <p>
+ * The input type, text, hint message of EditText box and an optional description to be contained in
+ * the group may be set in XML. Sample declaration in XML:
+ * <pre>{@code
+ *  <org.chromium.chrome.browser.ui.widget.RadioButtonWithEditText
+ *     android:id="@+id/system_default"
+ *     android:layout_width="match_parent"
+ *     android:layout_height="wrap_content"
+ *     android:background="?attr/selectableItemBackground"
+ *     android:inputType="text"
+ *     android:hint="@string/hint_text_bar"
+ *     app:primaryText="@string/feature_foo_option_one"
+ *     app:descriptionText="@string/feature_foo_option_one_description" />
+ * }</pre>
+ * </p>
+ */
+public class RadioButtonWithEditText extends RadioButtonWithDescription {
+    /**
+     * Interface that will subscribe to changes to the text inside {@link RadioButtonWithEditText}.
+     *
+     */
+    public interface OnTextChangeListener {
+        /**
+         * Will be called when the EditText has a value change.
+         * @param newText The updated text in EditText.
+         */
+        void onTextChanged(CharSequence newText);
+    }
+
+    private EditText mEditText;
+    private List<OnTextChangeListener> mListeners;
+
+    public RadioButtonWithEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mListeners = new ArrayList<>();
+    }
+
+    @Override
+    protected int getLayoutResource() {
+        return R.layout.radio_button_with_edit_text;
+    }
+
+    @Override
+    protected void setViewsInternal() {
+        super.setViewsInternal();
+
+        mEditText = (EditText) getPrimaryTextView();
+        mEditText.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {}
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                // Text set through AttributionSet will trigger this function before mListeners
+                // initialize, so we only notify listeners after initialization.
+                if (mListeners == null) return;
+
+                for (OnTextChangeListener listener : mListeners) {
+                    listener.onTextChanged(s);
+                }
+            }
+        });
+    }
+
+    @Override
+    protected TextView getPrimaryTextView() {
+        return findViewById(R.id.edit_text);
+    }
+
+    @Override
+    protected void applyAttributes(AttributeSet attrs) {
+        super.applyAttributes(attrs);
+
+        TypedArray a = getContext().getTheme().obtainStyledAttributes(
+                attrs, R.styleable.RadioButtonWithEditText, 0, 0);
+
+        String hint = a.getString(R.styleable.RadioButtonWithEditText_android_hint);
+        if (hint != null) setHint(hint);
+
+        int inputType = a.getInt(
+                R.styleable.RadioButtonWithEditText_android_inputType, InputType.TYPE_CLASS_TEXT);
+        setInputType(inputType);
+
+        a.recycle();
+    }
+
+    /**
+     * Add a listener that will be notified when text inside this url has been changed
+     * @param listener New listener that will be notified when text edit has been changed
+     */
+    public void addTextChangeListener(OnTextChangeListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Remove the listener from the subscription list
+     * @param listener Listener that will no longer listening to text edit changes
+     */
+    public void removeTextChangeListener(OnTextChangeListener listener) {
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Set the input type of text editor
+     * @param inputType An input type from {@link android.text.InputType}
+     */
+    public void setInputType(int inputType) {
+        mEditText.setInputType(inputType);
+    }
+
+    /**
+     * Set the hint message of text edit box
+     */
+    public void setHint(CharSequence hint) {
+        mEditText.setHint(hint);
+    }
+
+    /**
+     * Set the hint message of text edit box using pre-defined string from {@link R.string}
+     */
+    public void setHint(int hintId) {
+        mEditText.setHint(hintId);
+    }
+}
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditTextTest.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditTextTest.java
new file mode 100644
index 0000000..d106127
--- /dev/null
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditTextTest.java
@@ -0,0 +1,202 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.ui.widget;
+
+import android.app.Activity;
+import android.support.test.filters.SmallTest;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.RadioButton;
+import android.widget.TextView;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.chrome.browser.ui.widget.test.R;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.test.util.DummyUiActivityTestCase;
+
+/**
+ * Unit tests for {@link RadioButtonWithEditText}.
+ */
+@RunWith(BaseJUnit4ClassRunner.class)
+public class RadioButtonWithEditTextTest extends DummyUiActivityTestCase {
+    private class TestListener implements RadioButtonWithEditText.OnTextChangeListener {
+        private CharSequence mCurrentText;
+        private int mNumberOfTimesTextChanged;
+
+        TestListener() {
+            mNumberOfTimesTextChanged = 0;
+        }
+
+        /**
+         * Will be called when the text edit has a value change.
+         */
+        @Override
+        public void onTextChanged(CharSequence newText) {
+            mCurrentText = newText;
+            mNumberOfTimesTextChanged += 1;
+        }
+
+        void setCurrentText(CharSequence currentText) {
+            mCurrentText = currentText;
+        }
+
+        /**
+         * Get the current text stored inside
+         * @return current text updated by RadioButtonWithEditText
+         */
+        CharSequence getCurrentText() {
+            return mCurrentText;
+        }
+
+        int getTimesCalled() {
+            return mNumberOfTimesTextChanged;
+        }
+    }
+
+    private TestListener mListener;
+    private Activity mActivity;
+
+    private RadioButtonWithEditText mRadioButtonWithEditText;
+    private RadioButton mButton;
+    private EditText mEditText;
+
+    @Override
+    public void setUpTest() throws Exception {
+        super.setUpTest();
+        mActivity = getActivity();
+        mListener = new TestListener();
+
+        setupViewsForTest();
+    }
+
+    private void setupViewsForTest() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            View layout = LayoutInflater.from(mActivity).inflate(
+                    R.layout.radio_button_with_edit_text_test, null, false);
+            mActivity.setContentView(layout);
+
+            mRadioButtonWithEditText =
+                    (RadioButtonWithEditText) layout.findViewById(R.id.test_radio_button);
+            Assert.assertNotNull(mRadioButtonWithEditText);
+
+            mButton = layout.findViewById(R.id.radio_button);
+            mEditText = layout.findViewById(R.id.edit_text);
+
+            Assert.assertNotNull("Radio Button should not be null", mButton);
+            Assert.assertNotNull("Edit Text should not be null", mEditText);
+        });
+    }
+
+    @Test
+    @SmallTest
+    public void testViewSetup() {
+        Assert.assertFalse("Button should not be set checked after init.", mButton.isChecked());
+        Assert.assertTrue(
+                "Text entry should be empty after init.", TextUtils.isEmpty(mEditText.getText()));
+
+        // Test if apply attr works
+        int textUriInputType = InputType.TYPE_TEXT_VARIATION_URI | InputType.TYPE_CLASS_TEXT;
+        Assert.assertEquals("EditText input type is different than attr setting.", textUriInputType,
+                mEditText.getInputType());
+        Assert.assertEquals("EditText input hint is different than attr setting.",
+                mActivity.getResources().getString(R.string.test_uri), mEditText.getHint());
+
+        TextView description = mActivity.findViewById(R.id.description);
+        Assert.assertNotNull("Description should not be null", description);
+        Assert.assertEquals("Description is different than attr setting.",
+                mActivity.getResources().getString(R.string.test_string), description.getText());
+    }
+
+    @Test
+    @SmallTest
+    public void testSetHint() {
+        final CharSequence hintMsg = "Text hint";
+        final String resourceString = mActivity.getResources().getString(R.string.test_string);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mRadioButtonWithEditText.setHint(hintMsg);
+            Assert.assertEquals("Hint message set from string is different from test setting",
+                    hintMsg.toString(), mEditText.getHint().toString());
+
+            mRadioButtonWithEditText.setHint(R.string.test_string);
+            Assert.assertEquals("Hint message set from resource id is different from test setting",
+                    resourceString, mEditText.getHint().toString());
+        });
+    }
+
+    @Test
+    @SmallTest
+    public void testSetInputType() {
+        int[] commonInputTypes = {
+                InputType.TYPE_CLASS_DATETIME,
+                InputType.TYPE_CLASS_NUMBER,
+                InputType.TYPE_CLASS_PHONE,
+                InputType.TYPE_CLASS_TEXT,
+                InputType.TYPE_TEXT_VARIATION_URI,
+                InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS,
+                InputType.TYPE_DATETIME_VARIATION_DATE,
+        };
+
+        for (int type : commonInputTypes) {
+            mRadioButtonWithEditText.setInputType(type);
+            Assert.assertEquals(mEditText.getInputType(), type);
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testChangeEditText() {
+        final CharSequence str1 = "First string";
+        final CharSequence str2 = "SeConD sTrINg";
+
+        CharSequence origStr = mRadioButtonWithEditText.getPrimaryText();
+
+        // Test if changing the text edit will result in changing of listener
+        mRadioButtonWithEditText.addTextChangeListener(mListener);
+        mListener.setCurrentText(origStr);
+        int timesCalled = mListener.getTimesCalled();
+
+        // Test changes for edit text
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { mRadioButtonWithEditText.setPrimaryText(str1); });
+
+        Assert.assertEquals("New String value should be updated", str1.toString(),
+                mRadioButtonWithEditText.getPrimaryText().toString());
+        Assert.assertEquals("Text message in listener should be updated accordingly",
+                str1.toString(), mListener.getCurrentText().toString());
+        Assert.assertEquals("TestListener#OnTextChanged should be called once", timesCalled + 1,
+                mListener.getTimesCalled());
+
+        // change to another text from View
+        timesCalled = mListener.getTimesCalled();
+        TestThreadUtils.runOnUiThreadBlocking(() -> { mEditText.setText(str2); });
+
+        Assert.assertEquals("New String value should be updated", str2.toString(),
+                mRadioButtonWithEditText.getPrimaryText().toString());
+        Assert.assertEquals("Text message in listener should be updated accordingly",
+                str2.toString(), mListener.getCurrentText().toString());
+        Assert.assertEquals("TestListener#OnTextChanged should be called once", timesCalled + 1,
+                mListener.getTimesCalled());
+
+        // change to another text from View
+        mRadioButtonWithEditText.removeTextChangeListener(mListener);
+        timesCalled = mListener.getTimesCalled();
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { mRadioButtonWithEditText.setPrimaryText(str1); });
+
+        Assert.assertEquals("New String value should be updated", str1.toString(),
+                mRadioButtonWithEditText.getPrimaryText().toString());
+        Assert.assertEquals("Text message in listener should not be updated.", str2.toString(),
+                mListener.getCurrentText().toString());
+        Assert.assertEquals("TestListener#OnTextChanged should not be called any more", timesCalled,
+                mListener.getTimesCalled());
+    }
+}
diff --git a/chrome/browser/ui/android/widget/test/java/res/layout/radio_button_with_description_layout_test.xml b/chrome/browser/ui/android/widget/test/java/res/layout/radio_button_with_description_layout_test.xml
new file mode 100644
index 0000000..c29761c
--- /dev/null
+++ b/chrome/browser/ui/android/widget/test/java/res/layout/radio_button_with_description_layout_test.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout
+        android:id="@+id/test_radio_button_layout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <!-- RadioButtonWithDescription - With primary, without description. -->
+        <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+              android:id="@+id/test_radio_description_1"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              app:primaryText="@string/test_string" />
+
+        <!-- RadioButtonWithDescription - With primary and description. -->
+        <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+            android:id="@+id/test_radio_description_2"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:primaryText="@string/test_string"
+            app:descriptionText="@string/test_string" />
+
+        <!-- RadioButtonWithDescription - With primary, without description. -->
+        <org.chromium.chrome.browser.ui.widget.RadioButtonWithEditText
+            android:id="@+id/test_radio_edit_text_1"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:minHeight="@dimen/min_touch_target_size"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:inputType="text"
+            android:hint="@string/test_uri"
+            app:primaryText="@string/test_string" />
+
+        <!-- RadioButtonWithDescription - With primary and description. -->
+        <org.chromium.chrome.browser.ui.widget.RadioButtonWithEditText
+            android:id="@+id/test_radio_edit_text_2"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:minHeight="@dimen/min_touch_target_size"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:inputType="text"
+            android:hint="@string/test_uri"
+            app:primaryText="@string/test_string"
+            app:descriptionText="@string/test_string" />
+    </org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout>
+</FrameLayout>
diff --git a/chrome/browser/ui/android/widget/test/java/res/layout/radio_button_with_edit_text_test.xml b/chrome/browser/ui/android/widget/test/java/res/layout/radio_button_with_edit_text_test.xml
new file mode 100644
index 0000000..c791f73
--- /dev/null
+++ b/chrome/browser/ui/android/widget/test/java/res/layout/radio_button_with_edit_text_test.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <org.chromium.chrome.browser.ui.widget.RadioButtonWithEditText
+        android:id="@+id/test_radio_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/min_touch_target_size"
+        android:paddingTop="8dp"
+        android:paddingBottom="8dp"
+        android:inputType="textUri"
+        android:hint="@string/test_uri"
+        android:background="?attr/selectableItemBackground"
+        app:descriptionText="@string/test_string" />
+</FrameLayout>
diff --git a/chrome/browser/ui/android/widget/test/java/res/values/strings.xml b/chrome/browser/ui/android/widget/test/java/res/values/strings.xml
index 9c64f9b9..234b9a8 100644
--- a/chrome/browser/ui/android/widget/test/java/res/values/strings.xml
+++ b/chrome/browser/ui/android/widget/test/java/res/values/strings.xml
@@ -9,4 +9,7 @@
     <string name="promo_dialog_test_primary_button">OK</string>
     <string name="promo_dialog_test_secondary_button">Cancel</string>
     <string name="promo_dialog_test_footer">Learn more</string>
+
+    <string name="test_string">A String used for tests</string>
+    <string name="test_uri">https://www.example.com</string>
 </resources>
\ No newline at end of file
diff --git a/chrome/browser/ui/ash/launcher/DEPS b/chrome/browser/ui/ash/launcher/DEPS
index 3922377..0f073e5 100644
--- a/chrome/browser/ui/ash/launcher/DEPS
+++ b/chrome/browser/ui/ash/launcher/DEPS
@@ -3,6 +3,9 @@
 ]
 
 specific_include_rules = {
+  "app_service_app_window_browsertest\.cc": [
+    "+ash/shell.h",
+  ],
   # https://crbug.com/826386
   "app_window_launcher_controller\.cc": [
     "+ash/shell.h",
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_arc_tracker.cc b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_arc_tracker.cc
index 00837a4..74d8d15 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_arc_tracker.cc
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_arc_tracker.cc
@@ -196,11 +196,8 @@
   // window information.
   // Update |state|. The app must be started, and running state. If visible,
   // set it as |kVisible|, otherwise, clear the visible bit.
-  apps::InstanceState state = apps::InstanceState::kUnknown;
   auto* proxy = apps::AppServiceProxyFactory::GetForProfile(observed_profile_);
-  proxy->InstanceRegistry().ForOneInstance(
-      window,
-      [&state](const apps::InstanceUpdate& update) { state = update.State(); });
+  apps::InstanceState state = proxy->InstanceRegistry().GetState(window);
   state = static_cast<apps::InstanceState>(
       state | apps::InstanceState::kStarted | apps::InstanceState::kRunning);
   app_service_controller_->app_service_instance_helper()->OnInstances(
@@ -283,6 +280,15 @@
               ? ArcAppWindow::FullScreenMode::kActive
               : ArcAppWindow::FullScreenMode::kNonActive);
     }
+    if (previous_arc_app_window_info->window()) {
+      apps::InstanceState state =
+          app_service_controller_->app_service_instance_helper()
+              ->CalculateActivatedState(previous_arc_app_window_info->window(),
+                                        false /* active */);
+      app_service_controller_->app_service_instance_helper()->OnInstances(
+          previous_arc_app_window_info->app_shelf_id().app_id(),
+          previous_arc_app_window_info->window(), std::string(), state);
+    }
   }
 
   active_task_id_ = task_id;
@@ -302,6 +308,13 @@
   }
   app_service_controller_->owner()->SetItemStatus(
       current_arc_app_window_info->shelf_id(), ash::STATUS_RUNNING);
+
+  apps::InstanceState state =
+      app_service_controller_->app_service_instance_helper()
+          ->CalculateActivatedState(window, true /* active */);
+  app_service_controller_->app_service_instance_helper()->OnInstances(
+      current_arc_app_window_info->app_shelf_id().app_id(), window,
+      std::string(), state);
 }
 
 void AppServiceAppWindowArcTracker::AttachControllerToWindow(
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_browsertest.cc b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_browsertest.cc
index daea507e..8fa4460 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_browsertest.cc
@@ -6,9 +6,11 @@
 #include <vector>
 
 #include "ash/public/cpp/shelf_model.h"
+#include "ash/shell.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_test_util.h"
@@ -17,17 +19,42 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/services/app_service/public/cpp/instance.h"
 #include "chrome/services/app_service/public/cpp/instance_registry.h"
+#include "components/arc/arc_service_manager.h"
+#include "components/arc/arc_util.h"
+#include "components/arc/session/arc_bridge_service.h"
+#include "components/arc/test/fake_app_instance.h"
+#include "components/exo/shell_surface_util.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/common/extension.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/display.h"
+#include "ui/views/widget/widget.h"
 
 using extensions::AppWindow;
 using extensions::Extension;
 
+namespace mojo {
+
+template <>
+struct TypeConverter<arc::mojom::ArcPackageInfoPtr,
+                     arc::mojom::ArcPackageInfo> {
+  static arc::mojom::ArcPackageInfoPtr Convert(
+      const arc::mojom::ArcPackageInfo& package_info) {
+    return package_info.Clone();
+  }
+};
+
+}  // namespace mojo
+
 namespace {
 
+constexpr char kTestAppName[] = "Test ARC App";
+constexpr char kTestAppName2[] = "Test ARC App 2";
+constexpr char kTestAppPackage[] = "test.arc.app.package";
+constexpr char kTestAppActivity[] = "test.arc.app.package.activity";
+constexpr char kTestAppActivity2[] = "test.arc.gitapp.package.activity2";
+
 ash::ShelfAction SelectItem(
     const ash::ShelfID& id,
     ui::EventType event_type = ui::ET_MOUSE_PRESSED,
@@ -36,6 +63,38 @@
   return SelectShelfItem(id, event_type, display_id, source);
 }
 
+std::string GetTestApp1Id(const std::string& package_name) {
+  return ArcAppListPrefs::GetAppId(package_name, kTestAppActivity);
+}
+
+std::string GetTestApp2Id(const std::string& package_name) {
+  return ArcAppListPrefs::GetAppId(package_name, kTestAppActivity2);
+}
+
+std::vector<arc::mojom::AppInfoPtr> GetTestAppsList(
+    const std::string& package_name,
+    bool multi_app) {
+  std::vector<arc::mojom::AppInfoPtr> apps;
+
+  arc::mojom::AppInfoPtr app(arc::mojom::AppInfo::New());
+  app->name = kTestAppName;
+  app->package_name = package_name;
+  app->activity = kTestAppActivity;
+  app->sticky = false;
+  apps.push_back(std::move(app));
+
+  if (multi_app) {
+    app = arc::mojom::AppInfo::New();
+    app->name = kTestAppName2;
+    app->package_name = package_name;
+    app->activity = kTestAppActivity2;
+    app->sticky = false;
+    apps.push_back(std::move(app));
+  }
+
+  return apps;
+}
+
 }  // namespace
 
 class AppServiceAppWindowBrowserTest
@@ -366,3 +425,182 @@
   windows = app_service_proxy_->InstanceRegistry().GetWindows(app_id);
   EXPECT_EQ(0u, windows.size());
 }
+
+class AppServiceAppWindowArcAppBrowserTest
+    : public AppServiceAppWindowBrowserTest {
+ protected:
+  // AppServiceAppWindowBrowserTest:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    AppServiceAppWindowBrowserTest::SetUpCommandLine(command_line);
+    arc::SetArcAvailableCommandLineForTesting(command_line);
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    AppServiceAppWindowBrowserTest::SetUpInProcessBrowserTestFixture();
+    arc::ArcSessionManager::SetUiEnabledForTesting(false);
+  }
+
+  void SetUpOnMainThread() override {
+    AppServiceAppWindowBrowserTest::SetUpOnMainThread();
+    arc::SetArcPlayStoreEnabledForProfile(profile(), true);
+  }
+
+  void InstallTestApps(const std::string& package_name, bool multi_app) {
+    app_host()->OnAppListRefreshed(GetTestAppsList(package_name, multi_app));
+
+    std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
+        app_prefs()->GetApp(GetTestApp1Id(package_name));
+    ASSERT_TRUE(app_info);
+    EXPECT_TRUE(app_info->ready);
+    if (multi_app) {
+      std::unique_ptr<ArcAppListPrefs::AppInfo> app_info2 =
+          app_prefs()->GetApp(GetTestApp2Id(package_name));
+      ASSERT_TRUE(app_info2);
+      EXPECT_TRUE(app_info2->ready);
+    }
+  }
+
+  void SendPackageAdded(const std::string& package_name, bool package_synced) {
+    arc::mojom::ArcPackageInfo package_info;
+    package_info.package_name = package_name;
+    package_info.package_version = 1;
+    package_info.last_backup_android_id = 1;
+    package_info.last_backup_time = 1;
+    package_info.sync = package_synced;
+    package_info.system = false;
+    app_host()->OnPackageAdded(arc::mojom::ArcPackageInfo::From(package_info));
+
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void StartInstance() {
+    app_instance_ = std::make_unique<arc::FakeAppInstance>(app_host());
+    arc_brige_service()->app()->SetInstance(app_instance_.get());
+  }
+
+  void StopInstance() {
+    if (app_instance_)
+      arc_brige_service()->app()->CloseInstance(app_instance_.get());
+    arc_session_manager()->Shutdown();
+  }
+
+  // Creates app window and set optional ARC application id.
+  views::Widget* CreateArcWindow(const std::string& window_app_id) {
+    views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
+    params.bounds = gfx::Rect(5, 5, 20, 20);
+    params.context = ash::Shell::GetPrimaryRootWindow();
+    views::Widget* widget = new views::Widget();
+    widget->Init(std::move(params));
+    // Set ARC id before showing the window to be recognized in
+    // ArcAppWindowLauncherController.
+    exo::SetShellApplicationId(widget->GetNativeWindow(), window_app_id);
+    widget->Show();
+    widget->Activate();
+    return widget;
+  }
+
+  ArcAppListPrefs* app_prefs() { return ArcAppListPrefs::Get(profile()); }
+
+  // Returns as AppHost interface in order to access to private implementation
+  // of the interface.
+  arc::mojom::AppHost* app_host() { return app_prefs(); }
+
+ private:
+  arc::ArcSessionManager* arc_session_manager() {
+    return arc::ArcSessionManager::Get();
+  }
+
+  arc::ArcBridgeService* arc_brige_service() {
+    return arc::ArcServiceManager::Get()->arc_bridge_service();
+  }
+
+  std::unique_ptr<arc::FakeAppInstance> app_instance_;
+};
+
+// Test that we have the correct instance for ARC apps.
+IN_PROC_BROWSER_TEST_F(AppServiceAppWindowArcAppBrowserTest, ArcAppsWindow) {
+  // Install app to remember existing apps.
+  StartInstance();
+  InstallTestApps(kTestAppPackage, true);
+  SendPackageAdded(kTestAppPackage, false);
+
+  // Create the window for app1.
+  views::Widget* arc_window1 = CreateArcWindow("org.chromium.arc.1");
+  const std::string app_id1 = GetTestApp1Id(kTestAppPackage);
+
+  // Simulate task creation so the app is marked as running/open.
+  std::unique_ptr<ArcAppListPrefs::AppInfo> info = app_prefs()->GetApp(app_id1);
+  app_host()->OnTaskCreated(1, info->package_name, info->activity, info->name,
+                            info->intent_uri);
+  EXPECT_TRUE(controller_->GetItem(ash::ShelfID(app_id1)));
+
+  // Check the window state in instance for app1
+  auto windows = app_service_proxy_->InstanceRegistry().GetWindows(app_id1);
+  EXPECT_EQ(1u, windows.size());
+  aura::Window* window1 = *windows.begin();
+  apps::InstanceState latest_state =
+      app_service_proxy_->InstanceRegistry().GetState(window1);
+  EXPECT_EQ(apps::InstanceState::kStarted | apps::InstanceState::kRunning,
+            latest_state);
+
+  app_host()->OnTaskSetActive(1);
+  latest_state = app_service_proxy_->InstanceRegistry().GetState(window1);
+  EXPECT_EQ(apps::InstanceState::kStarted | apps::InstanceState::kRunning |
+                apps::InstanceState::kActive | apps::InstanceState::kVisible,
+            latest_state);
+  controller_->PinAppWithID(app_id1);
+
+  // Create the task id for app2 first, then create the window.
+  const std::string app_id2 = GetTestApp2Id(kTestAppPackage);
+  info = app_prefs()->GetApp(app_id2);
+  app_host()->OnTaskCreated(2, info->package_name, info->activity, info->name,
+                            info->intent_uri);
+  views::Widget* arc_window2 = CreateArcWindow("org.chromium.arc.2");
+  EXPECT_TRUE(controller_->GetItem(ash::ShelfID(app_id2)));
+
+  // Check the window state in instance for app2
+  windows = app_service_proxy_->InstanceRegistry().GetWindows(app_id2);
+  EXPECT_EQ(1u, windows.size());
+  aura::Window* window2 = *windows.begin();
+  latest_state = app_service_proxy_->InstanceRegistry().GetState(window2);
+  EXPECT_EQ(apps::InstanceState::kStarted | apps::InstanceState::kRunning |
+                apps::InstanceState::kActive | apps::InstanceState::kVisible,
+            latest_state);
+
+  // App1 is inactive.
+  latest_state = app_service_proxy_->InstanceRegistry().GetState(window1);
+  EXPECT_EQ(apps::InstanceState::kStarted | apps::InstanceState::kRunning |
+                apps::InstanceState::kVisible,
+            latest_state);
+
+  // Select the app1
+  SelectItem(ash::ShelfID(app_id1));
+  latest_state = app_service_proxy_->InstanceRegistry().GetState(window1);
+  EXPECT_EQ(apps::InstanceState::kStarted | apps::InstanceState::kRunning |
+                apps::InstanceState::kActive | apps::InstanceState::kVisible,
+            latest_state);
+  latest_state = app_service_proxy_->InstanceRegistry().GetState(window2);
+  EXPECT_EQ(apps::InstanceState::kStarted | apps::InstanceState::kRunning |
+                apps::InstanceState::kVisible,
+            latest_state);
+
+  // Close the window for app1, and destroy the task.
+  arc_window1->CloseNow();
+  app_host()->OnTaskDestroyed(1);
+  windows = app_service_proxy_->InstanceRegistry().GetWindows(app_id1);
+  EXPECT_EQ(0u, windows.size());
+
+  // App2 is activated.
+  latest_state = app_service_proxy_->InstanceRegistry().GetState(window2);
+  EXPECT_EQ(apps::InstanceState::kStarted | apps::InstanceState::kRunning |
+                apps::InstanceState::kActive | apps::InstanceState::kVisible,
+            latest_state);
+
+  // destroy the task for app2 and close the window.
+  app_host()->OnTaskDestroyed(2);
+  arc_window2->CloseNow();
+  windows = app_service_proxy_->InstanceRegistry().GetWindows(app_id2);
+  EXPECT_EQ(0u, windows.size());
+
+  StopInstance();
+}
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.cc b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.cc
index 5c78ef7..5446c97 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.cc
@@ -40,8 +40,7 @@
     : AppWindowLauncherController(owner),
       proxy_(apps::AppServiceProxyFactory::GetForProfile(owner->profile())),
       app_service_instance_helper_(
-          std::make_unique<AppServiceInstanceRegistryHelper>(
-              owner->profile())) {
+          std::make_unique<AppServiceInstanceRegistryHelper>(this)) {
   aura::Env::GetInstance()->AddObserver(this);
   DCHECK(proxy_);
   Observe(&proxy_->InstanceRegistry());
@@ -91,11 +90,8 @@
   // Deactivates the running app windows in InstanceRegistry for the inactive
   // user, and activates the app windows for the active user.
   for (auto* window : window_list_) {
-    ash::ShelfID shelf_id;
-    if (proxy_->InstanceRegistry().ForOneInstance(
-            window, [&shelf_id](const apps::InstanceUpdate& update) {
-              shelf_id = ash::ShelfID(update.AppId(), update.LaunchId());
-            })) {
+    ash::ShelfID shelf_id = proxy_->InstanceRegistry().GetShelfId(window);
+    if (!shelf_id.IsNull()) {
       RegisterWindow(window, shelf_id);
     } else {
       auto app_window_it = aura_window_to_app_window_.find(window);
@@ -170,7 +166,7 @@
   if (arc_tracker_)
     arc_tracker_->OnWindowVisibilityChanged(window);
 
-  ash::ShelfID shelf_id = GetShelfId(window);
+  ash::ShelfID shelf_id = GetShelfId(window, false /*search_profile_list*/);
   if (shelf_id.IsNull())
     return;
 
@@ -205,7 +201,12 @@
   if (arc_tracker_)
     arc_tracker_->RemoveCandidateWindow(window);
 
-  const ash::ShelfID shelf_id = GetShelfId(window);
+  // When the window is destroyed, we should search all proxies, because the
+  // window could be teleported from the inactive user, and isn't saved in the
+  // proxy of the active user's profile, but it should still be removed from
+  // the controller, and the shelf, so search all the proxies.
+  const ash::ShelfID shelf_id =
+      GetShelfId(window, true /*search_profile_list*/);
   if (shelf_id.IsNull())
     return;
 
@@ -271,6 +272,12 @@
     window->SetProperty(ash::kShelfIDKey, shelf_id.Serialize());
     window->SetProperty<int>(ash::kShelfItemTypeKey, ash::TYPE_APP);
 
+    // When an extension app window is added, calls UserHasAppOnActiveDesktop to
+    // handle teleport function.
+    if (update.BrowserContext() &&
+        (update.State() == apps::InstanceState::kStarted)) {
+      UserHasAppOnActiveDesktop(window, shelf_id, update.BrowserContext());
+    }
     // Apps opened in browser are managed by browser, so skip them.
     if (app_service_instance_helper_->IsOpenedInBrowser(shelf_id.app_id,
                                                         window) ||
@@ -281,40 +288,22 @@
     return;
   }
 
-  // For Chrome apps, when it is shown, call UserHasAppOnActiveDesktop to handle
-  // teleport function.
-  if (update.BrowserContext() &&
-      (update.State() & apps::InstanceState::kStarted) !=
-          apps::InstanceState::kUnknown &&
-      (update.State() & apps::InstanceState::kRunning) !=
-          apps::InstanceState::kUnknown) {
-    UserHasAppOnActiveDesktop(window, shelf_id, update.BrowserContext());
-  }
-
   // Launch id is updated, so constructs a new shelf id.
   if (update.LaunchIdChanged()) {
     window->SetProperty(ash::kShelfIDKey, shelf_id.Serialize());
     window->SetProperty<int>(ash::kShelfItemTypeKey, ash::TYPE_APP);
   }
 
-  if (update.StateChanged() &&
-      update.State() == apps::InstanceState::kStarted) {
-    // For Chrome apps, when the app window is added or hidden, the state is set
-    // as kStarted, no kRunning/kActive/kVisible. For all other app types, it is
-    // impossible that the state is kStarted, because kStarted and kRunning are
-    // set together. So if the state is started, that means the Chrome app
-    // window is just added or hidden, and we don't need to add the app window
-    // to Shelf. When the app window is visible or activeted, it can be added to
-    // Shelf.
+  if (update.State() == apps::InstanceState::kHidden) {
+    // When the app window is hidden, it should be removed from Shelf.
     //
-    // The the window is teleported to the current user could be hidden as
+    // The window is teleported to the current user could be hidden as
     // well. But we only remove the window added for the active user, and skip
     // the window teleported to the current user, because
     // MultiUserWindowManagerHelper manages those windows.
     auto app_window_it = aura_window_to_app_window_.find(window);
     if (app_window_it != aura_window_to_app_window_.end() &&
-        proxy_->InstanceRegistry().ForOneInstance(
-            window, [](const apps::InstanceUpdate& update) {})) {
+        proxy_->InstanceRegistry().Exists(window)) {
       RemoveAppWindowFromShelf(app_window_it->second.get());
       aura_window_to_app_window_.erase(app_window_it);
     }
@@ -387,10 +376,11 @@
 void AppServiceAppWindowLauncherController::SetWindowActivated(
     aura::Window* window,
     bool active) {
-  if (!window)
+  if (!window || !observed_windows_.IsObserving(window))
     return;
 
-  const ash::ShelfID shelf_id = GetShelfId(window);
+  const ash::ShelfID shelf_id =
+      GetShelfId(window, false /*search_profile_list*/);
   if (shelf_id.IsNull())
     return;
 
@@ -511,7 +501,8 @@
 }
 
 ash::ShelfID AppServiceAppWindowLauncherController::GetShelfId(
-    aura::Window* window) const {
+    aura::Window* window,
+    bool search_profile_list) const {
   if (crostini_tracker_) {
     std::string shelf_app_id;
     shelf_app_id = crostini_tracker_->GetShelfAppId(window);
@@ -523,21 +514,44 @@
 
   // If the window exists in InstanceRegistry, get the shelf id from
   // InstanceRegistry.
-  bool exist_in_instance = proxy_->InstanceRegistry().ForOneInstance(
-      window, [&shelf_id](const apps::InstanceUpdate& update) {
-        shelf_id = ash::ShelfID(update.AppId(), update.LaunchId());
-      });
-  if (!exist_in_instance) {
-    shelf_id = ash::ShelfID::Deserialize(window->GetProperty(ash::kShelfIDKey));
-  }
-
-  if (!shelf_id.IsNull()) {
-    if (proxy_->AppRegistryCache().GetAppType(shelf_id.app_id) ==
-            apps::mojom::AppType::kUnknown &&
-        shelf_id.app_id != extension_misc::kChromeAppId) {
+  if (!search_profile_list) {
+    // Search from the proxy of the active user's profile, and verify whether
+    // the app exists in the proxy.
+    shelf_id = proxy_->InstanceRegistry().GetShelfId(window);
+    if (shelf_id.IsNull()) {
+      shelf_id =
+          ash::ShelfID::Deserialize(window->GetProperty(ash::kShelfIDKey));
+    }
+    if (!shelf_id.IsNull()) {
+      if (proxy_->AppRegistryCache().GetAppType(shelf_id.app_id) ==
+              apps::mojom::AppType::kUnknown &&
+          shelf_id.app_id != extension_misc::kChromeAppId) {
+        return ash::ShelfID();
+      }
+      return shelf_id;
+    }
+  } else {
+    for (auto* profile : profile_list_) {
+      auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
+      shelf_id = proxy->InstanceRegistry().GetShelfId(window);
+      if (!shelf_id.IsNull())
+        break;
+    }
+    if (shelf_id.IsNull()) {
+      shelf_id =
+          ash::ShelfID::Deserialize(window->GetProperty(ash::kShelfIDKey));
+    }
+    if (!shelf_id.IsNull()) {
+      for (auto* profile : profile_list_) {
+        auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
+        if (proxy->AppRegistryCache().GetAppType(shelf_id.app_id) !=
+                apps::mojom::AppType::kUnknown ||
+            shelf_id.app_id == extension_misc::kChromeAppId) {
+          return shelf_id;
+        }
+      }
       return ash::ShelfID();
     }
-    return shelf_id;
   }
 
   // For null shelf id, it could be VM window or ARC apps window.
@@ -556,8 +570,7 @@
     content::BrowserContext* browser_context) {
   // If the window was created for the active user, register it to show an item
   // on the shelf.
-  if (proxy_->InstanceRegistry().ForOneInstance(
-          window, [](const apps::InstanceUpdate& update) {})) {
+  if (proxy_->InstanceRegistry().Exists(window)) {
     RegisterWindow(window, shelf_id);
     return;
   }
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.h b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.h
index 2ec6099..7071a03 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.h
@@ -42,6 +42,8 @@
       public apps::InstanceRegistry::Observer,
       public ArcAppWindowDelegate {
  public:
+  using ProfileList = std::vector<Profile*>;
+
   explicit AppServiceAppWindowLauncherController(
       ChromeLauncherController* owner);
   ~AppServiceAppWindowLauncherController() override;
@@ -97,10 +99,11 @@
 
   AppWindowBase* GetAppWindow(aura::Window* window);
 
+  ProfileList& GetProfileList() { return profile_list_; }
+
  private:
   using AuraWindowToAppWindow =
       std::map<aura::Window*, std::unique_ptr<AppWindowBase>>;
-  using ProfileList = std::vector<Profile*>;
   using WindowList = std::vector<aura::Window*>;
 
   void SetWindowActivated(aura::Window* window, bool active);
@@ -118,7 +121,15 @@
   // AppWindowLauncherController:
   void OnItemDelegateDiscarded(ash::ShelfItemDelegate* delegate) override;
 
-  ash::ShelfID GetShelfId(aura::Window* window) const;
+  // Returns the shelf id for |window|. The window could be teleported from the
+  // inactive user to the active user. When |search_profile_list| is true, we
+  // should check the all proxies for all profiles, otherwise, only check the
+  // current active user profile's proxy.
+  //
+  // When |window|'s visibility or activate status is changed,
+  // |search_profile_list| is false to check the active user profile only. When
+  // |window| is destroyed, |search_profile_list| is true to check all proxies.
+  ash::ShelfID GetShelfId(aura::Window* window, bool search_profile_list) const;
 
   // Register |window| if the owner of the given |window| has a window
   // teleported of the |window|'s application type to the current desktop.
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.cc b/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.cc
index 8f4dc8c..6f5d8e1 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.cc
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.cc
@@ -13,6 +13,8 @@
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -24,10 +26,15 @@
 #include "extensions/common/constants.h"
 
 AppServiceInstanceRegistryHelper::AppServiceInstanceRegistryHelper(
-    Profile* profile)
-    : proxy_(apps::AppServiceProxyFactory::GetForProfile(profile)),
-      launcher_controller_helper_(
-          std::make_unique<LauncherControllerHelper>(profile)) {}
+    AppServiceAppWindowLauncherController* controller)
+    : controller_(controller),
+      proxy_(apps::AppServiceProxyFactory::GetForProfile(
+          controller->owner()->profile())),
+      launcher_controller_helper_(std::make_unique<LauncherControllerHelper>(
+          controller->owner()->profile())) {
+  DCHECK(controller_);
+  DCHECK(proxy_);
+}
 
 AppServiceInstanceRegistryHelper::~AppServiceInstanceRegistryHelper() = default;
 
@@ -50,12 +57,8 @@
     // If app_id is empty, we should not set it as inactive because this is
     // Chrome's tab.
     if (!app_id.empty()) {
-      apps::InstanceState state = apps::InstanceState::kUnknown;
-      proxy_->InstanceRegistry().ForOneInstance(
-          old_contents->GetNativeView(),
-          [&state](const apps::InstanceUpdate& update) {
-            state = update.State();
-          });
+      apps::InstanceState state =
+          proxy_->InstanceRegistry().GetState(old_contents->GetNativeView());
       // If the app has been inactive, we don't need to update the instance.
       if ((state & apps::InstanceState::kActive) !=
           apps::InstanceState::kUnknown) {
@@ -120,16 +123,13 @@
     return;
 
   aura::Window* window = GetWindow(contents);
-  std::string app_id;
+
   // When the tab is closed, if the window does not exists in the AppService
   // InstanceRegistry, we don't need to update the status.
-  if (!proxy_->InstanceRegistry().ForOneInstance(
-          window, [&app_id](const apps::InstanceUpdate& update) {
-            app_id = update.AppId();
-          })) {
+  if (!proxy_->InstanceRegistry().Exists(window))
     return;
-  }
 
+  std::string app_id = proxy_->InstanceRegistry().GetShelfId(window).app_id;
   RemoveTabWindow(app_id, window);
   OnInstances(app_id, window, std::string(), apps::InstanceState::kDestroyed);
 }
@@ -162,7 +162,21 @@
 
   std::vector<std::unique_ptr<apps::Instance>> deltas;
   deltas.push_back(std::move(instance));
-  proxy_->InstanceRegistry().OnInstances(std::move(deltas));
+
+  // The window could be teleported from the inactive user's profile to the
+  // current active user, so search all proxies. If the instance is found from a
+  // proxy, still save to that proxy, otherwise, save to the current active user
+  // profile's proxy.
+  auto* proxy = proxy_;
+  for (auto* profile : controller_->GetProfileList()) {
+    auto* proxy_for_profile =
+        apps::AppServiceProxyFactory::GetForProfile(profile);
+    if (proxy_for_profile->InstanceRegistry().Exists(window)) {
+      proxy = proxy_for_profile;
+      break;
+    }
+  }
+  proxy->InstanceRegistry().OnInstances(std::move(deltas));
 }
 
 void AppServiceInstanceRegistryHelper::OnWindowVisibilityChanged(
@@ -192,12 +206,10 @@
   // For Chrome browser app windows, sets the state for each tab window instance
   // in this browser.
   for (auto* it : browser_window_to_tab_window_[window]) {
-    std::string app_id;
-    if (!proxy_->InstanceRegistry().ForOneInstance(
-            it, [&app_id](const apps::InstanceUpdate& update) {
-              app_id = update.AppId();
-            }))
+    if (!proxy_->InstanceRegistry().Exists(it))
       continue;
+
+    std::string app_id = proxy_->InstanceRegistry().GetShelfId(it).app_id;
     apps::InstanceState state = CalculateVisibilityState(it, visible);
     OnInstances(app_id, it, std::string(), state);
   }
@@ -248,12 +260,9 @@
   // For Chrome browser app windows, sets the state for each tab window instance
   // in this browser.
   for (auto* it : browser_window_to_tab_window_[window]) {
-    std::string app_id;
-    if (!proxy_->InstanceRegistry().ForOneInstance(
-            it, [&app_id](const apps::InstanceUpdate& update) {
-              app_id = update.AppId();
-            }))
+    if (!proxy_->InstanceRegistry().Exists(it))
       continue;
+    std::string app_id = proxy_->InstanceRegistry().GetShelfId(it).app_id;
     apps::InstanceState state = CalculateActivatedState(it, active);
     OnInstances(app_id, it, std::string(), state);
   }
@@ -265,10 +274,7 @@
 apps::InstanceState AppServiceInstanceRegistryHelper::CalculateVisibilityState(
     aura::Window* window,
     bool visible) const {
-  apps::InstanceState state = apps::InstanceState::kUnknown;
-  proxy_->InstanceRegistry().ForOneInstance(
-      window,
-      [&state](const apps::InstanceUpdate& update) { state = update.State(); });
+  apps::InstanceState state = proxy_->InstanceRegistry().GetState(window);
   state = static_cast<apps::InstanceState>(
       state | apps::InstanceState::kStarted | apps::InstanceState::kRunning);
   state = (visible) ? static_cast<apps::InstanceState>(
@@ -288,10 +294,7 @@
         apps::InstanceState::kActive | apps::InstanceState::kVisible);
   }
 
-  apps::InstanceState state = apps::InstanceState::kUnknown;
-  proxy_->InstanceRegistry().ForOneInstance(
-      window,
-      [&state](const apps::InstanceUpdate& update) { state = update.State(); });
+  apps::InstanceState state = proxy_->InstanceRegistry().GetState(window);
   state = static_cast<apps::InstanceState>(
       state | apps::InstanceState::kStarted | apps::InstanceState::kRunning);
   state =
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.h b/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.h
index d7c605f..64e63627 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.h
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.h
@@ -22,12 +22,13 @@
 class WebContents;
 }
 
-class Profile;
+class AppServiceAppWindowLauncherController;
 
 // The helper class to operate the App Service Instance Registry.
 class AppServiceInstanceRegistryHelper {
  public:
-  explicit AppServiceInstanceRegistryHelper(Profile* profile);
+  explicit AppServiceInstanceRegistryHelper(
+      AppServiceAppWindowLauncherController* controller);
   ~AppServiceInstanceRegistryHelper();
 
   void ActiveUserChanged();
@@ -97,6 +98,8 @@
   // Removes the tab's |window| from |browser_window_to_tab_window_|.
   void RemoveTabWindow(const std::string& app_id, aura::Window* window);
 
+  AppServiceAppWindowLauncherController* controller_ = nullptr;
+
   apps::AppServiceProxy* proxy_ = nullptr;
 
   // Used to get app info for tabs.
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 821294d..7219648 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -2568,6 +2568,7 @@
 TEST_F(ChromeLauncherControllerWithArcTest, ArcWindowPackageName) {
   InitLauncherController();
   SendListOfArcApps();
+  app_service_test().WaitForAppService();
 
   std::string window_app_id1("org.chromium.arc.1");
   std::string window_app_id2("org.chromium.arc.2");
diff --git a/chrome/browser/ui/extensions/application_launch.cc b/chrome/browser/ui/extensions/application_launch.cc
index 3e4498d..856fc99 100644
--- a/chrome/browser/ui/extensions/application_launch.cc
+++ b/chrome/browser/ui/extensions/application_launch.cc
@@ -144,14 +144,22 @@
 // |override_url|, if non-empty, will be preferred over the extension's
 // launch url.
 GURL UrlForExtension(const extensions::Extension* extension,
-                     const GURL& override_url) {
+                     Profile* profile,
+                     const apps::AppLaunchParams& params) {
   if (!extension)
-    return override_url;
+    return params.override_url;
 
   GURL url;
-  if (!override_url.is_empty()) {
-    DCHECK(IsAllowedToOverrideURL(extension, override_url));
-    url = override_url;
+  if (!params.override_url.is_empty()) {
+    DCHECK(IsAllowedToOverrideURL(extension, params.override_url));
+    url = params.override_url;
+  } else if (extension->from_bookmark()) {
+    web_app::FileHandlerManager& file_handler_manager =
+        web_app::WebAppProviderBase::GetProviderBase(profile)
+            ->file_handler_manager();
+    url = file_handler_manager
+              .GetMatchingFileHandlerURL(params.app_id, params.launch_files)
+              .value_or(extensions::AppLaunchInfo::GetFullLaunchURL(extension));
   } else {
     url = extensions::AppLaunchInfo::GetFullLaunchURL(extension);
   }
@@ -303,7 +311,7 @@
   UMA_HISTOGRAM_ENUMERATION("Extensions.HostedAppLaunchContainer",
                             params.container);
 
-  GURL url = UrlForExtension(extension, params.override_url);
+  GURL url = UrlForExtension(extension, profile, params);
 
   // System Web Apps go through their own launch path.
   base::Optional<web_app::SystemAppType> system_app_type =
@@ -337,6 +345,11 @@
       break;
   }
 
+  if (base::FeatureList::IsEnabled(blink::features::kFileHandlingAPI)) {
+    web_launch::WebLaunchFilesHelper::SetLaunchPaths(tab, url,
+                                                     params.launch_files);
+  }
+
   if (extension->from_bookmark()) {
     UMA_HISTOGRAM_ENUMERATION("Extensions.BookmarkAppLaunchSource",
                               params.source);
@@ -410,24 +423,13 @@
 
 WebContents* NavigateApplicationWindow(Browser* browser,
                                        const apps::AppLaunchParams& params,
-                                       const GURL& default_url,
+                                       const GURL& url,
                                        WindowOpenDisposition disposition) {
   const Extension* const extension = GetExtension(browser->profile(), params);
   ui::PageTransition transition =
       (extension ? ui::PAGE_TRANSITION_AUTO_BOOKMARK
                  : ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
 
-  GURL url = default_url;
-
-  if (extension && extension->from_bookmark()) {
-    web_app::FileHandlerManager& file_handler_manager =
-        web_app::WebAppProviderBase::GetProviderBase(browser->profile())
-            ->file_handler_manager();
-    url = file_handler_manager
-              .GetMatchingFileHandlerURL(params.app_id, params.launch_files)
-              .value_or(default_url);
-  }
-
   NavigateParams nav_params(browser, url, transition);
   nav_params.disposition = disposition;
   Navigate(&nav_params);
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
index 36eb8b28..776e07a 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
@@ -771,6 +771,11 @@
 
   void ReduceAnimationTime() {
     GetSaveCardIconView()->ReduceAnimationTimeForTesting();
+    auto* const animating_layout = GetAnimatingLayoutManager();
+    if (animating_layout) {
+      animating_layout->SetAnimationDuration(
+          base::TimeDelta::FromMilliseconds(1));
+    }
   }
 
   void ResetEventWaiterForSequence(std::list<DialogEvent> event_sequence) {
@@ -781,16 +786,11 @@
   void WaitForObservedEvent() { event_waiter_->Wait(); }
 
   void WaitForAnimationToEnd() {
-    if (base::FeatureList::IsEnabled(
-            features::kAutofillEnableToolbarStatusChip)) {
+    auto* const animating_layout = GetAnimatingLayoutManager();
+    if (animating_layout) {
       // Wait for animations to finish.
       base::RunLoop loop;
-      static_cast<views::AnimatingLayoutManager*>(
-          BrowserView::GetBrowserViewForBrowser(browser())
-              ->toolbar()
-              ->toolbar_account_icon_container()
-              ->GetLayoutManager())
-          ->PostOrQueueAction(loop.QuitClosure());
+      animating_layout->PostOrQueueAction(loop.QuitClosure());
       loop.Run();
     }
   }
@@ -808,6 +808,19 @@
   CreditCardSaveManager* credit_card_save_manager_ = nullptr;
 
  private:
+  views::AnimatingLayoutManager* GetAnimatingLayoutManager() {
+    if (!base::FeatureList::IsEnabled(
+            autofill::features::kAutofillEnableToolbarStatusChip)) {
+      return nullptr;
+    }
+
+    return static_cast<views::AnimatingLayoutManager*>(
+        BrowserView::GetBrowserViewForBrowser(browser())
+            ->toolbar()
+            ->toolbar_account_icon_container()
+            ->GetLayoutManager());
+  }
+
   std::unique_ptr<autofill::EventWaiter<DialogEvent>> event_waiter_;
   std::unique_ptr<net::FakeURLFetcherFactory> url_fetcher_factory_;
   scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
@@ -2260,6 +2273,7 @@
   FillForm();
   SubmitForm();
   WaitForObservedEvent();
+  WaitForAnimationToEnd();
   EXPECT_TRUE(GetSaveCardIconView()->GetVisible());
   EXPECT_FALSE(GetSaveCardBubbleViews());
 
@@ -2337,6 +2351,7 @@
   FillForm();
   SubmitForm();
   WaitForObservedEvent();
+  WaitForAnimationToEnd();
   EXPECT_TRUE(GetSaveCardIconView()->GetVisible());
   EXPECT_FALSE(GetSaveCardBubbleViews());
 
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_view.cc b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
index ccefb1d6f..998ac0a 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_view.cc
+++ b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
@@ -92,7 +92,13 @@
 
 void PageActionIconView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   node_data->role = ax::mojom::Role::kButton;
-  node_data->SetName(GetTextForTooltipAndAccessibleName());
+  const base::string16 name_text = GetTextForTooltipAndAccessibleName();
+  node_data->SetName(name_text);
+  // TODO(crbug/1038567): This shouldn't be necessary and suggests a bug in how
+  // we deconstruct icons as we're fading them out. In the interim let's make
+  // sure interactive uitests don't crash as they're trying to hide a button.
+  if (name_text.empty())
+    node_data->SetNameExplicitlyEmpty();
 }
 
 base::string16 PageActionIconView::GetTooltipText(const gfx::Point& p) const {
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc b/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc
index 7b035b98..9d0f7c9 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc
@@ -40,6 +40,11 @@
     ManagePasswordsTest::SetUp();
   }
 
+  void SetUpOnMainThread() override {
+    ManagePasswordsTest::SetUpOnMainThread();
+    ReduceAnimationTime();
+  }
+
   ManagePasswordsIconViews* GetView() {
     views::View* const view =
         BrowserView::GetBrowserViewForBrowser(browser())
@@ -57,7 +62,34 @@
     return GetView()->GetImageView()->GetImage();
   }
 
+  void WaitForAnimationToEnd() {
+    auto* const animating_layout = GetAnimatingLayoutManager();
+    if (animating_layout) {
+      // Wait for animations to finish.
+      base::RunLoop loop;
+      animating_layout->PostOrQueueAction(loop.QuitClosure());
+      loop.Run();
+    }
+  }
+
  private:
+  views::AnimatingLayoutManager* GetAnimatingLayoutManager() {
+    return GetParam() ? static_cast<views::AnimatingLayoutManager*>(
+                            BrowserView::GetBrowserViewForBrowser(browser())
+                                ->toolbar()
+                                ->toolbar_account_icon_container()
+                                ->GetLayoutManager())
+                      : nullptr;
+  }
+
+  void ReduceAnimationTime() {
+    auto* const animating_layout = GetAnimatingLayoutManager();
+    if (animating_layout) {
+      animating_layout->SetAnimationDuration(
+          base::TimeDelta::FromMilliseconds(1));
+    }
+  }
+
   base::test::ScopedFeatureList scoped_feature_list_;
 
   DISALLOW_COPY_AND_ASSIGN(ManagePasswordsIconViewTest);
@@ -65,12 +97,14 @@
 
 IN_PROC_BROWSER_TEST_P(ManagePasswordsIconViewTest, DefaultStateIsInactive) {
   EXPECT_EQ(password_manager::ui::INACTIVE_STATE, ViewState());
+  WaitForAnimationToEnd();
   EXPECT_FALSE(GetView()->GetVisible());
 }
 
 IN_PROC_BROWSER_TEST_P(ManagePasswordsIconViewTest, PendingState) {
   SetupPendingPassword();
   EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE, ViewState());
+  WaitForAnimationToEnd();
   EXPECT_TRUE(GetView()->GetVisible());
   // No tooltip because the bubble is showing.
   EXPECT_EQ(base::string16(), GetTooltipText());
@@ -80,6 +114,7 @@
 IN_PROC_BROWSER_TEST_P(ManagePasswordsIconViewTest, ManageState) {
   SetupManagingPasswords();
   EXPECT_EQ(password_manager::ui::MANAGE_STATE, ViewState());
+  WaitForAnimationToEnd();
   EXPECT_TRUE(GetView()->GetVisible());
   EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_TOOLTIP_MANAGE),
             GetTooltipText());
@@ -87,6 +122,7 @@
 
 IN_PROC_BROWSER_TEST_P(ManagePasswordsIconViewTest, CloseOnClick) {
   SetupPendingPassword();
+  WaitForAnimationToEnd();
   EXPECT_TRUE(GetView()->GetVisible());
   ui::MouseEvent mouse_down(ui::ET_MOUSE_PRESSED, gfx::Point(10, 10),
                             gfx::Point(900, 60), ui::EventTimeForNow(),
diff --git a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
index 6dfc124..e149a5c6 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
@@ -46,15 +46,16 @@
   params.page_action_icon_delegate = this;
   params.button_observer = this;
   params.view_observer = this;
-  page_action_icon_container_view_ =
-      AddChildView(std::make_unique<PageActionIconContainerView>(params));
-  page_action_icon_controller_ = page_action_icon_container_view_->controller();
-
   avatar_->SetProperty(views::kFlexBehaviorKey,
                        views::FlexSpecification::ForSizeRule(
                            views::MinimumFlexSizeRule::kScaleToMinimum,
                            views::MaximumFlexSizeRule::kPreferred));
   AddMainButton(avatar_);
+
+  // Since the insertion point for icons before the avatar button, we don't
+  // initialize until after the avatar button has been added.
+  page_action_icon_controller_ = std::make_unique<PageActionIconController>();
+  page_action_icon_controller_->Init(params, this);
 }
 
 ToolbarAccountIconContainerView::~ToolbarAccountIconContainerView() = default;
@@ -104,6 +105,8 @@
   return kToolbarAccountIconContainerViewClassName;
 }
 
-const views::View::Views& ToolbarAccountIconContainerView::GetChildren() const {
-  return page_action_icon_container_view_->children();
+void ToolbarAccountIconContainerView::AddPageActionIcon(views::View* icon) {
+  // Add the page action icons to the end of the container, just before the
+  // avatar icon.
+  AddChildViewAt(icon, GetIndexOf(avatar_));
 }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h
index 85ccf27d..fcde9a2 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h
@@ -6,18 +6,19 @@
 #define CHROME_BROWSER_UI_VIEWS_TOOLBAR_TOOLBAR_ACCOUNT_ICON_CONTAINER_VIEW_H_
 
 #include "chrome/browser/ui/page_action/page_action_icon_type.h"
+#include "chrome/browser/ui/views/page_action/page_action_icon_container.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h"
 
 class AvatarToolbarButton;
 class Browser;
-class PageActionIconContainerView;
 class PageActionIconController;
 
 // A container view for user-account-related PageActionIconViews and the profile
 // avatar icon.
 class ToolbarAccountIconContainerView : public ToolbarIconContainerView,
                                         public IconLabelBubbleView::Delegate,
+                                        public PageActionIconContainer,
                                         public PageActionIconView::Delegate {
  public:
   explicit ToolbarAccountIconContainerView(Browser* browser);
@@ -44,18 +45,17 @@
   const char* GetClassName() const override;
 
   PageActionIconController* page_action_icon_controller() {
-    return page_action_icon_controller_;
+    return page_action_icon_controller_.get();
   }
   AvatarToolbarButton* avatar_button() { return avatar_; }
 
   static const char kToolbarAccountIconContainerViewClassName[];
 
  private:
-  // ToolbarIconContainerView:
-  const views::View::Views& GetChildren() const override;
+  // PageActionIconContainer:
+  void AddPageActionIcon(views::View* icon) override;
 
-  PageActionIconContainerView* page_action_icon_container_view_ = nullptr;
-  PageActionIconController* page_action_icon_controller_ = nullptr;
+  std::unique_ptr<PageActionIconController> page_action_icon_controller_;
 
   AvatarToolbarButton* const avatar_ = nullptr;
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
index d885b691..6dedb1d 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
@@ -88,10 +88,6 @@
   UpdateHighlight();
 }
 
-const views::View::Views& ToolbarIconContainerView::GetChildren() const {
-  return children();
-}
-
 void ToolbarIconContainerView::OnMouseEntered(const ui::MouseEvent& event) {
   UpdateHighlight();
 }
@@ -119,7 +115,7 @@
     return true;
 
   // Focused, pressed or hovered children should trigger the highlight.
-  for (views::View* child : GetChildren()) {
+  for (views::View* child : children()) {
     if (child == main_button_)
       continue;
     if (child->HasFocus())
diff --git a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
index 822f0e8..25be6b5 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
@@ -68,13 +68,6 @@
 
   static const char kToolbarIconContainerViewClassName[];
 
- protected:
-  // TODO(pbos): Remove this when PageActionIconContainerView is not nested
-  // inside ToolbarAccountIconContainerView. This would require making
-  // PageActionIconContainerView something that ToolbarAccountIconContainerView
-  // could inherit instead of nesting into the views hierarchy.
-  virtual const views::View::Views& GetChildren() const;
-
  private:
   friend class ToolbarAccountIconContainerViewBrowserTest;
 
diff --git a/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc b/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
index 5d0285f8..d214402 100644
--- a/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
@@ -29,17 +29,25 @@
     return https_server()->GetURL("app.com", "/ssl/google.html");
   }
 
-  GURL GetFileHandlerActionURL() {
+  GURL GetTextFileHandlerActionURL() {
     return https_server()->GetURL("app.com", "/ssl/blank_page.html");
   }
 
-  base::FilePath NewTestFilePath() {
+  GURL GetCSVFileHandlerActionURL() {
+    return https_server()->GetURL("app.com", "/ssl/page_with_refs.html");
+  }
+
+  base::FilePath NewTestFilePath(const base::FilePath::CharType* extension) {
     // CreateTemporaryFile blocks, temporarily allow blocking.
     base::ScopedAllowBlockingForTesting allow_blocking;
 
+    // In order to test file handling, we need to be able to supply a file
+    // extension for the temp file.
     base::FilePath test_file_path;
     base::CreateTemporaryFile(&test_file_path);
-    return test_file_path;
+    base::FilePath new_file_path = test_file_path.AddExtension(extension);
+    EXPECT_TRUE(base::ReplaceFile(test_file_path, new_file_path, nullptr));
+    return new_file_path;
   }
 
   std::string InstallFileHandlingPWA() {
@@ -50,30 +58,37 @@
     web_app_info->scope = url.GetWithoutFilename();
     web_app_info->title = base::ASCIIToUTF16("A Hosted App");
 
-    blink::Manifest::FileHandler entry;
-    entry.action = GetFileHandlerActionURL();
-    entry.name = base::ASCIIToUTF16("text");
-    entry.accept[base::ASCIIToUTF16("text/*")].push_back(
+    blink::Manifest::FileHandler entry1;
+    entry1.action = GetTextFileHandlerActionURL();
+    entry1.name = base::ASCIIToUTF16("text");
+    entry1.accept[base::ASCIIToUTF16("text/*")].push_back(
         base::ASCIIToUTF16(".txt"));
-    web_app_info->file_handlers.push_back(std::move(entry));
+    web_app_info->file_handlers.push_back(std::move(entry1));
+
+    blink::Manifest::FileHandler entry2;
+    entry2.action = GetCSVFileHandlerActionURL();
+    entry2.name = base::ASCIIToUTF16("csv");
+    entry2.accept[base::ASCIIToUTF16("application/csv")].push_back(
+        base::ASCIIToUTF16(".csv"));
+    web_app_info->file_handlers.push_back(std::move(entry2));
 
     return WebAppControllerBrowserTest::InstallWebApp(std::move(web_app_info));
   }
 
   content::WebContents* LaunchWithFiles(
       const std::string& app_id,
-      const std::vector<base::FilePath>& files) {
+      const GURL& expected_launch_url,
+      const std::vector<base::FilePath>& files,
+      const apps::mojom::LaunchContainer launch_container =
+          apps::mojom::LaunchContainer::kLaunchContainerWindow) {
     apps::AppLaunchParams params(
-        app_id, apps::mojom::LaunchContainer::kLaunchContainerWindow,
-        WindowOpenDisposition::NEW_WINDOW,
+        app_id, launch_container, WindowOpenDisposition::NEW_WINDOW,
         apps::mojom::AppLaunchSource::kSourceFileHandler);
     params.launch_files = files;
 
-    content::TestNavigationObserver navigation_observer(
-        GetFileHandlerActionURL());
+    content::TestNavigationObserver navigation_observer(expected_launch_url);
     navigation_observer.StartWatchingNewWebContents();
 
-    params.override_url = GetFileHandlerActionURL();
     content::WebContents* web_contents =
         apps::LaunchService::Get(profile())->OpenApplication(params);
 
@@ -96,18 +111,22 @@
   ASSERT_TRUE(https_server()->Start());
 
   const std::string app_id = InstallFileHandlingPWA();
-  content::WebContents* web_contents = LaunchWithFiles(app_id, {});
+  content::WebContents* web_contents =
+      LaunchWithFiles(app_id, GetSecureAppURL(), {});
   EXPECT_EQ(false, content::EvalJs(web_contents, "!!window.launchParams"));
 }
 
-IN_PROC_BROWSER_TEST_P(WebAppFileHandlingBrowserTest,
+using WebAppFileHandlingBrowserTestNoUnifiedWebApp =
+    WebAppFileHandlingBrowserTest;
+
+IN_PROC_BROWSER_TEST_P(WebAppFileHandlingBrowserTestNoUnifiedWebApp,
                        PWAsCanReceiveFileLaunchParams) {
   ASSERT_TRUE(https_server()->Start());
 
   const std::string app_id = InstallFileHandlingPWA();
-  base::FilePath test_file_path = NewTestFilePath();
+  base::FilePath test_file_path = NewTestFilePath(FILE_PATH_LITERAL("txt"));
   content::WebContents* web_contents =
-      LaunchWithFiles(app_id, {test_file_path});
+      LaunchWithFiles(app_id, GetTextFileHandlerActionURL(), {test_file_path});
 
   EXPECT_EQ(1,
             content::EvalJs(web_contents, "window.launchParams.files.length"));
@@ -115,6 +134,46 @@
             content::EvalJs(web_contents, "window.launchParams.files[0].name"));
 }
 
+IN_PROC_BROWSER_TEST_P(WebAppFileHandlingBrowserTestNoUnifiedWebApp,
+                       PWAsCanReceiveFileLaunchParamsInTab) {
+  ASSERT_TRUE(https_server()->Start());
+
+  const std::string app_id = InstallFileHandlingPWA();
+  base::FilePath test_file_path = NewTestFilePath(FILE_PATH_LITERAL("txt"));
+  content::WebContents* web_contents =
+      LaunchWithFiles(app_id, GetTextFileHandlerActionURL(), {test_file_path},
+                      apps::mojom::LaunchContainer::kLaunchContainerTab);
+
+  EXPECT_EQ(1,
+            content::EvalJs(web_contents, "window.launchParams.files.length"));
+  EXPECT_EQ(test_file_path.BaseName().value(),
+            content::EvalJs(web_contents, "window.launchParams.files[0].name"));
+}
+
+IN_PROC_BROWSER_TEST_P(WebAppFileHandlingBrowserTestNoUnifiedWebApp,
+                       PWAsDispatchOnCorrectFileHandlingURL) {
+  ASSERT_TRUE(https_server()->Start());
+
+  const std::string app_id = InstallFileHandlingPWA();
+
+  // Test that file handler dispatches correct URL based on file extension.
+  LaunchWithFiles(app_id, GetSecureAppURL(), {});
+  LaunchWithFiles(app_id, GetTextFileHandlerActionURL(),
+                  {NewTestFilePath(FILE_PATH_LITERAL("txt"))});
+  LaunchWithFiles(app_id, GetCSVFileHandlerActionURL(),
+                  {NewTestFilePath(FILE_PATH_LITERAL("csv"))});
+
+  // Test as above in a tab.
+  LaunchWithFiles(app_id, GetSecureAppURL(), {},
+                  apps::mojom::LaunchContainer::kLaunchContainerTab);
+  LaunchWithFiles(app_id, GetTextFileHandlerActionURL(),
+                  {NewTestFilePath(FILE_PATH_LITERAL("txt"))},
+                  apps::mojom::LaunchContainer::kLaunchContainerTab);
+  LaunchWithFiles(app_id, GetCSVFileHandlerActionURL(),
+                  {NewTestFilePath(FILE_PATH_LITERAL("csv"))},
+                  apps::mojom::LaunchContainer::kLaunchContainerTab);
+}
+
 INSTANTIATE_TEST_SUITE_P(
     All,
     WebAppFileHandlingBrowserTest,
@@ -123,3 +182,13 @@
         web_app::ControllerType::kUnifiedControllerWithBookmarkApp,
         web_app::ControllerType::kUnifiedControllerWithWebApp),
     web_app::ControllerTypeParamToString);
+
+// Currently web apps don't support getting a list of all file handlers, so some
+// tests cannot be run.
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    WebAppFileHandlingBrowserTestNoUnifiedWebApp,
+    ::testing::Values(
+        web_app::ControllerType::kHostedAppController,
+        web_app::ControllerType::kUnifiedControllerWithBookmarkApp),
+    web_app::ControllerTypeParamToString);
diff --git a/chrome/browser/web_applications/components/file_handler_manager.cc b/chrome/browser/web_applications/components/file_handler_manager.cc
index da30a705e..7ec253ff 100644
--- a/chrome/browser/web_applications/components/file_handler_manager.cc
+++ b/chrome/browser/web_applications/components/file_handler_manager.cc
@@ -97,6 +97,9 @@
 const base::Optional<GURL> FileHandlerManager::GetMatchingFileHandlerURL(
     const AppId& app_id,
     const std::vector<base::FilePath>& launch_files) {
+  if (!base::FeatureList::IsEnabled(blink::features::kFileHandlingAPI))
+    return base::nullopt;
+
   const std::vector<apps::FileHandlerInfo>* file_handlers =
       GetAllFileHandlers(app_id);
   if (!file_handlers || launch_files.empty())
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 9ffdfa38e..c8f1f605 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -64,7 +64,7 @@
 #if !defined(OS_ANDROID)
 // App Service related flags. See chrome/services/app_service/README.md.
 const base::Feature kAppServiceInstanceRegistry{
-    "AppServiceInstanceRegistry", base::FEATURE_DISABLED_BY_DEFAULT};
+    "AppServiceInstanceRegistry", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kAppServiceIntentHandling{
     "AppServiceIntentHandling", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kAppServiceShelf{"AppServiceShelf",
diff --git a/chrome/services/app_service/DEPS b/chrome/services/app_service/DEPS
index c8e261d..45f95e4 100644
--- a/chrome/services/app_service/DEPS
+++ b/chrome/services/app_service/DEPS
@@ -3,3 +3,9 @@
   "+components/services/app_service/public/cpp",
   "+content/public",
 ]
+
+specific_include_rules = {
+  "instance_registry\.h": [
+    "+ash/public/cpp/shelf_types.h",
+  ]
+}
diff --git a/chrome/services/app_service/public/cpp/instance.h b/chrome/services/app_service/public/cpp/instance.h
index 0228dd43..a771ca0 100644
--- a/chrome/services/app_service/public/cpp/instance.h
+++ b/chrome/services/app_service/public/cpp/instance.h
@@ -20,6 +20,7 @@
   kRunning = 0x02,
   kActive = 0x04,
   kVisible = 0x08,
+  kHidden = 0x10,
   kDestroyed = 0x80,
 };
 
diff --git a/chrome/services/app_service/public/cpp/instance_registry.cc b/chrome/services/app_service/public/cpp/instance_registry.cc
index 60bb85d..2881569 100644
--- a/chrome/services/app_service/public/cpp/instance_registry.cc
+++ b/chrome/services/app_service/public/cpp/instance_registry.cc
@@ -97,6 +97,24 @@
   return std::set<aura::Window*>{};
 }
 
+InstanceState InstanceRegistry::GetState(aura::Window* window) const {
+  auto s_iter = states_.find(window);
+  return (s_iter != states_.end()) ? s_iter->second.get()->State()
+                                   : InstanceState::kUnknown;
+}
+
+ash::ShelfID InstanceRegistry::GetShelfId(aura::Window* window) const {
+  auto s_iter = states_.find(window);
+  return (s_iter != states_.end())
+             ? ash::ShelfID(s_iter->second.get()->AppId(),
+                            s_iter->second.get()->LaunchId())
+             : ash::ShelfID();
+}
+
+bool InstanceRegistry::Exists(aura::Window* window) const {
+  return states_.find(window) != states_.end();
+}
+
 void InstanceRegistry::DoOnInstances(const Instances& deltas) {
   in_progress_ = true;
 
diff --git a/chrome/services/app_service/public/cpp/instance_registry.h b/chrome/services/app_service/public/cpp/instance_registry.h
index 540652e9..d82fa75 100644
--- a/chrome/services/app_service/public/cpp/instance_registry.h
+++ b/chrome/services/app_service/public/cpp/instance_registry.h
@@ -11,6 +11,7 @@
 #include <string>
 #include <vector>
 
+#include "ash/public/cpp/shelf_types.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 #include "base/sequence_checker.h"
@@ -92,6 +93,15 @@
   // Return windows for the |app_id|.
   std::set<aura::Window*> GetWindows(const std::string& app_id);
 
+  // Return the state for the |window|.
+  InstanceState GetState(aura::Window* window) const;
+
+  // Return the shelf id for the |window|.
+  ash::ShelfID GetShelfId(aura::Window* window) const;
+
+  // Return true if there is an instance for the |window|.
+  bool Exists(aura::Window* window) const;
+
   // Calls f, a void-returning function whose arguments are (const
   // apps::InstanceUpdate&), on each window in the instance_registry.
   //
diff --git a/chrome/services/app_service/public/cpp/instance_registry_unittest.cc b/chrome/services/app_service/public/cpp/instance_registry_unittest.cc
index 7329258..6fc3c61 100644
--- a/chrome/services/app_service/public/cpp/instance_registry_unittest.cc
+++ b/chrome/services/app_service/public/cpp/instance_registry_unittest.cc
@@ -34,13 +34,8 @@
   }
 
   apps::InstanceState GetState(apps::InstanceRegistry& instance_registry,
-                               const aura::Window* window) {
-    apps::InstanceState state = apps::InstanceState::kUnknown;
-    instance_registry.ForOneInstance(
-        window, [&state](const apps::InstanceUpdate& update) {
-          state = update.State();
-        });
-    return state;
+                               aura::Window* window) {
+    return instance_registry.GetState(window);
   }
 
   // apps::InstanceRegistry::Observer overrides.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3bb7b98..28cf1f16 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -890,6 +890,8 @@
       "../browser/chrome_worker_browsertest.cc",
       "../browser/chromeos/certificate_provider/test_certificate_provider_extension_login_screen_mixin.cc",
       "../browser/chromeos/certificate_provider/test_certificate_provider_extension_login_screen_mixin.h",
+      "../browser/chromeos/scoped_test_system_nss_key_slot_mixin.cc",
+      "../browser/chromeos/scoped_test_system_nss_key_slot_mixin.h",
       "../browser/client_hints/client_hints_browsertest.cc",
       "../browser/component_updater/component_patcher_operation_browsertest.cc",
       "../browser/content_index/content_index_browsertest.cc",
diff --git a/chrome/test/data/perf/throughput_test_cases/main-animations-throughput.html b/chrome/test/data/perf/throughput_test_cases/main-animations-throughput.html
index 66c2c374..e4e1b24d 100644
--- a/chrome/test/data/perf/throughput_test_cases/main-animations-throughput.html
+++ b/chrome/test/data/perf/throughput_test_cases/main-animations-throughput.html
@@ -32,6 +32,8 @@
   window.location.hash.length > 1 ? +window.location.hash.substr(1) : 0;
 const skipEvery = targetMainFps > 0 ? parseInt(60 / targetMainFps) : 0;
 
+const shouldJank = window.location.search.indexOf('?jank') >= 0;
+
 var currentValue = 0;
 const maxValue = 150;
 const valueChange = 5 * skipEvery;
@@ -54,6 +56,10 @@
       if (currentValue <= minValue)
         incrementing = true;
     }
+    if (shouldJank) {
+      const now = new Date();
+      while ((new Date() - now) < 16) {}
+    }
     mainanim.style[attributeName] = setProp(currentValue);
   }
   if (animating)
diff --git a/chrome/test/data/prerender/prerender_web_audio_device.html b/chrome/test/data/prerender/prerender_web_audio_device.html
deleted file mode 100644
index 59a74436..0000000
--- a/chrome/test/data/prerender/prerender_web_audio_device.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<!DOCTYPE html>
-<!-- This test checks that if a page being prerendered creates an
-     WebAudioDevice,the prerender is cancelled.
--->
-<head>
-  <title>Prerender WebAudioDevice</title>
-</head>
-<body>
-  <script type="text/javascript">
-    var device = new AudioContext();
-    device.createGain();
-  </script>
-</body>
-</html>
-
diff --git a/chromeos/profiles/airmont.afdo.newest.txt b/chromeos/profiles/airmont.afdo.newest.txt
index 7f6e565..3d0c3936 100644
--- a/chromeos/profiles/airmont.afdo.newest.txt
+++ b/chromeos/profiles/airmont.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-airmont-81-3987.18-1577702544-benchmark-81.0.4011.0-r1-redacted.afdo.xz
\ No newline at end of file
+chromeos-chrome-amd64-airmont-81-3987.18-1577702544-benchmark-81.0.4013.0-r1-redacted.afdo.xz
\ No newline at end of file
diff --git a/chromeos/profiles/broadwell.afdo.newest.txt b/chromeos/profiles/broadwell.afdo.newest.txt
index b527432..bbcf255 100644
--- a/chromeos/profiles/broadwell.afdo.newest.txt
+++ b/chromeos/profiles/broadwell.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-broadwell-81-3987.18-1577704324-benchmark-81.0.4011.0-r1-redacted.afdo.xz
\ No newline at end of file
+chromeos-chrome-amd64-broadwell-81-3987.18-1577704324-benchmark-81.0.4013.0-r1-redacted.afdo.xz
\ No newline at end of file
diff --git a/chromeos/profiles/silvermont.afdo.newest.txt b/chromeos/profiles/silvermont.afdo.newest.txt
index 9cb6b04..261fb51 100644
--- a/chromeos/profiles/silvermont.afdo.newest.txt
+++ b/chromeos/profiles/silvermont.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-silvermont-81-3987.18-1577705382-benchmark-81.0.4011.0-r1-redacted.afdo.xz
\ No newline at end of file
+chromeos-chrome-amd64-silvermont-81-3987.18-1577705382-benchmark-81.0.4013.0-r1-redacted.afdo.xz
\ No newline at end of file
diff --git a/components/pdf/renderer/pdf_accessibility_tree.cc b/components/pdf/renderer/pdf_accessibility_tree.cc
index c100e088..8bf618f3 100644
--- a/components/pdf/renderer/pdf_accessibility_tree.cc
+++ b/components/pdf/renderer/pdf_accessibility_tree.cc
@@ -362,6 +362,16 @@
   if (render_accessibility && tree_.size() > 1) {
     ui::AXNode* root = tree_.root();
     ui::AXNodeData root_data = root->data();
+    root_data.AddIntAttribute(ax::mojom::IntAttribute::kScrollXMin, 0);
+    root_data.AddIntAttribute(ax::mojom::IntAttribute::kScrollXMax,
+                              viewport_info.total_scrollable_size.width);
+    root_data.AddIntAttribute(ax::mojom::IntAttribute::kScrollX,
+                              viewport_info.current_scroll_position.x);
+    root_data.AddIntAttribute(ax::mojom::IntAttribute::kScrollYMin, 0);
+    root_data.AddIntAttribute(ax::mojom::IntAttribute::kScrollYMax,
+                              viewport_info.total_scrollable_size.height);
+    root_data.AddIntAttribute(ax::mojom::IntAttribute::kScrollY,
+                              viewport_info.current_scroll_position.y);
     root_data.relative_bounds.transform =
         base::WrapUnique(MakeTransformFromViewInfo());
     root->SetData(root_data);
diff --git a/components/viz/service/display_embedder/direct_context_provider.cc b/components/viz/service/display_embedder/direct_context_provider.cc
index df76fc0..0b5ce9e 100644
--- a/components/viz/service/display_embedder/direct_context_provider.cc
+++ b/components/viz/service/display_embedder/direct_context_provider.cc
@@ -39,7 +39,10 @@
     const gpu::GpuPreferences& gpu_preferences,
     gpu::gles2::FeatureInfo* feature_info,
     std::unique_ptr<DirectContextProviderDelegate> delegate)
-    : translator_cache_(gpu_preferences), delegate_(std::move(delegate)) {
+    : discardable_manager_(gpu_preferences),
+      passthrough_discardable_manager_(gpu_preferences),
+      translator_cache_(gpu_preferences),
+      delegate_(std::move(delegate)) {
   DCHECK(gl_context->IsCurrent(gl_surface.get()));
 
   auto limits = gpu::SharedMemoryLimits::ForMailboxContext();
diff --git a/content/browser/network_context_client_base_impl.cc b/content/browser/network_context_client_base_impl.cc
index 414ea85..221275f 100644
--- a/content/browser/network_context_client_base_impl.cc
+++ b/content/browser/network_context_client_base_impl.cc
@@ -22,7 +22,7 @@
 namespace {
 
 void HandleFileUploadRequest(
-    uint32_t process_id,
+    int32_t process_id,
     bool async,
     const std::vector<base::FilePath>& file_paths,
     network::mojom::NetworkContextClient::OnFileUploadRequestedCallback
@@ -65,7 +65,7 @@
 }  // namespace
 
 void NetworkContextOnFileUploadRequested(
-    uint32_t process_id,
+    int32_t process_id,
     bool async,
     const std::vector<base::FilePath>& file_paths,
     network::mojom::NetworkContextClient::OnFileUploadRequestedCallback
@@ -83,8 +83,8 @@
 
 void NetworkContextClientBase::OnAuthRequired(
     const base::Optional<base::UnguessableToken>& window_id,
-    uint32_t process_id,
-    uint32_t routing_id,
+    int32_t process_id,
+    int32_t routing_id,
     uint32_t request_id,
     const GURL& url,
     bool first_auth_attempt,
@@ -99,8 +99,8 @@
 
 void NetworkContextClientBase::OnCertificateRequested(
     const base::Optional<base::UnguessableToken>& window_id,
-    uint32_t process_id,
-    uint32_t routing_id,
+    int32_t process_id,
+    int32_t routing_id,
     uint32_t request_id,
     const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
     mojo::PendingRemote<network::mojom::ClientCertificateResponder>
@@ -111,8 +111,8 @@
 }
 
 void NetworkContextClientBase::OnSSLCertificateError(
-    uint32_t process_id,
-    uint32_t routing_id,
+    int32_t process_id,
+    int32_t routing_id,
     const GURL& url,
     int net_error,
     const net::SSLInfo& ssl_info,
@@ -122,7 +122,7 @@
 }
 
 void NetworkContextClientBase::OnFileUploadRequested(
-    uint32_t process_id,
+    int32_t process_id,
     bool async,
     const std::vector<base::FilePath>& file_paths,
     OnFileUploadRequestedCallback callback) {
@@ -143,7 +143,7 @@
 }
 
 void NetworkContextClientBase::OnClearSiteData(
-    uint32_t process_id,
+    int32_t process_id,
     int32_t routing_id,
     const GURL& url,
     const std::string& header_value,
diff --git a/content/browser/network_context_client_base_impl.h b/content/browser/network_context_client_base_impl.h
index bbdde0af..ab7e264 100644
--- a/content/browser/network_context_client_base_impl.h
+++ b/content/browser/network_context_client_base_impl.h
@@ -12,7 +12,7 @@
 // Helper method that NetworkContext::OnFileUploadRequested need to use for
 // their implementation.
 void NetworkContextOnFileUploadRequested(
-    uint32_t process_id,
+    int32_t process_id,
     bool async,
     const std::vector<base::FilePath>& file_paths,
     network::mojom::NetworkContextClient::OnFileUploadRequestedCallback
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 6978dcbf..8e1ab0e 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -1619,8 +1619,8 @@
 
 void StoragePartitionImpl::OnAuthRequired(
     const base::Optional<base::UnguessableToken>& window_id,
-    uint32_t process_id,
-    uint32_t routing_id,
+    int32_t process_id,
+    int32_t routing_id,
     uint32_t request_id,
     const GURL& url,
     bool first_auth_attempt,
@@ -1654,8 +1654,8 @@
 
 void StoragePartitionImpl::OnCertificateRequested(
     const base::Optional<base::UnguessableToken>& window_id,
-    uint32_t process_id,
-    uint32_t routing_id,
+    int32_t process_id,
+    int32_t routing_id,
     uint32_t request_id,
     const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
     mojo::PendingRemote<network::mojom::ClientCertificateResponder>
@@ -1682,8 +1682,8 @@
 }
 
 void StoragePartitionImpl::OnSSLCertificateError(
-    uint32_t process_id,
-    uint32_t routing_id,
+    int32_t process_id,
+    int32_t routing_id,
     const GURL& url,
     int net_error,
     const net::SSLInfo& ssl_info,
@@ -1698,7 +1698,7 @@
 }
 
 void StoragePartitionImpl::OnFileUploadRequested(
-    uint32_t process_id,
+    int32_t process_id,
     bool async,
     const std::vector<base::FilePath>& file_paths,
     OnFileUploadRequestedCallback callback) {
@@ -1739,7 +1739,7 @@
       blink::mojom::PermissionStatus::GRANTED);
 }
 
-void StoragePartitionImpl::OnClearSiteData(uint32_t process_id,
+void StoragePartitionImpl::OnClearSiteData(int32_t process_id,
                                            int32_t routing_id,
                                            const GURL& url,
                                            const std::string& header_value,
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h
index 840e38d..1809fac 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -197,8 +197,8 @@
   // network::mojom::NetworkContextClient interface.
   void OnAuthRequired(
       const base::Optional<base::UnguessableToken>& window_id,
-      uint32_t process_id,
-      uint32_t routing_id,
+      int32_t process_id,
+      int32_t routing_id,
       uint32_t request_id,
       const GURL& url,
       bool first_auth_attempt,
@@ -208,20 +208,20 @@
           auth_challenge_responder) override;
   void OnCertificateRequested(
       const base::Optional<base::UnguessableToken>& window_id,
-      uint32_t process_id,
-      uint32_t routing_id,
+      int32_t process_id,
+      int32_t routing_id,
       uint32_t request_id,
       const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
       mojo::PendingRemote<network::mojom::ClientCertificateResponder>
           cert_responder) override;
-  void OnSSLCertificateError(uint32_t process_id,
-                             uint32_t routing_id,
+  void OnSSLCertificateError(int32_t process_id,
+                             int32_t routing_id,
                              const GURL& url,
                              int net_error,
                              const net::SSLInfo& ssl_info,
                              bool fatal,
                              OnSSLCertificateErrorCallback response) override;
-  void OnFileUploadRequested(uint32_t process_id,
+  void OnFileUploadRequested(int32_t process_id,
                              bool async,
                              const std::vector<base::FilePath>& file_paths,
                              OnFileUploadRequestedCallback callback) override;
@@ -231,7 +231,7 @@
   void OnCanSendDomainReliabilityUpload(
       const GURL& origin,
       OnCanSendDomainReliabilityUploadCallback callback) override;
-  void OnClearSiteData(uint32_t process_id,
+  void OnClearSiteData(int32_t process_id,
                        int32_t routing_id,
                        const GURL& url,
                        const std::string& header_value,
diff --git a/content/public/browser/network_context_client_base.h b/content/public/browser/network_context_client_base.h
index b9ab9b1..7cdb6a39 100644
--- a/content/public/browser/network_context_client_base.h
+++ b/content/public/browser/network_context_client_base.h
@@ -24,8 +24,8 @@
   // network::mojom::NetworkContextClient implementation:
   void OnAuthRequired(
       const base::Optional<base::UnguessableToken>& window_id,
-      uint32_t process_id,
-      uint32_t routing_id,
+      int32_t process_id,
+      int32_t routing_id,
       uint32_t request_id,
       const GURL& url,
       bool first_auth_attempt,
@@ -35,20 +35,20 @@
           auth_challenge_responder) override;
   void OnCertificateRequested(
       const base::Optional<base::UnguessableToken>& window_id,
-      uint32_t process_id,
-      uint32_t routing_id,
+      int32_t process_id,
+      int32_t routing_id,
       uint32_t request_id,
       const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
       mojo::PendingRemote<network::mojom::ClientCertificateResponder>
           cert_responder) override;
-  void OnSSLCertificateError(uint32_t process_id,
-                             uint32_t routing_id,
+  void OnSSLCertificateError(int32_t process_id,
+                             int32_t routing_id,
                              const GURL& url,
                              int net_error,
                              const net::SSLInfo& ssl_info,
                              bool fatal,
                              OnSSLCertificateErrorCallback response) override;
-  void OnFileUploadRequested(uint32_t process_id,
+  void OnFileUploadRequested(int32_t process_id,
                              bool async,
                              const std::vector<base::FilePath>& file_paths,
                              OnFileUploadRequestedCallback callback) override;
@@ -58,7 +58,7 @@
   void OnCanSendDomainReliabilityUpload(
       const GURL& origin,
       OnCanSendDomainReliabilityUploadCallback callback) override;
-  void OnClearSiteData(uint32_t process_id,
+  void OnClearSiteData(int32_t process_id,
                        int32_t routing_id,
                        const GURL& url,
                        const std::string& header_value,
diff --git a/fuchsia/engine/context_provider_impl.cc b/fuchsia/engine/context_provider_impl.cc
index 47c15e3..6fe6dde 100644
--- a/fuchsia/engine/context_provider_impl.cc
+++ b/fuchsia/engine/context_provider_impl.cc
@@ -153,6 +153,7 @@
       switches::kEnableFuchsiaAudioConsumer,
       switches::kEnableLowEndDeviceMode,
       switches::kForceGpuMemAvailableMb,
+      switches::kForceGpuMemDiscardableLimitMb,
       switches::kMinHeightForGpuRasterTile,
       switches::kRendererProcessLimit,
   };
diff --git a/gpu/command_buffer/service/context_group_unittest.cc b/gpu/command_buffer/service/context_group_unittest.cc
index 5ed2ee7..a3c4c70 100644
--- a/gpu/command_buffer/service/context_group_unittest.cc
+++ b/gpu/command_buffer/service/context_group_unittest.cc
@@ -40,7 +40,7 @@
  public:
   static const bool kBindGeneratesResource = false;
 
-  ContextGroupTest() = default;
+  ContextGroupTest() : discardable_manager_(gpu_preferences_) {}
 
  protected:
   void SetUp() override {
@@ -171,5 +171,3 @@
 
 }  // namespace gles2
 }  // namespace gpu
-
-
diff --git a/gpu/command_buffer/service/framebuffer_manager_unittest.cc b/gpu/command_buffer/service/framebuffer_manager_unittest.cc
index 22be5a54..5cf6f4f9 100644
--- a/gpu/command_buffer/service/framebuffer_manager_unittest.cc
+++ b/gpu/command_buffer/service/framebuffer_manager_unittest.cc
@@ -17,6 +17,7 @@
 #include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/test_helper.h"
 #include "gpu/command_buffer/service/texture_manager.h"
+#include "gpu/config/gpu_preferences.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/gl_mock.h"
 
@@ -44,7 +45,8 @@
  public:
   FramebufferManagerTest()
       : manager_(1, 1, nullptr),
-        feature_info_(new FeatureInfo()) {
+        feature_info_(new FeatureInfo()),
+        discardable_manager_(GpuPreferences()) {
     texture_manager_.reset(new TextureManager(
         nullptr, feature_info_.get(), kMaxTextureSize, kMaxCubemapSize,
         kMaxRectangleTextureSize, kMax3DTextureSize, kMaxArrayTextureLayers,
@@ -123,7 +125,8 @@
         manager_(kMaxDrawBuffers,
                  kMaxColorAttachments,
                  &framebuffer_completeness_cache_),
-        feature_info_(new FeatureInfo()) {
+        feature_info_(new FeatureInfo()),
+        discardable_manager_(GpuPreferences()) {
     texture_manager_.reset(new TextureManager(
         nullptr, feature_info_.get(), kMaxTextureSize, kMaxCubemapSize,
         kMaxRectangleTextureSize, kMax3DTextureSize, kMaxArrayTextureLayers,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index d89c9ee..3118624 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -132,7 +132,8 @@
       cached_stencil_front_mask_(static_cast<GLuint>(-1)),
       cached_stencil_back_mask_(static_cast<GLuint>(-1)),
       shader_language_version_(100),
-      shader_translator_cache_(gpu_preferences_) {
+      shader_translator_cache_(gpu_preferences_),
+      discardable_manager_(gpu_preferences_) {
   memset(immediate_buffer_, 0xEE, sizeof(immediate_buffer_));
 }
 
@@ -2394,7 +2395,9 @@
 GLES2DecoderPassthroughTestBase::GLES2DecoderPassthroughTestBase(
     ContextType context_type)
     : gpu_preferences_(GenerateGpuPreferencesForPassthroughTests()),
-      shader_translator_cache_(gpu_preferences_) {
+      shader_translator_cache_(gpu_preferences_),
+      discardable_manager_(gpu_preferences_),
+      passthrough_discardable_manager_(gpu_preferences_) {
   context_creation_attribs_.context_type = context_type;
 }
 
diff --git a/gpu/command_buffer/service/gpu_switches.cc b/gpu/command_buffer/service/gpu_switches.cc
index f9aadc7..0a878ad 100644
--- a/gpu/command_buffer/service/gpu_switches.cc
+++ b/gpu/command_buffer/service/gpu_switches.cc
@@ -43,6 +43,10 @@
 // Sets the total amount of memory that may be allocated for GPU resources
 const char kForceGpuMemAvailableMb[]        = "force-gpu-mem-available-mb";
 
+// Sets the maximum GPU memory to use for discardable caches.
+const char kForceGpuMemDiscardableLimitMb[] =
+    "force-gpu-mem-discardable-limit-mb";
+
 // Sets the maximum size of the in-memory gpu program cache, in kb
 const char kGpuProgramCacheSizeKb[]         = "gpu-program-cache-size-kb";
 
diff --git a/gpu/command_buffer/service/gpu_switches.h b/gpu/command_buffer/service/gpu_switches.h
index ee702988..ed9eda57 100644
--- a/gpu/command_buffer/service/gpu_switches.h
+++ b/gpu/command_buffer/service/gpu_switches.h
@@ -23,6 +23,7 @@
 GPU_EXPORT extern const char kDisableGpuProgramCache[];
 GPU_EXPORT extern const char kEnforceGLMinimums[];
 GPU_EXPORT extern const char kForceGpuMemAvailableMb[];
+GPU_EXPORT extern const char kForceGpuMemDiscardableLimitMb[];
 GPU_EXPORT extern const char kGpuProgramCacheSizeKb[];
 GPU_EXPORT extern const char kDisableGpuShaderDiskCache[];
 GPU_EXPORT extern const char kEnableThreadedTextureMailboxes[];
diff --git a/gpu/command_buffer/service/gr_cache_controller_unittest.cc b/gpu/command_buffer/service/gr_cache_controller_unittest.cc
index f70344e..724f94b 100644
--- a/gpu/command_buffer/service/gr_cache_controller_unittest.cc
+++ b/gpu/command_buffer/service/gr_cache_controller_unittest.cc
@@ -40,7 +40,7 @@
     context_state_ = base::MakeRefCounted<SharedContextState>(
         std::move(share_group), std::move(surface), std::move(context),
         false /* use_virtualized_gl_contexts */, base::DoNothing());
-    context_state_->InitializeGrContext(workarounds, nullptr);
+    context_state_->InitializeGrContext(GpuPreferences(), workarounds, nullptr);
     auto feature_info =
         base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo());
     context_state_->InitializeGL(GpuPreferences(), std::move(feature_info));
diff --git a/gpu/command_buffer/service/passthrough_discardable_manager.cc b/gpu/command_buffer/service/passthrough_discardable_manager.cc
index 631aaa46..82d433d3 100644
--- a/gpu/command_buffer/service/passthrough_discardable_manager.cc
+++ b/gpu/command_buffer/service/passthrough_discardable_manager.cc
@@ -7,6 +7,7 @@
 #include "gpu/command_buffer/service/context_group.h"
 #include "gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h"
 #include "gpu/command_buffer/service/service_discardable_manager.h"
+#include "gpu/config/gpu_preferences.h"
 
 namespace gpu {
 
@@ -25,9 +26,12 @@
 PassthroughDiscardableManager::DiscardableCacheValue::~DiscardableCacheValue() =
     default;
 
-PassthroughDiscardableManager::PassthroughDiscardableManager()
+PassthroughDiscardableManager::PassthroughDiscardableManager(
+    const GpuPreferences& preferences)
     : cache_(DiscardableCache::NO_AUTO_EVICT),
-      cache_size_limit_(DiscardableCacheSizeLimit()) {}
+      cache_size_limit_(preferences.force_gpu_mem_discardable_limit_bytes
+                            ? preferences.force_gpu_mem_discardable_limit_bytes
+                            : DiscardableCacheSizeLimit()) {}
 
 PassthroughDiscardableManager::~PassthroughDiscardableManager() {
   DCHECK(cache_.empty());
diff --git a/gpu/command_buffer/service/passthrough_discardable_manager.h b/gpu/command_buffer/service/passthrough_discardable_manager.h
index e4174a9..c8a566a1 100644
--- a/gpu/command_buffer/service/passthrough_discardable_manager.h
+++ b/gpu/command_buffer/service/passthrough_discardable_manager.h
@@ -11,6 +11,7 @@
 #include "gpu/gpu_gles2_export.h"
 
 namespace gpu {
+struct GpuPreferences;
 namespace gles2 {
 class TexturePassthrough;
 class ContextGroup;
@@ -18,7 +19,7 @@
 
 class GPU_GLES2_EXPORT PassthroughDiscardableManager {
  public:
-  PassthroughDiscardableManager();
+  explicit PassthroughDiscardableManager(const GpuPreferences& preferences);
   ~PassthroughDiscardableManager();
 
   void InitializeTexture(uint32_t client_id,
diff --git a/gpu/command_buffer/service/raster_decoder_unittest.cc b/gpu/command_buffer/service/raster_decoder_unittest.cc
index ac90def..0ac91df5 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest.cc
@@ -21,6 +21,7 @@
 #include "gpu/command_buffer/service/shared_image_factory.h"
 #include "gpu/command_buffer/service/shared_image_manager.h"
 #include "gpu/command_buffer/service/test_helper.h"
+#include "gpu/config/gpu_preferences.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/gl_image_stub.h"
 #include "ui/gl/gl_mock.h"
@@ -337,7 +338,7 @@
         std::move(share_group), std::move(surface), std::move(context),
         false /* use_virtualized_gl_contexts */, base::DoNothing(),
         GpuPreferences().gr_context_type);
-    context_state_->InitializeGrContext(workarounds, nullptr);
+    context_state_->InitializeGrContext(GpuPreferences(), workarounds, nullptr);
     context_state_->InitializeGL(GpuPreferences(), feature_info);
   }
   void TearDown() override {
diff --git a/gpu/command_buffer/service/service_discardable_manager.cc b/gpu/command_buffer/service/service_discardable_manager.cc
index 52fe92b..5852112 100644
--- a/gpu/command_buffer/service/service_discardable_manager.cc
+++ b/gpu/command_buffer/service/service_discardable_manager.cc
@@ -6,13 +6,17 @@
 
 #include <inttypes.h>
 
+#include "base/command_line.h"
 #include "base/memory/singleton.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "build/build_config.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
 #include "gpu/command_buffer/service/texture_manager.h"
+#include "gpu/config/gpu_preferences.h"
 
 namespace gpu {
 
@@ -80,9 +84,12 @@
 ServiceDiscardableManager::GpuDiscardableEntry::~GpuDiscardableEntry() =
     default;
 
-ServiceDiscardableManager::ServiceDiscardableManager()
+ServiceDiscardableManager::ServiceDiscardableManager(
+    const GpuPreferences& preferences)
     : entries_(EntryCache::NO_AUTO_EVICT),
-      cache_size_limit_(DiscardableCacheSizeLimit()) {
+      cache_size_limit_(preferences.force_gpu_mem_discardable_limit_bytes
+                            ? preferences.force_gpu_mem_discardable_limit_bytes
+                            : DiscardableCacheSizeLimit()) {
   // In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview).
   // Don't register a dump provider in these cases.
   if (base::ThreadTaskRunnerHandle::IsSet()) {
diff --git a/gpu/command_buffer/service/service_discardable_manager.h b/gpu/command_buffer/service/service_discardable_manager.h
index 4a54490..522b4a16 100644
--- a/gpu/command_buffer/service/service_discardable_manager.h
+++ b/gpu/command_buffer/service/service_discardable_manager.h
@@ -14,6 +14,7 @@
 #include "gpu/gpu_gles2_export.h"
 
 namespace gpu {
+struct GpuPreferences;
 namespace gles2 {
 class TextureManager;
 class TextureRef;
@@ -27,7 +28,7 @@
 class GPU_GLES2_EXPORT ServiceDiscardableManager
     : public base::trace_event::MemoryDumpProvider {
  public:
-  ServiceDiscardableManager();
+  explicit ServiceDiscardableManager(const GpuPreferences& preferences);
   ~ServiceDiscardableManager() override;
 
   // base::trace_event::MemoryDumpProvider implementation.
diff --git a/gpu/command_buffer/service/service_discardable_manager_unittest.cc b/gpu/command_buffer/service/service_discardable_manager_unittest.cc
index 1a50b05a..6d1aa65 100644
--- a/gpu/command_buffer/service/service_discardable_manager_unittest.cc
+++ b/gpu/command_buffer/service/service_discardable_manager_unittest.cc
@@ -15,6 +15,7 @@
 #include "gpu/command_buffer/service/shared_image_manager.h"
 #include "gpu/command_buffer/service/test_helper.h"
 #include "gpu/command_buffer/service/texture_manager.h"
+#include "gpu/config/gpu_preferences.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/gl_image_stub.h"
 #include "ui/gl/gl_mock.h"
@@ -65,7 +66,7 @@
 
 class ServiceDiscardableManagerTest : public GpuServiceTest {
  public:
-  ServiceDiscardableManagerTest() = default;
+  ServiceDiscardableManagerTest() : discardable_manager_(GpuPreferences()) {}
   ~ServiceDiscardableManagerTest() override = default;
 
  protected:
diff --git a/gpu/command_buffer/service/service_transfer_cache.cc b/gpu/command_buffer/service/service_transfer_cache.cc
index c15e9b63..e2adebe6 100644
--- a/gpu/command_buffer/service/service_transfer_cache.cc
+++ b/gpu/command_buffer/service/service_transfer_cache.cc
@@ -8,6 +8,7 @@
 
 #include <utility>
 
+#include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
@@ -123,9 +124,11 @@
 ServiceTransferCache::CacheEntryInternal::operator=(
     CacheEntryInternal&& other) = default;
 
-ServiceTransferCache::ServiceTransferCache()
+ServiceTransferCache::ServiceTransferCache(const GpuPreferences& preferences)
     : entries_(EntryCache::NO_AUTO_EVICT),
-      cache_size_limit_(DiscardableCacheSizeLimit()),
+      cache_size_limit_(preferences.force_gpu_mem_discardable_limit_bytes
+                            ? preferences.force_gpu_mem_discardable_limit_bytes
+                            : DiscardableCacheSizeLimit()),
       max_cache_entries_(kMaxCacheEntries) {
   // In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview).
   // Don't register a dump provider in these cases.
@@ -249,21 +252,10 @@
 
 void ServiceTransferCache::PurgeMemory(
     base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
-  switch (memory_pressure_level) {
-    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
-      // This function is only called with moderate or critical pressure.
-      NOTREACHED();
-      return;
-    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
-      cache_size_limit_ = cache_size_limit_ / 4;
-      break;
-    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
-      cache_size_limit_ = 0u;
-      break;
-  }
-
+  base::AutoReset<size_t> reset_limit(
+      &cache_size_limit_, DiscardableCacheSizeLimitForPressure(
+                              cache_size_limit_, memory_pressure_level));
   EnforceLimits();
-  cache_size_limit_ = DiscardableCacheSizeLimit();
 }
 
 void ServiceTransferCache::DeleteAllEntriesForDecoder(int decoder_id) {
diff --git a/gpu/command_buffer/service/service_transfer_cache.h b/gpu/command_buffer/service/service_transfer_cache.h
index 990152b3..c51b336 100644
--- a/gpu/command_buffer/service/service_transfer_cache.h
+++ b/gpu/command_buffer/service/service_transfer_cache.h
@@ -48,7 +48,7 @@
     uint32_t entry_id;
   };
 
-  ServiceTransferCache();
+  explicit ServiceTransferCache(const GpuPreferences& preferences);
   ~ServiceTransferCache() override;
 
   bool CreateLockedEntry(const EntryKey& key,
@@ -137,10 +137,10 @@
   int total_image_count_ = 0;
 
   // The limit above which the cache will start evicting resources.
-  size_t cache_size_limit_ = 0;
+  size_t cache_size_limit_;
 
   // The max number of entries we will hold in the cache.
-  size_t max_cache_entries_ = 0;
+  size_t max_cache_entries_;
 
   DISALLOW_COPY_AND_ASSIGN(ServiceTransferCache);
 };
diff --git a/gpu/command_buffer/service/service_transfer_cache_unittest.cc b/gpu/command_buffer/service/service_transfer_cache_unittest.cc
index ff4412c..4308b549 100644
--- a/gpu/command_buffer/service/service_transfer_cache_unittest.cc
+++ b/gpu/command_buffer/service/service_transfer_cache_unittest.cc
@@ -5,6 +5,7 @@
 #include "gpu/command_buffer/service/service_transfer_cache.h"
 
 #include "cc/paint/raw_memory_transfer_cache_entry.h"
+#include "gpu/config/gpu_preferences.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace gpu {
@@ -21,7 +22,7 @@
 }
 
 TEST(ServiceTransferCacheTest, EnforcesOnPurgeMemory) {
-  ServiceTransferCache cache;
+  ServiceTransferCache cache{GpuPreferences()};
   uint32_t entry_id = 0u;
   size_t entry_size = 1024u;
   uint32_t number_of_entry = 4u;
@@ -57,7 +58,7 @@
 }
 
 TEST(ServiceTransferCache, MultipleDecoderUse) {
-  ServiceTransferCache cache;
+  ServiceTransferCache cache{GpuPreferences()};
   const uint32_t entry_id = 0u;
   const size_t entry_size = 1024u;
 
@@ -84,7 +85,7 @@
 }
 
 TEST(ServiceTransferCache, DeleteEntriesForDecoder) {
-  ServiceTransferCache cache;
+  ServiceTransferCache cache{GpuPreferences()};
   const size_t entry_size = 1024u;
   const size_t cache_size = 4 * entry_size;
   cache.SetCacheSizeLimitForTesting(cache_size);
diff --git a/gpu/command_buffer/service/service_utils.cc b/gpu/command_buffer/service/service_utils.cc
index 0a7df7e..5eaa199 100644
--- a/gpu/command_buffer/service/service_utils.cc
+++ b/gpu/command_buffer/service/service_utils.cc
@@ -129,8 +129,13 @@
   gpu_preferences.enforce_gl_minimums =
       command_line->HasSwitch(switches::kEnforceGLMinimums);
   if (GetUintFromSwitch(command_line, switches::kForceGpuMemAvailableMb,
-                        &gpu_preferences.force_gpu_mem_available)) {
-    gpu_preferences.force_gpu_mem_available *= 1024 * 1024;
+                        &gpu_preferences.force_gpu_mem_available_bytes)) {
+    gpu_preferences.force_gpu_mem_available_bytes *= 1024 * 1024;
+  }
+  if (GetUintFromSwitch(
+          command_line, switches::kForceGpuMemDiscardableLimitMb,
+          &gpu_preferences.force_gpu_mem_discardable_limit_bytes)) {
+    gpu_preferences.force_gpu_mem_discardable_limit_bytes *= 1024 * 1024;
   }
   if (GetUintFromSwitch(command_line, switches::kGpuProgramCacheSizeKb,
                         &gpu_preferences.gpu_program_cache_size)) {
diff --git a/gpu/command_buffer/service/shared_context_state.cc b/gpu/command_buffer/service/shared_context_state.cc
index 5ac1d3e4..67e63f4 100644
--- a/gpu/command_buffer/service/shared_context_state.cc
+++ b/gpu/command_buffer/service/shared_context_state.cc
@@ -155,6 +155,7 @@
 }
 
 void SharedContextState::InitializeGrContext(
+    const GpuPreferences& gpu_preferences,
     const GpuDriverBugWorkarounds& workarounds,
     GrContextOptions::PersistentCache* cache,
     GpuProcessActivityFlags* activity_flags,
@@ -220,7 +221,7 @@
   } else {
     gr_context_->setResourceCacheLimit(max_resource_cache_bytes_);
   }
-  transfer_cache_ = std::make_unique<ServiceTransferCache>();
+  transfer_cache_ = std::make_unique<ServiceTransferCache>(gpu_preferences);
 }
 
 bool SharedContextState::InitializeGL(
diff --git a/gpu/command_buffer/service/shared_context_state.h b/gpu/command_buffer/service/shared_context_state.h
index d3a5606..7c761b60 100644
--- a/gpu/command_buffer/service/shared_context_state.h
+++ b/gpu/command_buffer/service/shared_context_state.h
@@ -64,7 +64,8 @@
       viz::DawnContextProvider* dawn_context_provider = nullptr,
       gpu::MemoryTracker::Observer* peak_memory_monitor = nullptr);
 
-  void InitializeGrContext(const GpuDriverBugWorkarounds& workarounds,
+  void InitializeGrContext(const GpuPreferences& gpu_preferences,
+                           const GpuDriverBugWorkarounds& workarounds,
                            GrContextOptions::PersistentCache* cache,
                            GpuProcessActivityFlags* activity_flags = nullptr,
                            gl::ProgressReporter* progress_reporter = nullptr);
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc
index f34ebf15..eec33c3c 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc
@@ -58,7 +58,7 @@
     context_state_ = base::MakeRefCounted<SharedContextState>(
         std::move(share_group), surface_, context_,
         false /* use_virtualized_gl_contexts */, base::DoNothing());
-    context_state_->InitializeGrContext(workarounds, nullptr);
+    context_state_->InitializeGrContext(GpuPreferences(), workarounds, nullptr);
     auto feature_info =
         base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo());
     context_state_->InitializeGL(GpuPreferences(), std::move(feature_info));
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_d3d_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_d3d_unittest.cc
index 2c5bb23..a8e4214 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_d3d_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_d3d_unittest.cc
@@ -427,7 +427,7 @@
     context_state_ = base::MakeRefCounted<SharedContextState>(
         std::move(share_group), surface_, context_,
         /*use_virtualized_gl_contexts=*/false, base::DoNothing());
-    context_state_->InitializeGrContext(workarounds, nullptr);
+    context_state_->InitializeGrContext(GpuPreferences(), workarounds, nullptr);
     auto feature_info =
         base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo());
     context_state_->InitializeGL(GpuPreferences(), std::move(feature_info));
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
index beab91e..751ee24 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
@@ -64,7 +64,7 @@
   context_state = base::MakeRefCounted<SharedContextState>(
       std::move(share_group), surface, context,
       false /* use_virtualized_gl_contexts */, base::DoNothing());
-  context_state->InitializeGrContext(workarounds, nullptr);
+  context_state->InitializeGrContext(GpuPreferences(), workarounds, nullptr);
   context_state->InitializeGL(GpuPreferences(), feature_info);
 }
 
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_iosurface_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_iosurface_unittest.cc
index cc58235..19e87966 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_iosurface_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_iosurface_unittest.cc
@@ -53,7 +53,7 @@
     context_state_ = base::MakeRefCounted<SharedContextState>(
         std::move(share_group), surface_, context_,
         false /* use_virtualized_gl_contexts */, base::DoNothing());
-    context_state_->InitializeGrContext(workarounds, nullptr);
+    context_state_->InitializeGrContext(GpuPreferences(), workarounds, nullptr);
     auto feature_info =
         base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo());
     context_state_->InitializeGL(GpuPreferences(), std::move(feature_info));
diff --git a/gpu/command_buffer/service/texture_manager_unittest.cc b/gpu/command_buffer/service/texture_manager_unittest.cc
index 106e2e4..fd96443 100644
--- a/gpu/command_buffer/service/texture_manager_unittest.cc
+++ b/gpu/command_buffer/service/texture_manager_unittest.cc
@@ -71,7 +71,7 @@
   static const GLint kMax3dLevels = 10;
   static const bool kUseDefaultTextures = false;
 
-  TextureManagerTest() {
+  TextureManagerTest() : discardable_manager_(GpuPreferences()) {
     GpuDriverBugWorkarounds gpu_driver_bug_workaround;
     feature_info_ =
         new FeatureInfo(gpu_driver_bug_workaround, GpuFeatureInfo());
@@ -638,8 +638,8 @@
   static const bool kUseDefaultTextures = false;
 
   TextureTestBase()
-      : feature_info_(new FeatureInfo()) {
-  }
+      : feature_info_(new FeatureInfo()),
+        discardable_manager_(GpuPreferences()) {}
   ~TextureTestBase() override { texture_ref_ = nullptr; }
 
  protected:
@@ -2208,7 +2208,9 @@
  public:
   static const bool kUseDefaultTextures = false;
 
-  SharedTextureTest() : feature_info_(new FeatureInfo()) {}
+  SharedTextureTest()
+      : feature_info_(new FeatureInfo()),
+        discardable_manager_(GpuPreferences()) {}
 
   ~SharedTextureTest() override = default;
 
diff --git a/gpu/command_buffer/tests/decoder_perftest.cc b/gpu/command_buffer/tests/decoder_perftest.cc
index eb59284..8b28e13 100644
--- a/gpu/command_buffer/tests/decoder_perftest.cc
+++ b/gpu/command_buffer/tests/decoder_perftest.cc
@@ -153,6 +153,8 @@
   RecordReplayContext()
       : gpu_preferences_(GetGpuPreferences()),
         share_group_(new gl::GLShareGroup),
+        discardable_manager_(gpu::GpuPreferences()),
+        passthrough_discardable_manager_(gpu::GpuPreferences()),
         translator_cache_(gpu_preferences_) {
     bool bind_generates_resource = false;
     if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-stub")) {
diff --git a/gpu/command_buffer/tests/fuzzer_main.cc b/gpu/command_buffer/tests/fuzzer_main.cc
index 7a8d1e2..ce879bf8 100644
--- a/gpu/command_buffer/tests/fuzzer_main.cc
+++ b/gpu/command_buffer/tests/fuzzer_main.cc
@@ -339,9 +339,10 @@
 #else
 #error invalid configuration
 #endif
-    discardable_manager_ = std::make_unique<ServiceDiscardableManager>();
+    discardable_manager_ =
+        std::make_unique<ServiceDiscardableManager>(gpu_preferences_);
     passthrough_discardable_manager_ =
-        std::make_unique<PassthroughDiscardableManager>();
+        std::make_unique<PassthroughDiscardableManager>(gpu_preferences_);
 
     if (gpu_preferences_.use_passthrough_cmd_decoder)
       recreate_context_ = true;
@@ -384,7 +385,8 @@
         share_group_, surface_, std::move(shared_context),
         config_.workarounds.use_virtualized_gl_contexts, base::DoNothing(),
         gpu_preferences_.gr_context_type);
-    context_state_->InitializeGrContext(config_.workarounds, nullptr);
+    context_state_->InitializeGrContext(gpu_preferences_, config_.workarounds,
+                                        nullptr);
     context_state_->InitializeGL(gpu_preferences_, feature_info);
 
     shared_image_manager_ = std::make_unique<SharedImageManager>();
diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc
index 4066803..6d051bf 100644
--- a/gpu/command_buffer/tests/gl_manager.cc
+++ b/gpu/command_buffer/tests/gl_manager.cc
@@ -337,8 +337,13 @@
   attribs.offscreen_framebuffer_size = options.size;
   attribs.buffer_preserved = options.preserve_backbuffer;
   attribs.bind_generates_resource = options.bind_generates_resource;
+
   translator_cache_ =
       std::make_unique<gles2::ShaderTranslatorCache>(gpu_preferences_);
+  discardable_manager_ =
+      std::make_unique<ServiceDiscardableManager>(gpu_preferences_);
+  passthrough_discardable_manager_ =
+      std::make_unique<PassthroughDiscardableManager>(gpu_preferences_);
 
   if (!context_group) {
     GpuFeatureInfo gpu_feature_info;
@@ -351,7 +356,7 @@
         translator_cache_.get(), &completeness_cache_, feature_info,
         options.bind_generates_resource, &image_manager_, options.image_factory,
         nullptr /* progress_reporter */, gpu_feature_info,
-        &discardable_manager_, &passthrough_discardable_manager_,
+        discardable_manager_.get(), passthrough_discardable_manager_.get(),
         &shared_image_manager_);
   }
 
diff --git a/gpu/command_buffer/tests/gl_manager.h b/gpu/command_buffer/tests/gl_manager.h
index 61ec91ee..0baf985 100644
--- a/gpu/command_buffer/tests/gl_manager.h
+++ b/gpu/command_buffer/tests/gl_manager.h
@@ -130,10 +130,10 @@
   gl::GLContext* context() { return context_.get(); }
 
   ServiceDiscardableManager* discardable_manager() {
-    return &discardable_manager_;
+    return discardable_manager_.get();
   }
   PassthroughDiscardableManager* passthrough_discardable_manager() {
-    return &passthrough_discardable_manager_;
+    return passthrough_discardable_manager_.get();
   }
 
   const GpuDriverBugWorkarounds& workarounds() const;
@@ -183,8 +183,9 @@
   gles2::MailboxManagerImpl owned_mailbox_manager_;
   gles2::TraceOutputter outputter_;
   gles2::ImageManager image_manager_;
-  ServiceDiscardableManager discardable_manager_;
-  PassthroughDiscardableManager passthrough_discardable_manager_;
+  std::unique_ptr<ServiceDiscardableManager> discardable_manager_;
+  std::unique_ptr<PassthroughDiscardableManager>
+      passthrough_discardable_manager_;
   std::unique_ptr<gles2::ShaderTranslatorCache> translator_cache_;
   gles2::FramebufferCompletenessCache completeness_cache_;
   MailboxManager* mailbox_manager_ = nullptr;
diff --git a/gpu/config/gpu_preferences.h b/gpu/config/gpu_preferences.h
index b3bfe52..9778656 100644
--- a/gpu/config/gpu_preferences.h
+++ b/gpu/config/gpu_preferences.h
@@ -135,8 +135,11 @@
   // Enforce GL minimums.
   bool enforce_gl_minimums = false;
 
-  // Sets the total amount of memory that may be allocated for GPU resources
-  uint32_t force_gpu_mem_available = 0;
+  // Sets the total amount of memory that may be allocated for GPU resources.
+  uint32_t force_gpu_mem_available_bytes = 0u;
+
+  // Sets the maximum discardable cache size limit for GPU resources.
+  uint32_t force_gpu_mem_discardable_limit_bytes = 0u;
 
   // Sets the maximum size of the in-memory gpu program cache, in kb
   uint32_t gpu_program_cache_size = kDefaultMaxProgramCacheMemoryBytes;
diff --git a/gpu/config/gpu_preferences_unittest.cc b/gpu/config/gpu_preferences_unittest.cc
index c24a7205..99f475b 100644
--- a/gpu/config/gpu_preferences_unittest.cc
+++ b/gpu/config/gpu_preferences_unittest.cc
@@ -46,7 +46,10 @@
             right.enable_gpu_driver_debug_logging);
   EXPECT_EQ(left.disable_gpu_program_cache, right.disable_gpu_program_cache);
   EXPECT_EQ(left.enforce_gl_minimums, right.enforce_gl_minimums);
-  EXPECT_EQ(left.force_gpu_mem_available, right.force_gpu_mem_available);
+  EXPECT_EQ(left.force_gpu_mem_available_bytes,
+            right.force_gpu_mem_available_bytes);
+  EXPECT_EQ(left.force_gpu_mem_discardable_limit_bytes,
+            right.force_gpu_mem_discardable_limit_bytes);
   EXPECT_EQ(left.gpu_program_cache_size, right.gpu_program_cache_size);
   EXPECT_EQ(left.disable_gpu_shader_disk_cache,
             right.disable_gpu_shader_disk_cache);
@@ -138,7 +141,8 @@
     GPU_PREFERENCES_FIELD(enable_gpu_driver_debug_logging, true)
     GPU_PREFERENCES_FIELD(disable_gpu_program_cache, true)
     GPU_PREFERENCES_FIELD(enforce_gl_minimums, true)
-    GPU_PREFERENCES_FIELD(force_gpu_mem_available, 4096)
+    GPU_PREFERENCES_FIELD(force_gpu_mem_available_bytes, 4096)
+    GPU_PREFERENCES_FIELD(force_gpu_mem_discardable_limit_bytes, 8092)
     GPU_PREFERENCES_FIELD(gpu_program_cache_size,
                           kDefaultMaxProgramCacheMemoryBytes - 1)
     GPU_PREFERENCES_FIELD(disable_gpu_shader_disk_cache, true)
diff --git a/gpu/gles2_conform_support/egl/context.cc b/gpu/gles2_conform_support/egl/context.cc
index e8e4bf61..2e5c030 100644
--- a/gpu/gles2_conform_support/egl/context.cc
+++ b/gpu/gles2_conform_support/egl/context.cc
@@ -61,6 +61,8 @@
       is_destroyed_(false),
       gpu_driver_bug_workarounds_(
           platform_gpu_feature_info_.enabled_gpu_driver_bug_workarounds),
+      discardable_manager_(gpu::GpuPreferences()),
+      passthrough_discardable_manager_(gpu::GpuPreferences()),
       translator_cache_(gpu::GpuPreferences()) {}
 
 Context::~Context() {
diff --git a/gpu/ipc/command_buffer_task_executor.cc b/gpu/ipc/command_buffer_task_executor.cc
index fb1b239e..b843ee6 100644
--- a/gpu/ipc/command_buffer_task_executor.cc
+++ b/gpu/ipc/command_buffer_task_executor.cc
@@ -29,6 +29,8 @@
       share_group_(share_group),
       share_group_surface_format_(share_group_surface_format),
       program_cache_(program_cache),
+      discardable_manager_(gpu_preferences_),
+      passthrough_discardable_manager_(gpu_preferences_),
       shader_translator_cache_(gpu_preferences_),
       shared_image_manager_(shared_image_manager) {
   DCHECK(mailbox_manager_);
diff --git a/gpu/ipc/common/gpu_preferences.mojom b/gpu/ipc/common/gpu_preferences.mojom
index 305d8a2..22ef86c6 100644
--- a/gpu/ipc/common/gpu_preferences.mojom
+++ b/gpu/ipc/common/gpu_preferences.mojom
@@ -55,7 +55,8 @@
   bool enable_gpu_driver_debug_logging;
   bool disable_gpu_program_cache;
   bool enforce_gl_minimums;
-  uint32 force_gpu_mem_available;
+  uint32 force_gpu_mem_available_bytes;
+  uint32 force_gpu_mem_discardable_limit_bytes;
   uint32 gpu_program_cache_size;
   bool disable_gpu_shader_disk_cache;
   bool enable_threaded_texture_mailboxes;
diff --git a/gpu/ipc/common/gpu_preferences_mojom_traits.h b/gpu/ipc/common/gpu_preferences_mojom_traits.h
index 4844116..a3a2c3a4 100644
--- a/gpu/ipc/common/gpu_preferences_mojom_traits.h
+++ b/gpu/ipc/common/gpu_preferences_mojom_traits.h
@@ -124,7 +124,9 @@
         prefs.enable_gpu_driver_debug_logging();
     out->disable_gpu_program_cache = prefs.disable_gpu_program_cache();
     out->enforce_gl_minimums = prefs.enforce_gl_minimums();
-    out->force_gpu_mem_available = prefs.force_gpu_mem_available();
+    out->force_gpu_mem_available_bytes = prefs.force_gpu_mem_available_bytes();
+    out->force_gpu_mem_discardable_limit_bytes =
+        prefs.force_gpu_mem_discardable_limit_bytes();
     out->gpu_program_cache_size = prefs.gpu_program_cache_size();
     out->disable_gpu_shader_disk_cache = prefs.disable_gpu_shader_disk_cache();
     out->enable_threaded_texture_mailboxes =
@@ -245,8 +247,13 @@
   static bool enforce_gl_minimums(const gpu::GpuPreferences& prefs) {
     return prefs.enforce_gl_minimums;
   }
-  static uint32_t force_gpu_mem_available(const gpu::GpuPreferences& prefs) {
-    return prefs.force_gpu_mem_available;
+  static uint32_t force_gpu_mem_available_bytes(
+      const gpu::GpuPreferences& prefs) {
+    return prefs.force_gpu_mem_available_bytes;
+  }
+  static uint32_t force_gpu_mem_discardable_limit_bytes(
+      const gpu::GpuPreferences& prefs) {
+    return prefs.force_gpu_mem_discardable_limit_bytes;
   }
   static uint32_t gpu_program_cache_size(const gpu::GpuPreferences& prefs) {
     return prefs.gpu_program_cache_size;
diff --git a/gpu/ipc/in_process_gpu_thread_holder.cc b/gpu/ipc/in_process_gpu_thread_holder.cc
index 308243bf..050aa72 100644
--- a/gpu/ipc/in_process_gpu_thread_holder.cc
+++ b/gpu/ipc/in_process_gpu_thread_holder.cc
@@ -99,7 +99,8 @@
   auto feature_info = base::MakeRefCounted<gles2::FeatureInfo>(
       gpu_driver_bug_workarounds, gpu_feature_info_);
   context_state_->InitializeGL(gpu_preferences_, feature_info);
-  context_state_->InitializeGrContext(gpu_driver_bug_workarounds, nullptr);
+  context_state_->InitializeGrContext(gpu_preferences_,
+                                      gpu_driver_bug_workarounds, nullptr);
 
   task_executor_ = std::make_unique<GpuInProcessThreadService>(
       task_runner(), scheduler_.get(), sync_point_manager_.get(),
diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc
index d5ce5a5..1808acd 100644
--- a/gpu/ipc/service/gpu_channel_manager.cc
+++ b/gpu/ipc/service/gpu_channel_manager.cc
@@ -128,6 +128,8 @@
       default_offscreen_surface_(std::move(default_offscreen_surface)),
       gpu_memory_buffer_factory_(gpu_memory_buffer_factory),
       gpu_feature_info_(gpu_feature_info),
+      discardable_manager_(gpu_preferences_),
+      passthrough_discardable_manager_(gpu_preferences_),
       image_decode_accelerator_worker_(image_decode_accelerator_worker),
       activity_flags_(std::move(activity_flags)),
       memory_pressure_listener_(
@@ -516,9 +518,9 @@
         return nullptr;
       }
     }
-    shared_context_state_->InitializeGrContext(gpu_driver_bug_workarounds_,
-                                               gr_shader_cache(),
-                                               &activity_flags_, watchdog_);
+    shared_context_state_->InitializeGrContext(
+        gpu_preferences_, gpu_driver_bug_workarounds_, gr_shader_cache(),
+        &activity_flags_, watchdog_);
   }
 
   gr_cache_controller_.emplace(shared_context_state_.get(), task_runner_);
diff --git a/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc b/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
index b190cfc..af206760 100644
--- a/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
+++ b/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
@@ -233,8 +233,8 @@
         channel_manager()->GetSharedContextState(&context_result);
     ASSERT_EQ(ContextResult::kSuccess, context_result);
     ASSERT_TRUE(shared_context_state);
-    shared_context_state->InitializeGrContext(GpuDriverBugWorkarounds(),
-                                              nullptr);
+    shared_context_state->InitializeGrContext(
+        GpuPreferences(), GpuDriverBugWorkarounds(), nullptr);
 
     GpuChannel* channel = CreateChannel(kChannelId, false /* is_gpu_host */);
     ASSERT_TRUE(channel);
diff --git a/ios/chrome/browser/overlays/public/infobar_banner/BUILD.gn b/ios/chrome/browser/overlays/public/infobar_banner/BUILD.gn
index 535e9f31..0c129f5 100644
--- a/ios/chrome/browser/overlays/public/infobar_banner/BUILD.gn
+++ b/ios/chrome/browser/overlays/public/infobar_banner/BUILD.gn
@@ -4,8 +4,6 @@
 
 source_set("infobar_banner") {
   sources = [
-    "infobar_banner_overlay_responses.cc",
-    "infobar_banner_overlay_responses.h",
     "save_password_infobar_banner_overlay.h",
     "save_password_infobar_banner_overlay.mm",
   ]
diff --git a/ios/chrome/browser/overlays/public/infobar_banner/infobar_banner_overlay_responses.cc b/ios/chrome/browser/overlays/public/infobar_banner/infobar_banner_overlay_responses.cc
deleted file mode 100644
index 75603b2..0000000
--- a/ios/chrome/browser/overlays/public/infobar_banner/infobar_banner_overlay_responses.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/overlays/public/infobar_banner/infobar_banner_overlay_responses.h"
-
-#pragma mark - InfobarBannerMainActionResponse
-
-OVERLAY_USER_DATA_SETUP_IMPL(InfobarBannerMainActionResponse);
-
-InfobarBannerMainActionResponse::InfobarBannerMainActionResponse() = default;
-
-InfobarBannerMainActionResponse::~InfobarBannerMainActionResponse() = default;
-
-#pragma mark - InfobarBannerShowModalResponse
-
-OVERLAY_USER_DATA_SETUP_IMPL(InfobarBannerShowModalResponse);
-
-InfobarBannerShowModalResponse::InfobarBannerShowModalResponse() = default;
-
-InfobarBannerShowModalResponse::~InfobarBannerShowModalResponse() = default;
-
-#pragma mark - InfobarBannerCompletionResponse
-
-OVERLAY_USER_DATA_SETUP_IMPL(InfobarBannerCompletionResponse);
-
-InfobarBannerCompletionResponse::InfobarBannerCompletionResponse(
-    bool user_initiated)
-    : user_initiated_(user_initiated) {}
-
-InfobarBannerCompletionResponse::~InfobarBannerCompletionResponse() = default;
diff --git a/ios/chrome/browser/overlays/public/infobar_banner/infobar_banner_overlay_responses.h b/ios/chrome/browser/overlays/public/infobar_banner/infobar_banner_overlay_responses.h
deleted file mode 100644
index 92f60c9..0000000
--- a/ios/chrome/browser/overlays/public/infobar_banner/infobar_banner_overlay_responses.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_BANNER_INFOBAR_BANNER_OVERLAY_RESPONSES_H_
-#define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_BANNER_INFOBAR_BANNER_OVERLAY_RESPONSES_H_
-
-#include "ios/chrome/browser/overlays/public/overlay_user_data.h"
-
-// Response info used to create dispatched OverlayResponses that trigger the
-// infobar's main action.
-class InfobarBannerMainActionResponse
-    : public OverlayUserData<InfobarBannerMainActionResponse> {
- public:
-  ~InfobarBannerMainActionResponse() override;
-
- private:
-  OVERLAY_USER_DATA_SETUP(InfobarBannerMainActionResponse);
-  InfobarBannerMainActionResponse();
-};
-
-// Response info used to create dispatched OverlayResponses that trigger the
-// presentation of the infobar's modal.
-class InfobarBannerShowModalResponse
-    : public OverlayUserData<InfobarBannerShowModalResponse> {
- public:
-  ~InfobarBannerShowModalResponse() override;
-
- private:
-  OVERLAY_USER_DATA_SETUP(InfobarBannerShowModalResponse);
-  InfobarBannerShowModalResponse();
-};
-
-// Response info used to create completion OverlayResponses for an infobar
-// banner OverlayRequest.  Executed when the banner is dismissed by the user or
-// the request is cancelled.
-class InfobarBannerCompletionResponse
-    : public OverlayUserData<InfobarBannerCompletionResponse> {
- public:
-  ~InfobarBannerCompletionResponse() override;
-
-  // Whether the banner dismissal was user-initiated.
-  bool user_initiated() const { return user_initiated_; }
-
- private:
-  OVERLAY_USER_DATA_SETUP(InfobarBannerCompletionResponse);
-  explicit InfobarBannerCompletionResponse(bool user_initiated);
-
-  bool user_initiated_ = false;
-};
-
-#endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_BANNER_INFOBAR_BANNER_OVERLAY_RESPONSES_H_
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
index e60c4b7f..bd94d1bd 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -301,12 +301,14 @@
   self.formInputAccessoryCoordinator.navigator = self;
   [self.formInputAccessoryCoordinator start];
 
+  self.snackbarCoordinator = [[SnackbarCoordinator alloc]
+      initWithBaseViewController:self.viewController];
+  self.snackbarCoordinator.dispatcher = self.dispatcher;
+  [self.snackbarCoordinator start];
+
   self.translateInfobarCoordinator = [[LegacyTranslateInfobarCoordinator alloc]
       initWithBaseViewController:self.viewController
-                    browserState:self.browserState
-                    webStateList:self.browser->GetWebStateList()
-                      dispatcher:static_cast<id<SnackbarCommands>>(
-                                     self.dispatcher)];
+                         browser:self.browser];
   [self.translateInfobarCoordinator start];
 
   self.pageInfoCoordinator = [[PageInfoLegacyCoordinator alloc]
@@ -334,11 +336,6 @@
 
   /* RepostFormCoordinator is created and started by a delegate method */
 
-  self.snackbarCoordinator = [[SnackbarCoordinator alloc]
-      initWithBaseViewController:self.viewController];
-  self.snackbarCoordinator.dispatcher = self.dispatcher;
-  [self.snackbarCoordinator start];
-
   self.storeKitCoordinator = [[StoreKitCoordinator alloc]
       initWithBaseViewController:self.viewController];
 
diff --git a/ios/chrome/browser/ui/infobars/banners/infobar_banner_consumer.h b/ios/chrome/browser/ui/infobars/banners/infobar_banner_consumer.h
index 601ad67..6042ec1 100644
--- a/ios/chrome/browser/ui/infobars/banners/infobar_banner_consumer.h
+++ b/ios/chrome/browser/ui/infobars/banners/infobar_banner_consumer.h
@@ -5,7 +5,7 @@
 #ifndef IOS_CHROME_BROWSER_UI_INFOBARS_BANNERS_INFOBAR_BANNER_CONSUMER_H_
 #define IOS_CHROME_BROWSER_UI_INFOBARS_BANNERS_INFOBAR_BANNER_CONSUMER_H_
 
-#import <UIKit/UIKit.h>
+#import <Foundation/Foundation.h>
 
 @protocol InfobarBannerConsumer <NSObject>
 
diff --git a/ios/chrome/browser/ui/infobars/banners/infobar_banner_delegate.h b/ios/chrome/browser/ui/infobars/banners/infobar_banner_delegate.h
index bddedbc..33891e9 100644
--- a/ios/chrome/browser/ui/infobars/banners/infobar_banner_delegate.h
+++ b/ios/chrome/browser/ui/infobars/banners/infobar_banner_delegate.h
@@ -5,7 +5,7 @@
 #ifndef IOS_CHROME_BROWSER_UI_INFOBARS_BANNERS_INFOBAR_BANNER_DELEGATE_H_
 #define IOS_CHROME_BROWSER_UI_INFOBARS_BANNERS_INFOBAR_BANNER_DELEGATE_H_
 
-#import <UIKit/UIKit.h>
+#import <Foundation/Foundation.h>
 
 // Delegate to handle InfobarBanner actions.
 @protocol InfobarBannerDelegate
diff --git a/ios/chrome/browser/ui/infobars/banners/test/BUILD.gn b/ios/chrome/browser/ui/infobars/banners/test/BUILD.gn
deleted file mode 100644
index dd71e84..0000000
--- a/ios/chrome/browser/ui/infobars/banners/test/BUILD.gn
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("test") {
-  testonly = true
-  sources = [
-    "fake_infobar_banner_consumer.h",
-    "fake_infobar_banner_consumer.mm",
-  ]
-
-  configs += [ "//build/config/compiler:enable_arc" ]
-
-  deps = [
-    "//base",
-    "//ios/chrome/browser/ui/infobars/banners",
-  ]
-}
diff --git a/ios/chrome/browser/ui/infobars/banners/test/fake_infobar_banner_consumer.h b/ios/chrome/browser/ui/infobars/banners/test/fake_infobar_banner_consumer.h
deleted file mode 100644
index 2c4421e..0000000
--- a/ios/chrome/browser/ui/infobars/banners/test/fake_infobar_banner_consumer.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_INFOBARS_BANNERS_TEST_FAKE_INFOBAR_BANNER_CONSUMER_H_
-#define IOS_CHROME_BROWSER_UI_INFOBARS_BANNERS_TEST_FAKE_INFOBAR_BANNER_CONSUMER_H_
-
-#import "ios/chrome/browser/ui/infobars/banners/infobar_banner_consumer.h"
-
-// Fake InfobarBannerConsumer used in tests.
-@interface FakeInfobarBannerConsumer : NSObject <InfobarBannerConsumer>
-// Redefine InfobarBannerConsumer properties as readwrite.
-@property(nonatomic, copy) NSString* bannerAccessibilityLabel;
-@property(nonatomic, copy) NSString* buttonText;
-@property(nonatomic, strong) UIImage* iconImage;
-@property(nonatomic, assign) BOOL presentsModal;
-@property(nonatomic, copy) NSString* titleText;
-@property(nonatomic, copy) NSString* subtitleText;
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_INFOBARS_BANNERS_TEST_FAKE_INFOBAR_BANNER_CONSUMER_H_
diff --git a/ios/chrome/browser/ui/infobars/banners/test/fake_infobar_banner_consumer.mm b/ios/chrome/browser/ui/infobars/banners/test/fake_infobar_banner_consumer.mm
deleted file mode 100644
index 4fe97c0b..0000000
--- a/ios/chrome/browser/ui/infobars/banners/test/fake_infobar_banner_consumer.mm
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/infobars/banners/test/fake_infobar_banner_consumer.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@implementation FakeInfobarBannerConsumer
-@end
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
index 57bb5d9..a00587aa 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
@@ -144,13 +144,8 @@
 
 // Tests that the XClientData header is sent when navigating to
 // https://google.com through the omnibox.
-- (void)testXClientData {
-#if !TARGET_IPHONE_SIMULATOR
-  // TODO(crbug.com/1036225): Test flaky on iOS 12 device.
-  if (!base::ios::IsRunningOnIOS13OrLater()) {
-    EARL_GREY_TEST_DISABLED(@"Flaky on iOS 12 device.");
-  }
-#endif
+// TODO(crbug.com/1036225): Test very flaky on all config.
+- (void)DISABLED_testXClientData {
   // Rewrite the google URL to localhost URL.
   [OmniboxAppInterface rewriteGoogleURLToLocalhost];
 
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/BUILD.gn b/ios/chrome/browser/ui/overlays/infobar_banner/BUILD.gn
index f2656eb7..6611898 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/BUILD.gn
@@ -14,52 +14,3 @@
 
   deps = []
 }
-
-source_set("common") {
-  sources = [
-    "infobar_banner_overlay_coordinator.h",
-    "infobar_banner_overlay_coordinator.mm",
-    "infobar_banner_overlay_mediator+consumer_support.h",
-    "infobar_banner_overlay_mediator.h",
-    "infobar_banner_overlay_mediator.mm",
-  ]
-
-  configs += [ "//build/config/compiler:enable_arc" ]
-
-  deps = [
-    "//base",
-    "//components/infobars/core",
-    "//ios/chrome/browser/overlays",
-    "//ios/chrome/browser/overlays/public/common/infobars",
-    "//ios/chrome/browser/overlays/public/infobar_banner",
-    "//ios/chrome/browser/ui/infobars/banners",
-    "//ios/chrome/browser/ui/infobars/banners:public",
-    "//ios/chrome/browser/ui/overlays:coordinators",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "infobar_banner_overlay_mediator_unittest.mm",
-  ]
-
-  configs += [ "//build/config/compiler:enable_arc" ]
-
-  deps = [
-    ":common",
-    "//components/infobars/core",
-    "//ios/chrome/browser/infobars/test",
-    "//ios/chrome/browser/overlays",
-    "//ios/chrome/browser/overlays/public/common/infobars",
-    "//ios/chrome/browser/overlays/public/infobar_banner",
-    "//ios/chrome/browser/ui/elements",
-    "//ios/chrome/browser/ui/infobars/banners",
-    "//ios/chrome/browser/ui/infobars/banners:public",
-    "//ios/chrome/browser/ui/infobars/banners/test",
-    "//ios/chrome/browser/ui/overlays/test",
-    "//testing/gtest",
-    "//third_party/ocmock",
-    "//ui/base",
-  ]
-}
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.h b/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.h
deleted file mode 100644
index 3e3add8..0000000
--- a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_INFOBAR_BANNER_INFOBAR_BANNER_OVERLAY_COORDINATOR_H_
-#define IOS_CHROME_BROWSER_UI_OVERLAYS_INFOBAR_BANNER_INFOBAR_BANNER_OVERLAY_COORDINATOR_H_
-
-#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator.h"
-
-// A coordinator that displays infobar banner UI using OverlayPresenter.
-@interface InfobarBannerOverlayCoordinator : OverlayRequestCoordinator
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_INFOBAR_BANNER_INFOBAR_BANNER_OVERLAY_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.mm b/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.mm
deleted file mode 100644
index 007400de..0000000
--- a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.mm
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.h"
-
-#include "base/logging.h"
-#include "base/mac/foundation_util.h"
-#include "base/no_destructor.h"
-#include "ios/chrome/browser/overlays/public/aggregate_overlay_request_support.h"
-#import "ios/chrome/browser/overlays/public/common/infobars/infobar_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/overlay_request.h"
-#import "ios/chrome/browser/overlays/public/overlay_response.h"
-#import "ios/chrome/browser/ui/infobars/banners/infobar_banner_accessibility_util.h"
-#import "ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.h"
-#import "ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator.h"
-#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator+subclassing.h"
-#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator_delegate.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-// Creates a vector containing the OverlayRequestSupports for each
-// OverlayRequestMediator class in |mediator_classes|
-const std::vector<const OverlayRequestSupport*> GetSupportsForMediatorClasses(
-    NSArray<Class>* mediator_classes) {
-  std::vector<const OverlayRequestSupport*> supports(mediator_classes.count);
-  for (NSUInteger index = 0; index < mediator_classes.count; ++index) {
-    supports[index] = [mediator_classes[index] requestSupport];
-  }
-  return supports;
-}
-}  // namespace
-
-@interface InfobarBannerOverlayCoordinator ()
-// The list of supported mediator classes.
-@property(class, nonatomic, readonly) NSArray<Class>* supportedMediatorClasses;
-// The banner view being managed by this coordinator.
-@property(nonatomic) InfobarBannerViewController* bannerViewController;
-@end
-
-@implementation InfobarBannerOverlayCoordinator
-
-#pragma mark - Accessors
-
-+ (NSArray<Class>*)supportedMediatorClasses {
-  // TODO(crbug.com/1030357): Add more mediator classes here.
-  return @[];
-}
-
-+ (const OverlayRequestSupport*)requestSupport {
-  static base::NoDestructor<AggregateOverlayRequestSupport> kSupport(
-      GetSupportsForMediatorClasses(self.supportedMediatorClasses));
-  return kSupport.get();
-}
-
-#pragma mark - OverlayRequestCoordinator
-
-- (void)startAnimated:(BOOL)animated {
-  if (self.started || !self.request)
-    return;
-  // Create the mediator and use it aas the delegate for the banner view.
-  InfobarOverlayRequestConfig* config =
-      self.request->GetConfig<InfobarOverlayRequestConfig>();
-  InfobarBannerOverlayMediator* mediator = [self newMediator];
-  self.bannerViewController = [[InfobarBannerViewController alloc]
-      initWithDelegate:mediator
-         presentsModal:config->has_badge()
-                  type:config->infobar_type()];
-  mediator.consumer = self.bannerViewController;
-  self.mediator = mediator;
-  // Present the banner.
-  // TODO(crbug.com/1030357): Use custom presentation.
-  self.bannerViewController.modalPresentationStyle =
-      UIModalPresentationOverCurrentContext;
-  self.bannerViewController.modalTransitionStyle =
-      UIModalTransitionStyleCrossDissolve;
-  [self.baseViewController presentViewController:self.viewController
-                                        animated:animated
-                                      completion:^{
-                                        [self finishPresentation];
-                                      }];
-  self.started = YES;
-}
-
-- (void)stopAnimated:(BOOL)animated {
-  if (!self.started)
-    return;
-  [self.baseViewController dismissViewControllerAnimated:animated
-                                              completion:^{
-                                                [self finishDismissal];
-                                              }];
-  self.started = NO;
-}
-
-- (UIViewController*)viewController {
-  return self.bannerViewController;
-}
-
-#pragma mark - Private
-
-// Called when the presentation of the banner UI is completed.
-- (void)finishPresentation {
-  // Notify the presentation context that the presentation has finished.  This
-  // is necessary to synchronize OverlayPresenter scheduling logic with the UI
-  // layer.
-  self.delegate->OverlayUIDidFinishPresentation(self.request);
-  UpdateBannerAccessibilityForPresentation(self.baseViewController,
-                                           self.viewController.view);
-}
-
-// Called when the dismissal of the banner UI is finished.
-- (void)finishDismissal {
-  self.bannerViewController = nil;
-  self.mediator = nil;
-  // Notify the presentation context that the dismissal has finished.  This
-  // is necessary to synchronize OverlayPresenter scheduling logic with the UI
-  // layer.
-  self.delegate->OverlayUIDidFinishDismissal(self.request);
-  UpdateBannerAccessibilityForDismissal(self.baseViewController);
-}
-
-// Creates a mediator instance from the supported mediator class list that
-// supports the coordinator's request.
-- (InfobarBannerOverlayMediator*)newMediator {
-  for (Class mediatorClass in [self class].supportedMediatorClasses) {
-    if (mediatorClass.requestSupport->IsRequestSupported(self.request))
-      return [[mediatorClass alloc] initWithRequest:self.request];
-  }
-  NOTREACHED() << "None of the supported mediator classes support request.";
-  return nil;
-}
-
-@end
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator+consumer_support.h b/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator+consumer_support.h
deleted file mode 100644
index 6561216..0000000
--- a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator+consumer_support.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_INFOBAR_BANNER_INFOBAR_BANNER_OVERLAY_MEDIATOR_CONSUMER_SUPPORT_H_
-#define IOS_CHROME_BROWSER_UI_OVERLAYS_INFOBAR_BANNER_INFOBAR_BANNER_OVERLAY_MEDIATOR_CONSUMER_SUPPORT_H_
-
-#import "ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator.h"
-
-// Category used by the InfobarBannerOverlayMediator superclass in order to
-// configure its consumer.
-@interface InfobarBannerOverlayMediator (ConsumerSupport)
-
-// Sets up the banner consumer using the configuration information from the
-// mediator's OverlayRequest.  Subclasses must implement.
-- (void)configureConsumer;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_INFOBAR_BANNER_INFOBAR_BANNER_OVERLAY_MEDIATOR_CONSUMER_SUPPORT_H_
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator.h b/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator.h
deleted file mode 100644
index db2d895..0000000
--- a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_INFOBAR_BANNER_INFOBAR_BANNER_OVERLAY_MEDIATOR_H_
-#define IOS_CHROME_BROWSER_UI_OVERLAYS_INFOBAR_BANNER_INFOBAR_BANNER_OVERLAY_MEDIATOR_H_
-
-#import "ios/chrome/browser/ui/infobars/banners/infobar_banner_delegate.h"
-#import "ios/chrome/browser/ui/overlays/overlay_request_mediator.h"
-
-@protocol InfobarBannerConsumer;
-
-// Mediator superclass for configuring InfobarBannerConsumers.
-@interface InfobarBannerOverlayMediator
-    : OverlayRequestMediator <InfobarBannerDelegate>
-
-// The consumer to be updated by this mediator.  Setting to a new value updates
-// the new consumer.
-@property(nonatomic, weak) id<InfobarBannerConsumer> consumer;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_INFOBAR_BANNER_INFOBAR_BANNER_OVERLAY_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator.mm b/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator.mm
deleted file mode 100644
index fe4090f2..0000000
--- a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator.mm
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator.h"
-
-#import <UIKit/UIKit.h>
-
-#include "base/logging.h"
-#import "base/strings/sys_string_conversions.h"
-#import "ios/chrome/browser/overlays/public/common/infobars/infobar_banner_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/common/infobars/infobar_overlay_request_config.h"
-#include "ios/chrome/browser/overlays/public/infobar_banner/infobar_banner_overlay_responses.h"
-#include "ios/chrome/browser/overlays/public/overlay_callback_manager.h"
-#include "ios/chrome/browser/overlays/public/overlay_request.h"
-#include "ios/chrome/browser/overlays/public/overlay_response.h"
-#import "ios/chrome/browser/ui/infobars/banners/infobar_banner_consumer.h"
-#import "ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator+consumer_support.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@interface InfobarBannerOverlayMediator ()
-// The banner config from the request.
-@property(nonatomic, readonly) InfobarBannerOverlayRequestConfig* config;
-@end
-
-@implementation InfobarBannerOverlayMediator
-
-- (instancetype)initWithRequest:(OverlayRequest*)request {
-  if (self = [super initWithRequest:request]) {
-    DCHECK(request->GetConfig<InfobarBannerOverlayRequestConfig>());
-  }
-  return self;
-}
-
-#pragma mark - Accessors
-
-- (void)setConsumer:(id<InfobarBannerConsumer>)consumer {
-  if (_consumer == consumer)
-    return;
-  _consumer = consumer;
-  if (_consumer)
-    [self configureConsumer];
-}
-
-- (InfobarBannerOverlayRequestConfig*)config {
-  return self.request
-             ? self.request->GetConfig<InfobarBannerOverlayRequestConfig>()
-             : nullptr;
-}
-
-#pragma mark - InfobarBannerDelegate
-
-- (void)bannerInfobarButtonWasPressed:(UIButton*)sender {
-  // Notify the model layer to perform the infobar's main action before
-  // dismissing the banner.
-  [self dispatchResponseAndStopOverlay:OverlayResponse::CreateWithInfo<
-                                           InfobarBannerMainActionResponse>()];
-}
-
-- (void)dismissInfobarBannerForUserInteraction:(BOOL)userInitiated {
-  if (self.request) {
-    // Add a completion response notifying the requesting code of whether the
-    // dismissal was user-initiated.  Provided to the request's completion
-    // callbacks that are executed when the UI is finished being dismissed.
-    self.request->GetCallbackManager()->SetCompletionResponse(
-        OverlayResponse::CreateWithInfo<InfobarBannerCompletionResponse>(
-            userInitiated));
-  }
-  [self.delegate stopOverlayForMediator:self];
-}
-
-- (void)presentInfobarModalFromBanner {
-  // Notify the model layer to show the infobar modal before dismissing the
-  // banner.
-  [self dispatchResponseAndStopOverlay:OverlayResponse::CreateWithInfo<
-                                           InfobarBannerShowModalResponse>()];
-}
-
-- (void)infobarBannerWasDismissed {
-  // Only needed in legacy implementation.  Dismissal completion cleanup occurs
-  // in InfobarBannerOverlayCoordinator.
-}
-
-#pragma mark - Private
-
-// Dispatches |response| through the OverlayRequest, then stops the overlay UI.
-- (void)dispatchResponseAndStopOverlay:
-    (std::unique_ptr<OverlayResponse>)response {
-  if (self.request)
-    self.request->GetCallbackManager()->DispatchResponse(std::move(response));
-  [self.delegate stopOverlayForMediator:self];
-}
-
-@end
-
-@implementation InfobarBannerOverlayMediator (ConsumerSupport)
-
-- (void)configureConsumer {
-  // TODO(crbug.com/1030357): Add NOTREACHED() here to enforce that subclasses
-  // implement.
-  InfobarBannerOverlayRequestConfig* config = self.config;
-  if (!config)
-    return;
-  [self.consumer
-      setBannerAccessibilityLabel:config->banner_accessibility_label()];
-  [self.consumer setButtonText:config->button_text()];
-  [self.consumer setIconImage:[UIImage imageNamed:config->icon_image_name()]];
-  [self.consumer setPresentsModal:config->presents_modal()];
-  [self.consumer setTitleText:config->title_text()];
-  [self.consumer setSubtitleText:config->subtitle_text()];
-}
-
-@end
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator_unittest.mm
deleted file mode 100644
index 4791ddc..0000000
--- a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator_unittest.mm
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator.h"
-
-#import "base/bind.h"
-#include "base/strings/sys_string_conversions.h"
-#import "ios/chrome/browser/overlays/public/common/infobars/infobar_banner_overlay_request_config.h"
-#include "ios/chrome/browser/overlays/public/infobar_banner/infobar_banner_overlay_responses.h"
-#include "ios/chrome/browser/overlays/public/overlay_callback_manager.h"
-#include "ios/chrome/browser/overlays/public/overlay_request.h"
-#include "ios/chrome/browser/overlays/public/overlay_response.h"
-#import "ios/chrome/browser/ui/infobars/banners/test/fake_infobar_banner_consumer.h"
-#import "testing/gtest_mac.h"
-#include "testing/platform_test.h"
-#import "third_party/ocmock/OCMock/OCMock.h"
-#import "third_party/ocmock/gtest_support.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-NSString* const kBannerAccessibilityLabel = @"a11y label";
-NSString* const kButtonText = @"button text";
-NSString* const kIconImageName = @"";
-const bool kPresentsModal(false);
-NSString* const kTitleText = @"title text";
-NSString* const kSubtitleText = @"subtitle text";
-}
-
-// Test fixture for InfobarBannerOverlayMediator.
-class InfobarBannerOverlayMediatorTest : public PlatformTest {
- public:
-  InfobarBannerOverlayMediatorTest()
-      : request_(
-            OverlayRequest::CreateWithConfig<InfobarBannerOverlayRequestConfig>(
-                kBannerAccessibilityLabel,
-                kButtonText,
-                kIconImageName,
-                kPresentsModal,
-                kTitleText,
-                kSubtitleText)),
-        delegate_(
-            OCMStrictProtocolMock(@protocol(OverlayRequestMediatorDelegate))),
-        mediator_([[InfobarBannerOverlayMediator alloc]
-            initWithRequest:request_.get()]) {
-    mediator_.delegate = delegate_;
-  }
-  ~InfobarBannerOverlayMediatorTest() override {
-    EXPECT_OCMOCK_VERIFY(delegate_);
-  }
-
- protected:
-  std::unique_ptr<OverlayRequest> request_;
-  id<OverlayRequestMediatorDelegate> delegate_ = nil;
-  InfobarBannerOverlayMediator* mediator_ = nil;
-};
-
-// Tests that an InfobarBannerOverlayMediator correctly sets up its consumer.
-TEST_F(InfobarBannerOverlayMediatorTest, SetUpConsumer) {
-  FakeInfobarBannerConsumer* consumer =
-      [[FakeInfobarBannerConsumer alloc] init];
-  mediator_.consumer = consumer;
-  EXPECT_NSEQ(kBannerAccessibilityLabel, consumer.bannerAccessibilityLabel);
-  EXPECT_NSEQ(kButtonText, consumer.buttonText);
-  EXPECT_NSEQ(nil, consumer.iconImage);
-  EXPECT_EQ(kPresentsModal, consumer.presentsModal);
-  EXPECT_NSEQ(kTitleText, consumer.titleText);
-  EXPECT_NSEQ(kSubtitleText, consumer.subtitleText);
-}
-
-// Tests that an InfobarBannerOverlayMediator correctly dispatches a response
-// for confirm button taps before stopping itself.
-TEST_F(InfobarBannerOverlayMediatorTest, ConfirmButtonTapped) {
-  __block bool confirm_button_tapped = false;
-  void (^confirm_button_tapped_callback)(OverlayResponse* response) =
-      ^(OverlayResponse* response) {
-        confirm_button_tapped = true;
-      };
-  request_->GetCallbackManager()
-      ->AddDispatchCallback<InfobarBannerMainActionResponse>(
-          base::BindRepeating(confirm_button_tapped_callback));
-  ASSERT_FALSE(confirm_button_tapped);
-
-  // Notify the mediator of the button tap via its InfobarBannerDelegate
-  // implementation and verify that the confirm button callback was executed and
-  // that the mediator's delegate was instructed to stop.
-  OCMExpect([delegate_ stopOverlayForMediator:mediator_]);
-  [mediator_ bannerInfobarButtonWasPressed:nil];
-  EXPECT_TRUE(confirm_button_tapped);
-}
-
-// Tests that an InfobarBannerOverlayMediator correctly dispatches a response
-// for modal button taps before stopping itself.
-TEST_F(InfobarBannerOverlayMediatorTest, ModalButtonTapped) {
-  __block bool modal_button_tapped = false;
-  void (^modal_button_tapped_callback)(OverlayResponse* response) =
-      ^(OverlayResponse* response) {
-        modal_button_tapped = true;
-      };
-  request_->GetCallbackManager()
-      ->AddDispatchCallback<InfobarBannerShowModalResponse>(
-          base::BindRepeating(modal_button_tapped_callback));
-  ASSERT_FALSE(modal_button_tapped);
-
-  // Notify the mediator of the button tap via its InfobarBannerDelegate
-  // implementation and verify that the modal button callback was executed and
-  // that the mediator's delegate was instructed to stop.
-  OCMExpect([delegate_ stopOverlayForMediator:mediator_]);
-  [mediator_ presentInfobarModalFromBanner];
-  EXPECT_TRUE(modal_button_tapped);
-}
-
-// Tests that an InfobarBannerOverlayMediator correctly sets the completion
-// response for user-initiated dismissals triggered by the banner UI.
-TEST_F(InfobarBannerOverlayMediatorTest, UserInitiatedDismissal) {
-  __block bool user_initiated = false;
-  void (^completion_callback)(OverlayResponse* response) =
-      ^(OverlayResponse* response) {
-        user_initiated = true;
-      };
-  request_->GetCallbackManager()->AddCompletionCallback(
-      base::BindOnce(completion_callback));
-  ASSERT_FALSE(user_initiated);
-
-  // Notify the mediator of the dismissal via its InfobarBannerDelegate
-  // implementation and verify that the completion callback was executed with
-  // the correct info and that the mediator's delegate was instructed to stop.
-  OCMExpect([delegate_ stopOverlayForMediator:mediator_]);
-  [mediator_ dismissInfobarBannerForUserInteraction:YES];
-  request_ = nullptr;
-  EXPECT_TRUE(user_initiated);
-}
diff --git a/ios/chrome/browser/ui/translate/BUILD.gn b/ios/chrome/browser/ui/translate/BUILD.gn
index 7227d28d..bb3ed286 100644
--- a/ios/chrome/browser/ui/translate/BUILD.gn
+++ b/ios/chrome/browser/ui/translate/BUILD.gn
@@ -17,7 +17,10 @@
     "//components/strings:components_strings_grit",
     "//components/translate/core/browser",
     "//ios/chrome/browser",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/main",
     "//ios/chrome/browser/translate",
+    "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/list_model",
     "//ios/chrome/browser/ui/popup_menu/public:popup_menu_ui",
diff --git a/ios/chrome/browser/ui/translate/legacy_translate_infobar_coordinator.h b/ios/chrome/browser/ui/translate/legacy_translate_infobar_coordinator.h
index 8bf83fc..e48a7b6 100644
--- a/ios/chrome/browser/ui/translate/legacy_translate_infobar_coordinator.h
+++ b/ios/chrome/browser/ui/translate/legacy_translate_infobar_coordinator.h
@@ -9,23 +9,16 @@
 
 #import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
 
-@protocol SnackbarCommands;
-class WebStateList;
-
 // Coordinator responsible for presenting and dismissing the translate infobar's
 // language selection popup menu, translate options popup menu, and translate
 // options notifications.
 @interface LegacyTranslateInfobarCoordinator : ChromeCoordinator
 
-// Creates a coordinator that uses |viewController|, |browserState|, and
-// |webStateList|.
+// Unavailable, use -initWithBaseViewController:browser:.
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
-                              browserState:
-                                  (ios::ChromeBrowserState*)browserState
-                              webStateList:(WebStateList*)webStateList
-                                dispatcher:(id<SnackbarCommands>)dispatcher;
+    NS_UNAVAILABLE;
 
-// Unavailable, use -initWithBaseViewController:browserState:webStateList:.
+// Unavailable, use -initWithBaseViewController:browser:.
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                               browserState:
                                   (ios::ChromeBrowserState*)browserState
diff --git a/ios/chrome/browser/ui/translate/legacy_translate_infobar_coordinator.mm b/ios/chrome/browser/ui/translate/legacy_translate_infobar_coordinator.mm
index 6017cc8..11a192c 100644
--- a/ios/chrome/browser/ui/translate/legacy_translate_infobar_coordinator.mm
+++ b/ios/chrome/browser/ui/translate/legacy_translate_infobar_coordinator.mm
@@ -7,11 +7,14 @@
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/translate/language_selection_context.h"
 #import "ios/chrome/browser/translate/language_selection_delegate.h"
 #import "ios/chrome/browser/translate/language_selection_handler.h"
 #import "ios/chrome/browser/translate/translate_option_selection_delegate.h"
 #import "ios/chrome/browser/translate/translate_option_selection_handler.h"
+#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
+#import "ios/chrome/browser/ui/commands/snackbar_commands.h"
 #import "ios/chrome/browser/ui/list_model/list_model.h"
 #import "ios/chrome/browser/ui/popup_menu/public/popup_menu_presenter.h"
 #import "ios/chrome/browser/ui/popup_menu/public/popup_menu_presenter_delegate.h"
@@ -34,8 +37,6 @@
     PopupMenuTableViewControllerDelegate,
     TranslateOptionSelectionHandler>
 
-// The WebStateList this coordinator observes.
-@property(nonatomic, assign) WebStateList* webStateList;
 // Presenter for the popup menu, managing the animations.
 @property(nonatomic, strong) PopupMenuPresenter* popupMenuPresenter;
 // Mediator for the popup menu.
@@ -53,41 +54,27 @@
     translateOptionSelectionDelegate;
 // YES if the coordinator has been started.
 @property(nonatomic) BOOL started;
-// The dispatcher used by this Coordinator.
-@property(nonatomic, weak) id<SnackbarCommands> dispatcher;
 
 @end
 
 @implementation LegacyTranslateInfobarCoordinator
 
-- (instancetype)initWithBaseViewController:(UIViewController*)viewController
-                              browserState:
-                                  (ios::ChromeBrowserState*)browserState
-                              webStateList:(WebStateList*)webStateList
-                                dispatcher:(id<SnackbarCommands>)dispatcher {
-  DCHECK(webStateList);
-  self = [super initWithBaseViewController:viewController
-                              browserState:browserState];
-  if (self) {
-    _webStateList = webStateList;
-    _dispatcher = dispatcher;
-  }
-  return self;
-}
-
 #pragma mark - ChromeCoordinator
 
 - (void)start {
   if (self.started)
     return;
 
-  self.notificationPresenter = [[TranslateNotificationPresenter alloc]
-      initWithDispatcher:self.dispatcher];
+  CommandDispatcher* dispatcher = self.browser->GetCommandDispatcher();
+  id<SnackbarCommands> handler =
+      HandlerForProtocol(dispatcher, SnackbarCommands);
+  self.notificationPresenter =
+      [[TranslateNotificationPresenter alloc] initWithDispatcher:handler];
 
   self.mediator = [[LegacyTranslateInfobarMediator alloc]
       initWithSelectionHandler:self
            notificationHandler:self.notificationPresenter];
-  self.mediator.webStateList = self.webStateList;
+  self.mediator.webStateList = self.browser->GetWebStateList();
 
   self.started = YES;
 }
@@ -100,7 +87,6 @@
   [self.mediator disconnect];
   self.mediator = nil;
   self.notificationPresenter = nil;
-  self.webStateList = nullptr;
   self.popupMenuPresenter = nil;
   self.viewController = nil;
   self.languageSelectionDelegate = nil;
diff --git a/ios/chrome/browser/web/font_size_js_unittest.mm b/ios/chrome/browser/web/font_size_js_unittest.mm
index f1d29937e..1fa90aa9 100644
--- a/ios/chrome/browser/web/font_size_js_unittest.mm
+++ b/ios/chrome/browser/web/font_size_js_unittest.mm
@@ -252,7 +252,8 @@
 }
 
 // Tests that __gCrWeb.accessibility.adjustFontSize works for nested elements.
-TEST_F(FontSizeJsTest, TestAdjustFontSizeForNestedElements) {
+// TODO(crbug.com/1038529): Re-enable this test.
+TEST_F(FontSizeJsTest, FLAKY_TestAdjustFontSizeForNestedElements) {
   // TODO(crbug.com/983776): This test fails on ipad since beta5 due to a
   // simulator bug. Re-enable this once the bug is fixed.
   if (base::ios::IsRunningOnIOS13OrLater() &&
diff --git a/ios/chrome/browser/web/web_state_delegate_tab_helper_unittest.mm b/ios/chrome/browser/web/web_state_delegate_tab_helper_unittest.mm
index 5c85ec0a..1800414 100644
--- a/ios/chrome/browser/web/web_state_delegate_tab_helper_unittest.mm
+++ b/ios/chrome/browser/web/web_state_delegate_tab_helper_unittest.mm
@@ -66,7 +66,8 @@
 
 // Tests that GetJavaScriptDialogPresenter() returns an overlay-based JavaScript
 // dialog presenter.
-TEST_F(WebStateDelegateTabHelperTest, GetJavaScriptDialogPresenter) {
+// TODO(crbug.com/1038514): Re-enable this test.
+TEST_F(WebStateDelegateTabHelperTest, DISABLED_GetJavaScriptDialogPresenter) {
   // Verify that the delegate returns a non-null presenter.
   web::JavaScriptDialogPresenter* presenter =
       delegate()->GetJavaScriptDialogPresenter(web_state());
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index f8bfa17..d31098b4 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -266,7 +266,6 @@
     "//ios/chrome/browser/ui/open_in:unit_tests",
     "//ios/chrome/browser/ui/overlays:unit_tests",
     "//ios/chrome/browser/ui/overlays/common/alerts:unit_tests",
-    "//ios/chrome/browser/ui/overlays/infobar_banner:unit_tests",
     "//ios/chrome/browser/ui/overlays/web_content_area/app_launcher:unit_tests",
     "//ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs:unit_tests",
     "//ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs:unit_tests",
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 45e14d40..eae1ec7 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -420,6 +420,7 @@
     "//ios/chrome/browser/ui/settings/clear_browsing_data",
     "//ios/chrome/browser/ui/settings/credit_card_scanner",
     "//ios/chrome/browser/ui/settings/google_services:constants",
+    "//ios/chrome/browser/ui/settings/google_services:eg_app_support+eg2",
     "//ios/chrome/browser/ui/settings/password:eg_app_support+eg2",
     "//ios/chrome/browser/ui/settings/password:password_constants",
     "//ios/chrome/browser/ui/settings/sync",
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index 795386a..5d9a913 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -600,9 +600,7 @@
     results_ = std::move(results);
   }
 
-  void set_error_info(int error, bool is_secure_network_error) {
-    error_info_ = ResolveErrorInfo(error, is_secure_network_error);
-  }
+  void set_error_info(int error) { error_info_ = ResolveErrorInfo(error); }
 
   void set_stale_info(HostCache::EntryStaleness stale_info) {
     // Should only be called at most once and before request is marked
@@ -636,10 +634,9 @@
   }
 
   // Cleans up Job assignment, marks request completed, and calls the completion
-  // callback. |is_secure_network_error| indicates whether |error| came from a
-  // secure DNS lookup.
-  void OnJobCompleted(Job* job, int error, bool is_secure_network_error) {
-    set_error_info(error, is_secure_network_error);
+  // callback.
+  void OnJobCompleted(Job* job, int error) {
+    set_error_info(error);
 
     DCHECK_EQ(job_, job);
     job_ = nullptr;
@@ -2594,9 +2591,7 @@
         req->set_results(
             results.CopyWithDefaultPort(req->request_host().port()));
       }
-      req->OnJobCompleted(
-          this, results.error(),
-          secure && results.error() != OK /* is_secure_network_error */);
+      req->OnJobCompleted(this, results.error());
 
       // Check if the resolver was destroyed as a result of running the
       // callback. If it was, we could continue, but we choose to bail.
@@ -2990,8 +2985,7 @@
       request->set_stale_info(std::move(stale_info).value());
     RecordTotalTime(request->parameters().is_speculative, true /* from_cache */,
                     effective_secure_dns_mode, base::TimeDelta());
-    request->set_error_info(results.error(),
-                            false /* is_secure_network_error */);
+    request->set_error_info(results.error());
     return results.error();
   }
 
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index 73a6844..7d465e2 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -5300,8 +5300,6 @@
       HostPortPair("automatic", 80), NetworkIsolationKey(), NetLogWithSource(),
       base::nullopt, request_context_.get(), host_cache_.get()));
   ASSERT_THAT(response_secure.result_error(), IsOk());
-  EXPECT_FALSE(
-      response_secure.request()->GetResolveErrorInfo().is_secure_network_error);
   EXPECT_THAT(
       response_secure.request()->GetAddressResults().value().endpoints(),
       testing::UnorderedElementsAre(CreateExpected("127.0.0.1", 80),
@@ -5320,9 +5318,6 @@
       NetLogWithSource(), base::nullopt, request_context_.get(),
       host_cache_.get()));
   ASSERT_THAT(response_insecure.result_error(), IsOk());
-  EXPECT_FALSE(response_insecure.request()
-                   ->GetResolveErrorInfo()
-                   .is_secure_network_error);
   EXPECT_THAT(
       response_insecure.request()->GetAddressResults().value().endpoints(),
       testing::UnorderedElementsAre(CreateExpected("127.0.0.1", 80),
@@ -5365,9 +5360,6 @@
       NetLogWithSource(), base::nullopt, request_context_.get(),
       host_cache_.get()));
   EXPECT_THAT(response_secure_cached.result_error(), IsOk());
-  EXPECT_FALSE(response_secure_cached.request()
-                   ->GetResolveErrorInfo()
-                   .is_secure_network_error);
   EXPECT_THAT(
       response_secure_cached.request()->GetAddressResults().value().endpoints(),
       testing::ElementsAre(kExpectedSecureIP));
@@ -5395,9 +5387,6 @@
       NetLogWithSource(), base::nullopt, request_context_.get(),
       host_cache_.get()));
   EXPECT_THAT(response_insecure_cached.result_error(), IsOk());
-  EXPECT_FALSE(response_insecure_cached.request()
-                   ->GetResolveErrorInfo()
-                   .is_secure_network_error);
   EXPECT_THAT(response_insecure_cached.request()
                   ->GetAddressResults()
                   .value()
@@ -5484,9 +5473,6 @@
       HostPortPair("automatic", 80), NetworkIsolationKey(), NetLogWithSource(),
       base::nullopt, request_context_.get(), host_cache_.get()));
   ASSERT_THAT(response_automatic.result_error(), IsOk());
-  EXPECT_FALSE(response_automatic.request()
-                   ->GetResolveErrorInfo()
-                   .is_secure_network_error);
   EXPECT_THAT(
       response_automatic.request()->GetAddressResults().value().endpoints(),
       testing::UnorderedElementsAre(CreateExpected("127.0.0.1", 80),
@@ -5519,8 +5505,6 @@
       HostPortPair("secure", 80), NetworkIsolationKey(), NetLogWithSource(),
       base::nullopt, request_context_.get(), host_cache_.get()));
   ASSERT_THAT(response_secure.result_error(), IsError(ERR_NAME_NOT_RESOLVED));
-  EXPECT_FALSE(
-      response_secure.request()->GetResolveErrorInfo().is_secure_network_error);
 
   HostCache::Key secure_key = HostCache::Key(
       "secure", DnsQueryType::UNSPECIFIED, 0 /* host_resolver_flags */,
@@ -5562,8 +5546,6 @@
       NetLogWithSource(), stale_allowed_parameters, request_context_.get(),
       host_cache_.get()));
   EXPECT_THAT(response_stale.result_error(), IsOk());
-  EXPECT_FALSE(
-      response_stale.request()->GetResolveErrorInfo().is_secure_network_error);
   EXPECT_THAT(response_stale.request()->GetAddressResults().value().endpoints(),
               testing::ElementsAre(kExpectedStaleIP));
   EXPECT_TRUE(response_stale.request()->GetStaleInfo()->is_stale());
@@ -5670,9 +5652,6 @@
       host_cache_.get()));
   proc_->SignalMultiple(1u);
   ASSERT_THAT(response_insecure.result_error(), IsOk());
-  EXPECT_FALSE(response_insecure.request()
-                   ->GetResolveErrorInfo()
-                   .is_secure_network_error);
   EXPECT_THAT(
       response_insecure.request()->GetAddressResults().value().endpoints(),
       testing::ElementsAre(CreateExpected("192.168.1.100", 80)));
@@ -5696,9 +5675,6 @@
       NetLogWithSource(), base::nullopt, request_context_.get(),
       host_cache_.get()));
   EXPECT_THAT(response_insecure_cached.result_error(), IsOk());
-  EXPECT_FALSE(response_insecure_cached.request()
-                   ->GetResolveErrorInfo()
-                   .is_secure_network_error);
   EXPECT_THAT(response_insecure_cached.request()
                   ->GetAddressResults()
                   .value()
@@ -5720,8 +5696,6 @@
       HostPortPair("secure", 80), NetworkIsolationKey(), NetLogWithSource(),
       base::nullopt, request_context_.get(), host_cache_.get()));
   ASSERT_THAT(response_secure.result_error(), IsOk());
-  EXPECT_FALSE(
-      response_secure.request()->GetResolveErrorInfo().is_secure_network_error);
   HostCache::Key secure_key = HostCache::Key(
       "secure", DnsQueryType::UNSPECIFIED, 0 /* host_resolver_flags */,
       HostResolverSource::ANY, NetworkIsolationKey());
@@ -5733,9 +5707,6 @@
       HostPortPair("ok", 80), NetworkIsolationKey(), NetLogWithSource(),
       base::nullopt, request_context_.get(), host_cache_.get()));
   ASSERT_THAT(response_insecure.result_error(), IsError(ERR_NAME_NOT_RESOLVED));
-  EXPECT_TRUE(response_insecure.request()
-                  ->GetResolveErrorInfo()
-                  .is_secure_network_error);
   HostCache::Key insecure_key = HostCache::Key(
       "ok", DnsQueryType::UNSPECIFIED, 0 /* host_resolver_flags */,
       HostResolverSource::ANY, NetworkIsolationKey());
@@ -5748,8 +5719,6 @@
       base::nullopt, request_context_.get(), host_cache_.get()));
   proc_->SignalMultiple(1u);
   EXPECT_THAT(response_proc.result_error(), IsError(ERR_NAME_NOT_RESOLVED));
-  EXPECT_TRUE(
-      response_proc.request()->GetResolveErrorInfo().is_secure_network_error);
 }
 
 TEST_F(HostResolverManagerDnsTest, SecureDnsMode_Secure_InsecureAsyncDisabled) {
@@ -5799,9 +5768,6 @@
       source_none_parameters, request_context_.get(), host_cache_.get()));
   EXPECT_TRUE(cache_miss_request.complete());
   EXPECT_THAT(cache_miss_request.result_error(), IsError(ERR_DNS_CACHE_MISS));
-  EXPECT_FALSE(cache_miss_request.request()
-                   ->GetResolveErrorInfo()
-                   .is_secure_network_error);
   EXPECT_FALSE(cache_miss_request.request()->GetAddressResults());
   EXPECT_FALSE(cache_miss_request.request()->GetStaleInfo());
 }
@@ -5830,8 +5796,6 @@
       base::nullopt, request_context_.get(), host_cache_.get()));
   EXPECT_TRUE(response_cached.complete());
   EXPECT_THAT(response_cached.result_error(), IsOk());
-  EXPECT_FALSE(
-      response_cached.request()->GetResolveErrorInfo().is_secure_network_error);
   EXPECT_THAT(
       response_cached.request()->GetAddressResults().value().endpoints(),
       testing::ElementsAre(kExpectedSecureIP));
diff --git a/net/dns/public/resolve_error_info.cc b/net/dns/public/resolve_error_info.cc
index f61c3a4..9a1b499 100644
--- a/net/dns/public/resolve_error_info.cc
+++ b/net/dns/public/resolve_error_info.cc
@@ -8,10 +8,8 @@
 
 ResolveErrorInfo::ResolveErrorInfo() {}
 
-ResolveErrorInfo::ResolveErrorInfo(int resolve_error,
-                                   bool is_secure_network_error)
-    : error(resolve_error), is_secure_network_error(is_secure_network_error) {
-  DCHECK(!(is_secure_network_error && resolve_error == net::OK));
+ResolveErrorInfo::ResolveErrorInfo(int resolve_error) {
+  error = resolve_error;
 }
 
 ResolveErrorInfo::ResolveErrorInfo(const ResolveErrorInfo& resolve_error_info) =
@@ -26,8 +24,7 @@
     default;
 
 bool ResolveErrorInfo::operator==(const ResolveErrorInfo& other) const {
-  return error == other.error &&
-         is_secure_network_error == other.is_secure_network_error;
+  return error == other.error;
 }
 
 bool ResolveErrorInfo::operator!=(const ResolveErrorInfo& other) const {
diff --git a/net/dns/public/resolve_error_info.h b/net/dns/public/resolve_error_info.h
index 07274f67..b108fa79 100644
--- a/net/dns/public/resolve_error_info.h
+++ b/net/dns/public/resolve_error_info.h
@@ -13,7 +13,7 @@
 // Host resolution error info.
 struct NET_EXPORT ResolveErrorInfo {
   ResolveErrorInfo();
-  ResolveErrorInfo(int resolve_error, bool is_secure_network_error = false);
+  ResolveErrorInfo(int resolve_error);
   ResolveErrorInfo(const ResolveErrorInfo& resolve_error_info);
   ResolveErrorInfo(ResolveErrorInfo&& other);
 
@@ -24,12 +24,6 @@
   bool operator!=(const ResolveErrorInfo& other) const;
 
   int error = net::OK;
-  // Whether |error| resulted from a DNS-over-HTTPS lookup. If an answer was
-  // obtained from the cache this field will be false, regardless of whether the
-  // answer was originally obtained securely, because this field is intended to
-  // identify secure DNS *network* failures. This field will also always be
-  // false if |error| is net::OK.
-  bool is_secure_network_error = false;
 };
 
 }  // namespace net
diff --git a/net/url_request/http_with_dns_over_https_unittest.cc b/net/url_request/http_with_dns_over_https_unittest.cc
index d65a847..d1ff9f8 100644
--- a/net/url_request/http_with_dns_over_https_unittest.cc
+++ b/net/url_request/http_with_dns_over_https_unittest.cc
@@ -287,37 +287,5 @@
   EXPECT_EQ(d.data_received(), kTestBody);
 }
 
-TEST_F(HttpWithDnsOverHttpsTest, EndToEndFail) {
-  // Shutdown the DoH server so that all DoH requests are refused.
-  EXPECT_TRUE(doh_server_.ShutdownAndWaitUntilComplete());
-
-  // Make a request that will trigger a DoH query.
-  TestDelegate d;
-  GURL main_url = test_server_.GetURL("fail.example.com", "/test");
-  std::unique_ptr<URLRequest> req(context()->CreateRequest(
-      main_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
-  req->Start();
-  base::RunLoop().Run();
-  EXPECT_TRUE(test_server_.ShutdownAndWaitUntilComplete());
-
-  // The two DoH lookups for "fail.example.com" (both A and AAAA
-  // records are queried) should both have failed to reach the DoH server
-  // since it was not running.
-  EXPECT_EQ(doh_queries_served_, 0u);
-  // The requests to the DoH server are pooled, so there should only be one
-  // insecure lookup for the DoH server hostname.
-  EXPECT_EQ(host_resolver_proc_->insecure_queries_served(), 1u);
-  // No HTTPS connection to the test server will be attempted due to the
-  // host resolution error.
-  EXPECT_EQ(test_https_requests_served_, 0u);
-
-  EXPECT_TRUE(d.response_completed());
-  EXPECT_EQ(d.request_status(), net::ERR_CONNECTION_REFUSED);
-
-  const auto& resolve_error_info = req->response_info().resolve_error_info;
-  EXPECT_TRUE(resolve_error_info.is_secure_network_error);
-  EXPECT_EQ(resolve_error_info.error, net::ERR_CONNECTION_REFUSED);
-}
-
 }  // namespace
 }  // namespace net
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index 9c3c5396..cdc89cc 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -1009,6 +1009,10 @@
 
   viewport_info.zoom = zoom_;
   viewport_info.scale = device_scale_;
+  viewport_info.total_scrollable_size = {GetTotalScrollableWidth(),
+                                         GetTotalScrollableHeight()};
+  viewport_info.current_scroll_position = {GetHorizontalScrollPosition(),
+                                           GetVerticalScrollPosition()};
 
   engine_->GetSelection(&viewport_info.selection_start_page_index,
                         &viewport_info.selection_start_char_index,
@@ -1291,6 +1295,25 @@
       ceil(document_size_.height() * zoom_ * device_scale_));
 }
 
+int OutOfProcessInstance::GetTotalScrollableWidth() const {
+  return std::max(GetDocumentPixelWidth() - plugin_size_.width(), 0);
+}
+
+int OutOfProcessInstance::GetTotalScrollableHeight() const {
+  return std::max(static_cast<int>(GetDocumentPixelHeight() +
+                                   GetToolbarHeightInScreenCoords() -
+                                   plugin_size_.height()),
+                  0);
+}
+
+int OutOfProcessInstance::GetHorizontalScrollPosition() const {
+  return static_cast<int>(scroll_offset_.x() * device_scale_);
+}
+
+int OutOfProcessInstance::GetVerticalScrollPosition() const {
+  return static_cast<int>(scroll_offset_.y() * device_scale_);
+}
+
 void OutOfProcessInstance::FillRect(const pp::Rect& rect, uint32_t color) {
   DCHECK(!image_data_.is_null() || rect.IsEmpty());
   uint32_t* buffer_start = static_cast<uint32_t*>(image_data_.data());
@@ -1982,7 +2005,7 @@
   pp::PDF::SetPluginCanSave(this, ShouldSaveEdits());
 }
 
-float OutOfProcessInstance::GetToolbarHeightInScreenCoords() {
+float OutOfProcessInstance::GetToolbarHeightInScreenCoords() const {
   return top_toolbar_height_in_viewport_coords_ * device_scale_;
 }
 
diff --git a/pdf/out_of_process_instance.h b/pdf/out_of_process_instance.h
index de8ab5fc..4ed34bd8 100644
--- a/pdf/out_of_process_instance.h
+++ b/pdf/out_of_process_instance.h
@@ -149,7 +149,7 @@
   void IsSelectingChanged(bool is_selecting) override;
   void SelectionChanged(const pp::Rect& left, const pp::Rect& right) override;
   void IsEditModeChanged(bool is_edit_mode) override;
-  float GetToolbarHeightInScreenCoords() override;
+  float GetToolbarHeightInScreenCoords() const override;
 
   // PreviewModeClient::Client implementation.
   void PreviewDocumentLoadComplete() override;
@@ -179,6 +179,14 @@
   int GetDocumentPixelWidth() const;
   int GetDocumentPixelHeight() const;
 
+  // Computes total scrollable Width and Height of the document.
+  int GetTotalScrollableWidth() const;
+  int GetTotalScrollableHeight() const;
+
+  // Computes current horizontal and scroll position of the document.
+  int GetHorizontalScrollPosition() const;
+  int GetVerticalScrollPosition() const;
+
   // Draws a rectangle with the specified dimensions and color in our buffer.
   void FillRect(const pp::Rect& rect, uint32_t color);
 
diff --git a/pdf/pdf_engine.h b/pdf/pdf_engine.h
index 48b22de..566f40b 100644
--- a/pdf/pdf_engine.h
+++ b/pdf/pdf_engine.h
@@ -281,7 +281,7 @@
 
     // Gets the height of the top toolbar in screen coordinates. This is
     // independent of whether it is hidden or not at the moment.
-    virtual float GetToolbarHeightInScreenCoords() = 0;
+    virtual float GetToolbarHeightInScreenCoords() const = 0;
   };
 
   struct AccessibilityLinkInfo {
diff --git a/pdf/preview_mode_client.cc b/pdf/preview_mode_client.cc
index 141b9eb..ee73d2cd 100644
--- a/pdf/preview_mode_client.cc
+++ b/pdf/preview_mode_client.cc
@@ -150,7 +150,7 @@
   return false;
 }
 
-float PreviewModeClient::GetToolbarHeightInScreenCoords() {
+float PreviewModeClient::GetToolbarHeightInScreenCoords() const {
   return 0.0f;
 }
 
diff --git a/pdf/preview_mode_client.h b/pdf/preview_mode_client.h
index e7b2e28..3df2019 100644
--- a/pdf/preview_mode_client.h
+++ b/pdf/preview_mode_client.h
@@ -67,7 +67,7 @@
   void DocumentHasUnsupportedFeature(const std::string& feature) override;
   void FormTextFieldFocusChange(bool in_focus) override;
   bool IsPrintPreview() override;
-  float GetToolbarHeightInScreenCoords() override;
+  float GetToolbarHeightInScreenCoords() const override;
   uint32_t GetBackgroundColor() override;
 
  private:
diff --git a/pdf/test/test_client.cc b/pdf/test/test_client.cc
index 7fab6611..4b979d56 100644
--- a/pdf/test/test_client.cc
+++ b/pdf/test/test_client.cc
@@ -56,7 +56,7 @@
   return 0;
 }
 
-float TestClient::GetToolbarHeightInScreenCoords() {
+float TestClient::GetToolbarHeightInScreenCoords() const {
   return 0;
 }
 
diff --git a/pdf/test/test_client.h b/pdf/test/test_client.h
index 4eaf930..0abea0b 100644
--- a/pdf/test/test_client.h
+++ b/pdf/test/test_client.h
@@ -37,7 +37,7 @@
   pp::Instance* GetPluginInstance() override;
   bool IsPrintPreview() override;
   uint32_t GetBackgroundColor() override;
-  float GetToolbarHeightInScreenCoords() override;
+  float GetToolbarHeightInScreenCoords() const override;
 
  private:
   // Not owned. Expected to dangle briefly, as the engine usually is destroyed
diff --git a/ppapi/c/private/ppb_pdf.h b/ppapi/c/private/ppb_pdf.h
index cfa0ef1..c209670 100644
--- a/ppapi/c/private/ppb_pdf.h
+++ b/ppapi/c/private/ppb_pdf.h
@@ -36,6 +36,10 @@
 struct PP_PrivateAccessibilityViewportInfo {
   double zoom;
   double scale;
+  // |total_scrollable_size| and |current_scroll_position| are relative
+  // to plugin embed and in screen coordinates.
+  struct PP_Size total_scrollable_size;
+  struct PP_Point current_scroll_position;
   struct PP_Point scroll;
   struct PP_Point offset;
   uint32_t selection_start_page_index;
diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h
index 88911ac..a80fd2d 100644
--- a/ppapi/proxy/ppapi_messages.h
+++ b/ppapi/proxy/ppapi_messages.h
@@ -268,6 +268,8 @@
 IPC_STRUCT_TRAITS_BEGIN(PP_PrivateAccessibilityViewportInfo)
   IPC_STRUCT_TRAITS_MEMBER(zoom)
   IPC_STRUCT_TRAITS_MEMBER(scale)
+  IPC_STRUCT_TRAITS_MEMBER(total_scrollable_size)
+  IPC_STRUCT_TRAITS_MEMBER(current_scroll_position)
   IPC_STRUCT_TRAITS_MEMBER(scroll)
   IPC_STRUCT_TRAITS_MEMBER(offset)
   IPC_STRUCT_TRAITS_MEMBER(selection_start_page_index)
diff --git a/services/device/nfc/android/java/src/org/chromium/device/nfc/NdefMessageUtils.java b/services/device/nfc/android/java/src/org/chromium/device/nfc/NdefMessageUtils.java
index 0429976..82636231 100644
--- a/services/device/nfc/android/java/src/org/chromium/device/nfc/NdefMessageUtils.java
+++ b/services/device/nfc/android/java/src/org/chromium/device/nfc/NdefMessageUtils.java
@@ -11,6 +11,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.device.mojom.NdefMessage;
 import org.chromium.device.mojom.NdefRecord;
+import org.chromium.device.mojom.NdefRecordTypeCategory;
 
 import java.io.UnsupportedEncodingException;
 import java.nio.ByteBuffer;
@@ -80,16 +81,6 @@
             "urn:nfc:", // 0x23
     };
 
-    private static class PairOfDomainAndType {
-        private String mDomain;
-        private String mType;
-
-        private PairOfDomainAndType(String domain, String type) {
-            mDomain = domain;
-            mType = type;
-        }
-    }
-
     /**
      * Converts mojo NdefMessage to android.nfc.NdefMessage
      */
@@ -136,33 +127,43 @@
     private static android.nfc.NdefRecord toNdefRecord(NdefRecord record)
             throws InvalidNdefMessageException, IllegalArgumentException,
                    UnsupportedEncodingException {
-        switch (record.recordType) {
-            case RECORD_TYPE_URL:
-                return createPlatformUrlRecord(record.data, record.id, false /* isAbsUrl */);
-            case RECORD_TYPE_ABSOLUTE_URL:
-                return createPlatformUrlRecord(record.data, record.id, true /* isAbsUrl */);
-            case RECORD_TYPE_TEXT:
-                return createPlatformTextRecord(
-                        record.id, record.lang, record.encoding, record.data);
-            case RECORD_TYPE_MIME:
-                return createPlatformMimeRecord(record.mediaType, record.id, record.data);
-            case RECORD_TYPE_UNKNOWN:
-                return new android.nfc.NdefRecord(android.nfc.NdefRecord.TNF_UNKNOWN,
-                        null /* type */,
-                        record.id == null ? null : ApiCompatibilityUtils.getBytesUtf8(record.id),
-                        record.data);
-            case RECORD_TYPE_EMPTY:
-                return new android.nfc.NdefRecord(android.nfc.NdefRecord.TNF_EMPTY, null /* type */,
-                        null /* id */, null /* payload */);
-            case RECORD_TYPE_SMART_POSTER:
-                // TODO(https://crbug.com/520391): Support 'smart-poster' type records.
-                throw new InvalidNdefMessageException();
+        if (record.category == NdefRecordTypeCategory.STANDARDIZED) {
+            switch (record.recordType) {
+                case RECORD_TYPE_URL:
+                    return createPlatformUrlRecord(record.data, record.id, false /* isAbsUrl */);
+                case RECORD_TYPE_ABSOLUTE_URL:
+                    return createPlatformUrlRecord(record.data, record.id, true /* isAbsUrl */);
+                case RECORD_TYPE_TEXT:
+                    return createPlatformTextRecord(
+                            record.id, record.lang, record.encoding, record.data);
+                case RECORD_TYPE_MIME:
+                    return createPlatformMimeRecord(record.mediaType, record.id, record.data);
+                case RECORD_TYPE_UNKNOWN:
+                    return new android.nfc.NdefRecord(android.nfc.NdefRecord.TNF_UNKNOWN,
+                            null /* type */,
+                            record.id == null ? null
+                                              : ApiCompatibilityUtils.getBytesUtf8(record.id),
+                            record.data);
+                case RECORD_TYPE_EMPTY:
+                    return new android.nfc.NdefRecord(android.nfc.NdefRecord.TNF_EMPTY,
+                            null /* type */, null /* id */, null /* payload */);
+                case RECORD_TYPE_SMART_POSTER:
+                    // TODO(https://crbug.com/520391): Support 'smart-poster' type records.
+                    throw new InvalidNdefMessageException();
+            }
+            throw new InvalidNdefMessageException();
         }
-        // TODO(https://crbug.com/520391): Need to create an external record for either a custom
-        // type name or a local type name (for an embedded record).
-        PairOfDomainAndType pair = parseDomainAndType(record.recordType);
-        if (pair != null) {
-            return createPlatformExternalRecord(pair.mDomain, pair.mType, record.id, record.data);
+
+        if (record.category == NdefRecordTypeCategory.EXTERNAL) {
+            if (isValidExternalType(record.recordType)) {
+                return createPlatformExternalRecord(record.recordType, record.id, record.data);
+            }
+            throw new InvalidNdefMessageException();
+        }
+
+        if (record.category == NdefRecordTypeCategory.LOCAL) {
+            // TODO(https://crbug.com/520391): Support local type records.
+            throw new InvalidNdefMessageException();
         }
 
         throw new InvalidNdefMessageException();
@@ -215,6 +216,7 @@
      */
     private static NdefRecord createEmptyRecord() {
         NdefRecord nfcRecord = new NdefRecord();
+        nfcRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         nfcRecord.recordType = RECORD_TYPE_EMPTY;
         nfcRecord.data = new byte[0];
         return nfcRecord;
@@ -226,6 +228,7 @@
     private static NdefRecord createURLRecord(Uri uri, boolean isAbsUrl) {
         if (uri == null) return null;
         NdefRecord nfcRecord = new NdefRecord();
+        nfcRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         if (isAbsUrl) {
             nfcRecord.recordType = RECORD_TYPE_ABSOLUTE_URL;
         } else {
@@ -241,6 +244,7 @@
      */
     private static NdefRecord createMIMERecord(String mediaType, byte[] payload) {
         NdefRecord nfcRecord = new NdefRecord();
+        nfcRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         nfcRecord.recordType = RECORD_TYPE_MIME;
         nfcRecord.mediaType = mediaType;
         nfcRecord.data = payload;
@@ -257,6 +261,7 @@
         }
 
         NdefRecord nfcRecord = new NdefRecord();
+        nfcRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         nfcRecord.recordType = RECORD_TYPE_TEXT;
         // According to NFCForum-TS-RTD_Text_1.0 specification, section 3.2.1 Syntax.
         // First byte of the payload is status byte, defined in Table 3: Status Byte Encodings.
@@ -289,6 +294,8 @@
 
         // TODO(https://crbug.com/520391): Support RTD_SMART_POSTER type records.
 
+        // TODO(https://crbug.com/520391): Support local type records.
+
         return null;
     }
 
@@ -297,6 +304,7 @@
      */
     private static NdefRecord createUnknownRecord(byte[] payload) {
         NdefRecord nfcRecord = new NdefRecord();
+        nfcRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         nfcRecord.recordType = RECORD_TYPE_UNKNOWN;
         nfcRecord.data = payload;
         return nfcRecord;
@@ -306,8 +314,8 @@
      * Constructs External type NdefRecord
      */
     private static NdefRecord createExternalTypeRecord(String type, byte[] payload) {
-        // |type| may be a custom type name or a local type name (for an embedded record).
         NdefRecord nfcRecord = new NdefRecord();
+        nfcRecord.category = NdefRecordTypeCategory.EXTERNAL;
         nfcRecord.recordType = type;
         nfcRecord.data = payload;
         nfcRecord.payloadMessage = getNdefMessageFromPayload(payload);
@@ -411,45 +419,39 @@
      * Creates a TNF_EXTERNAL_TYPE android.nfc.NdefRecord.
      */
     public static android.nfc.NdefRecord createPlatformExternalRecord(
-            String domain, String type, String id, byte[] payload) {
-        // Already guaranteed by parseDomainAndType().
-        assert domain != null && !domain.isEmpty();
-        assert type != null && !type.isEmpty();
+            String recordType, String id, byte[] payload) {
+        // Already guaranteed by the caller.
+        assert recordType != null && !recordType.isEmpty();
 
         // NFC Forum requires that the domain and type used in an external record are treated as
         // case insensitive, however Android intent filtering is always case sensitive. So we force
         // the domain and type to lower-case here and later we will compare in a case insensitive
         // way when filtering by them.
-        String record_type = domain.toLowerCase(Locale.ROOT) + ':' + type.toLowerCase(Locale.ROOT);
-
         return new android.nfc.NdefRecord(android.nfc.NdefRecord.TNF_EXTERNAL_TYPE,
-                ApiCompatibilityUtils.getBytesUtf8(record_type),
+                ApiCompatibilityUtils.getBytesUtf8(recordType.toLowerCase(Locale.ROOT)),
                 id == null ? null : ApiCompatibilityUtils.getBytesUtf8(id), payload);
     }
 
     /**
-     * Parses the input custom type to get its domain and type.
-     * e.g. returns a pair ('w3.org', 'xyz') for the input 'w3.org:xyz'.
-     * Returns null for invalid input.
-     * https://w3c.github.io/web-nfc/#ndef-record-types
-     *
-     * TODO(https://crbug.com/520391): Refine the validation algorithm here accordingly once there
-     * is a conclusion on some case-sensitive things at https://github.com/w3c/web-nfc/issues/331.
+     * Validates external types.
+     * https://w3c.github.io/web-nfc/#dfn-validate-external-type
      */
-    private static PairOfDomainAndType parseDomainAndType(String customType) {
-        int colonIndex = customType.indexOf(':');
-        if (colonIndex == -1) return null;
+    private static boolean isValidExternalType(String input) {
+        if (input.isEmpty() || input.length() > 255) return false;
 
-        // TODO(ThisCL): verify |domain| is a valid FQDN, asking help at
-        // https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/QN2mHt_WgHo.
-        String domain = customType.substring(0, colonIndex).trim();
-        if (domain.isEmpty()) return null;
+        int colonIndex = input.lastIndexOf(':');
+        if (colonIndex == -1) return false;
 
-        String type = customType.substring(colonIndex + 1).trim();
-        if (type.isEmpty()) return null;
-        if (!type.matches("[a-zA-Z0-9()+,\\-:=@;$_!*'.]+")) return null;
+        String domain = input.substring(0, colonIndex).trim();
+        if (domain.isEmpty()) return false;
+        // TODO(https://crbug.com/520391): Make sure |domain| can be converted successfully to ASCII
+        // using IDN rules and does not contain any forbidden host code point.
 
-        return new PairOfDomainAndType(domain, type);
+        String type = input.substring(colonIndex + 1).trim();
+        if (type.isEmpty()) return false;
+        if (!type.matches("[a-zA-Z0-9()+,\\-=@;$_*'.]+")) return false;
+
+        return true;
     }
 
     /**
diff --git a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java b/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
index 4c0b2a4..36748199 100644
--- a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
+++ b/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
@@ -29,6 +29,7 @@
 import org.chromium.device.mojom.NdefPushOptions;
 import org.chromium.device.mojom.NdefPushTarget;
 import org.chromium.device.mojom.NdefRecord;
+import org.chromium.device.mojom.NdefRecordTypeCategory;
 import org.chromium.device.mojom.NdefScanOptions;
 import org.chromium.device.mojom.Nfc;
 import org.chromium.device.mojom.NfcClient;
@@ -598,9 +599,18 @@
             if (options.id != null && !options.id.equals(message.data[i].id)) {
                 continue;
             }
-            if (options.recordType != null
-                    && !options.recordType.equals(message.data[i].recordType)) {
-                continue;
+            if (options.recordType != null) {
+                if (message.data[i].category == NdefRecordTypeCategory.EXTERNAL) {
+                    // The spec https://w3c.github.io/web-nfc/#the-record-type-string says "Two
+                    // external types MUST be compared character by character, in case-insensitive
+                    // manner".
+                    if (options.recordType.compareToIgnoreCase(message.data[i].recordType) != 0) {
+                        continue;
+                    }
+                    // All other types should be compared in case-sensitive manner.
+                } else if (!options.recordType.equals(message.data[i].recordType)) {
+                    continue;
+                }
             }
             if (!options.mediaType.isEmpty()
                     && !options.mediaType.equals(message.data[i].mediaType)) {
diff --git a/services/device/nfc/android/junit/src/org/chromium/device/nfc/NFCTest.java b/services/device/nfc/android/junit/src/org/chromium/device/nfc/NFCTest.java
index a8a8941..eb48e4e 100644
--- a/services/device/nfc/android/junit/src/org/chromium/device/nfc/NFCTest.java
+++ b/services/device/nfc/android/junit/src/org/chromium/device/nfc/NFCTest.java
@@ -48,6 +48,7 @@
 import org.chromium.device.mojom.NdefPushOptions;
 import org.chromium.device.mojom.NdefPushTarget;
 import org.chromium.device.mojom.NdefRecord;
+import org.chromium.device.mojom.NdefRecordTypeCategory;
 import org.chromium.device.mojom.NdefScanOptions;
 import org.chromium.device.mojom.Nfc.CancelAllWatchesResponse;
 import org.chromium.device.mojom.Nfc.CancelPushResponse;
@@ -62,6 +63,7 @@
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 
 /**
  * Unit tests for NfcImpl and NdefMessageUtils classes.
@@ -89,8 +91,7 @@
     private ArgumentCaptor<int[]> mOnWatchCallbackCaptor;
 
     // Constants used for the test.
-    private static final String DUMMY_EXTERNAL_RECORD_DOMAIN = "abc.com";
-    private static final String DUMMY_EXTERNAL_RECORD_TYPE = "xyz";
+    private static final String DUMMY_EXTERNAL_TYPE = "abc.com:xyz";
     private static final String DUMMY_RECORD_ID = "https://www.example.com/ids/1";
     private static final String TEST_TEXT = "test";
     private static final String TEST_URL = "https://google.com";
@@ -219,6 +220,7 @@
                 new android.nfc.NdefRecord(android.nfc.NdefRecord.TNF_EMPTY, null, null, null));
         NdefMessage emptyMojoNdefMessage = NdefMessageUtils.toNdefMessage(emptyNdefMessage);
         assertEquals(1, emptyMojoNdefMessage.data.length);
+        assertEquals(NdefRecordTypeCategory.STANDARDIZED, emptyMojoNdefMessage.data[0].category);
         assertEquals(NdefMessageUtils.RECORD_TYPE_EMPTY, emptyMojoNdefMessage.data[0].recordType);
         assertEquals(null, emptyMojoNdefMessage.data[0].mediaType);
         assertEquals(null, emptyMojoNdefMessage.data[0].id);
@@ -233,6 +235,7 @@
                         false /* isAbsUrl */));
         NdefMessage urlMojoNdefMessage = NdefMessageUtils.toNdefMessage(urlNdefMessage);
         assertEquals(1, urlMojoNdefMessage.data.length);
+        assertEquals(NdefRecordTypeCategory.STANDARDIZED, urlMojoNdefMessage.data[0].category);
         assertEquals(NdefMessageUtils.RECORD_TYPE_URL, urlMojoNdefMessage.data[0].recordType);
         assertEquals(null, urlMojoNdefMessage.data[0].mediaType);
         assertEquals(DUMMY_RECORD_ID, urlMojoNdefMessage.data[0].id);
@@ -247,6 +250,7 @@
                         true /* isAbsUrl */));
         NdefMessage absUrlMojoNdefMessage = NdefMessageUtils.toNdefMessage(absUrlNdefMessage);
         assertEquals(1, absUrlMojoNdefMessage.data.length);
+        assertEquals(NdefRecordTypeCategory.STANDARDIZED, absUrlMojoNdefMessage.data[0].category);
         assertEquals(NdefMessageUtils.RECORD_TYPE_ABSOLUTE_URL,
                 absUrlMojoNdefMessage.data[0].recordType);
         assertEquals(null, absUrlMojoNdefMessage.data[0].mediaType);
@@ -259,6 +263,7 @@
                         ENCODING_UTF8, ApiCompatibilityUtils.getBytesUtf8(TEST_TEXT)));
         NdefMessage utf8TextMojoNdefMessage = NdefMessageUtils.toNdefMessage(utf8TextNdefMessage);
         assertEquals(1, utf8TextMojoNdefMessage.data.length);
+        assertEquals(NdefRecordTypeCategory.STANDARDIZED, utf8TextMojoNdefMessage.data[0].category);
         assertEquals(NdefMessageUtils.RECORD_TYPE_TEXT, utf8TextMojoNdefMessage.data[0].recordType);
         assertEquals(null, utf8TextMojoNdefMessage.data[0].mediaType);
         assertEquals(DUMMY_RECORD_ID, utf8TextMojoNdefMessage.data[0].id);
@@ -275,6 +280,8 @@
         NdefMessage utf16TextMojoNdefMessage = NdefMessageUtils.toNdefMessage(utf16TextNdefMessage);
         assertEquals(1, utf16TextMojoNdefMessage.data.length);
         assertEquals(
+                NdefRecordTypeCategory.STANDARDIZED, utf16TextMojoNdefMessage.data[0].category);
+        assertEquals(
                 NdefMessageUtils.RECORD_TYPE_TEXT, utf16TextMojoNdefMessage.data[0].recordType);
         assertEquals(null, utf16TextMojoNdefMessage.data[0].mediaType);
         assertEquals(DUMMY_RECORD_ID, utf16TextMojoNdefMessage.data[0].id);
@@ -288,6 +295,7 @@
                         TEXT_MIME, DUMMY_RECORD_ID, ApiCompatibilityUtils.getBytesUtf8(TEST_TEXT)));
         NdefMessage mimeMojoNdefMessage = NdefMessageUtils.toNdefMessage(mimeNdefMessage);
         assertEquals(1, mimeMojoNdefMessage.data.length);
+        assertEquals(NdefRecordTypeCategory.STANDARDIZED, mimeMojoNdefMessage.data[0].category);
         assertEquals(NdefMessageUtils.RECORD_TYPE_MIME, mimeMojoNdefMessage.data[0].recordType);
         assertEquals(TEXT_MIME, mimeMojoNdefMessage.data[0].mediaType);
         assertEquals(DUMMY_RECORD_ID, mimeMojoNdefMessage.data[0].id);
@@ -301,6 +309,7 @@
                         JSON_MIME, DUMMY_RECORD_ID, ApiCompatibilityUtils.getBytesUtf8(TEST_JSON)));
         NdefMessage jsonMojoNdefMessage = NdefMessageUtils.toNdefMessage(jsonNdefMessage);
         assertEquals(1, jsonMojoNdefMessage.data.length);
+        assertEquals(NdefRecordTypeCategory.STANDARDIZED, jsonMojoNdefMessage.data[0].category);
         assertEquals(NdefMessageUtils.RECORD_TYPE_MIME, jsonMojoNdefMessage.data[0].recordType);
         assertEquals(JSON_MIME, jsonMojoNdefMessage.data[0].mediaType);
         assertEquals(DUMMY_RECORD_ID, jsonMojoNdefMessage.data[0].id);
@@ -315,6 +324,7 @@
                         ApiCompatibilityUtils.getBytesUtf8(TEST_TEXT)));
         NdefMessage unknownMojoNdefMessage = NdefMessageUtils.toNdefMessage(unknownNdefMessage);
         assertEquals(1, unknownMojoNdefMessage.data.length);
+        assertEquals(NdefRecordTypeCategory.STANDARDIZED, unknownMojoNdefMessage.data[0].category);
         assertEquals(
                 NdefMessageUtils.RECORD_TYPE_UNKNOWN, unknownMojoNdefMessage.data[0].recordType);
         assertEquals(DUMMY_RECORD_ID, unknownMojoNdefMessage.data[0].id);
@@ -323,14 +333,13 @@
         assertEquals(TEST_TEXT, new String(unknownMojoNdefMessage.data[0].data));
 
         // Test external record conversion.
-        android.nfc.NdefMessage extNdefMessage =
-                new android.nfc.NdefMessage(NdefMessageUtils.createPlatformExternalRecord(
-                        DUMMY_EXTERNAL_RECORD_DOMAIN, DUMMY_EXTERNAL_RECORD_TYPE, DUMMY_RECORD_ID,
+        android.nfc.NdefMessage extNdefMessage = new android.nfc.NdefMessage(
+                NdefMessageUtils.createPlatformExternalRecord(DUMMY_EXTERNAL_TYPE, DUMMY_RECORD_ID,
                         ApiCompatibilityUtils.getBytesUtf8(TEST_TEXT)));
         NdefMessage extMojoNdefMessage = NdefMessageUtils.toNdefMessage(extNdefMessage);
         assertEquals(1, extMojoNdefMessage.data.length);
-        assertEquals(DUMMY_EXTERNAL_RECORD_DOMAIN + ':' + DUMMY_EXTERNAL_RECORD_TYPE,
-                extMojoNdefMessage.data[0].recordType);
+        assertEquals(NdefRecordTypeCategory.EXTERNAL, extMojoNdefMessage.data[0].category);
+        assertEquals(DUMMY_EXTERNAL_TYPE, extMojoNdefMessage.data[0].recordType);
         assertEquals(null, extMojoNdefMessage.data[0].mediaType);
         assertEquals(DUMMY_RECORD_ID, extMojoNdefMessage.data[0].id);
         assertNull(extMojoNdefMessage.data[0].encoding);
@@ -342,19 +351,20 @@
                 android.nfc.NdefRecord.createTextRecord(LANG_EN_US, TEST_TEXT));
         byte[] payloadBytes = payloadMessage.toByteArray();
         // Put |payloadBytes| as payload of an external record.
-        android.nfc.NdefMessage extNdefMessage1 = new android.nfc.NdefMessage(
-                NdefMessageUtils.createPlatformExternalRecord(DUMMY_EXTERNAL_RECORD_DOMAIN,
-                        DUMMY_EXTERNAL_RECORD_TYPE, DUMMY_RECORD_ID, payloadBytes));
+        android.nfc.NdefMessage extNdefMessage1 =
+                new android.nfc.NdefMessage(NdefMessageUtils.createPlatformExternalRecord(
+                        DUMMY_EXTERNAL_TYPE, DUMMY_RECORD_ID, payloadBytes));
         NdefMessage extMojoNdefMessage1 = NdefMessageUtils.toNdefMessage(extNdefMessage1);
         assertEquals(1, extMojoNdefMessage1.data.length);
-        assertEquals(DUMMY_EXTERNAL_RECORD_DOMAIN + ':' + DUMMY_EXTERNAL_RECORD_TYPE,
-                extMojoNdefMessage1.data[0].recordType);
+        assertEquals(NdefRecordTypeCategory.EXTERNAL, extMojoNdefMessage1.data[0].category);
+        assertEquals(DUMMY_EXTERNAL_TYPE, extMojoNdefMessage1.data[0].recordType);
         assertEquals(null, extMojoNdefMessage1.data[0].mediaType);
         assertEquals(DUMMY_RECORD_ID, extMojoNdefMessage1.data[0].id);
         // The embedded ndef message should have content corresponding with the original
         // |payloadMessage|.
         NdefMessage payloadMojoMessage = extMojoNdefMessage1.data[0].payloadMessage;
         assertEquals(1, payloadMojoMessage.data.length);
+        assertEquals(NdefRecordTypeCategory.STANDARDIZED, payloadMojoMessage.data[0].category);
         assertEquals(NdefMessageUtils.RECORD_TYPE_TEXT, payloadMojoMessage.data[0].recordType);
         assertEquals(null, payloadMojoMessage.data[0].mediaType);
         assertEquals(TEST_TEXT, new String(payloadMojoMessage.data[0].data));
@@ -368,6 +378,7 @@
     public void testMojoToNdefConversion() throws InvalidNdefMessageException {
         // Test url record conversion.
         NdefRecord urlMojoNdefRecord = new NdefRecord();
+        urlMojoNdefRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         urlMojoNdefRecord.recordType = NdefMessageUtils.RECORD_TYPE_URL;
         urlMojoNdefRecord.id = DUMMY_RECORD_ID;
         urlMojoNdefRecord.data = ApiCompatibilityUtils.getBytesUtf8(TEST_URL);
@@ -383,6 +394,7 @@
 
         // Test absolute-url record conversion.
         NdefRecord absUrlMojoNdefRecord = new NdefRecord();
+        absUrlMojoNdefRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         absUrlMojoNdefRecord.recordType = NdefMessageUtils.RECORD_TYPE_ABSOLUTE_URL;
         absUrlMojoNdefRecord.id = DUMMY_RECORD_ID;
         absUrlMojoNdefRecord.data = ApiCompatibilityUtils.getBytesUtf8(TEST_URL);
@@ -397,6 +409,7 @@
 
         // Test text record conversion for UTF-8 content.
         NdefRecord utf8TextMojoNdefRecord = new NdefRecord();
+        utf8TextMojoNdefRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         utf8TextMojoNdefRecord.recordType = NdefMessageUtils.RECORD_TYPE_TEXT;
         utf8TextMojoNdefRecord.id = DUMMY_RECORD_ID;
         utf8TextMojoNdefRecord.encoding = ENCODING_UTF8;
@@ -423,6 +436,7 @@
 
         // Test text record conversion for UTF-16 content.
         NdefRecord utf16TextMojoNdefRecord = new NdefRecord();
+        utf16TextMojoNdefRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         utf16TextMojoNdefRecord.recordType = NdefMessageUtils.RECORD_TYPE_TEXT;
         utf16TextMojoNdefRecord.id = DUMMY_RECORD_ID;
         utf16TextMojoNdefRecord.encoding = ENCODING_UTF16;
@@ -450,6 +464,7 @@
 
         // Test mime record conversion with "text/plain" mime type.
         NdefRecord mimeMojoNdefRecord = new NdefRecord();
+        mimeMojoNdefRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         mimeMojoNdefRecord.recordType = NdefMessageUtils.RECORD_TYPE_MIME;
         mimeMojoNdefRecord.mediaType = TEXT_MIME;
         mimeMojoNdefRecord.id = DUMMY_RECORD_ID;
@@ -466,6 +481,7 @@
 
         // Test mime record conversion with "application/json" mime type.
         NdefRecord jsonMojoNdefRecord = new NdefRecord();
+        jsonMojoNdefRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         jsonMojoNdefRecord.recordType = NdefMessageUtils.RECORD_TYPE_MIME;
         jsonMojoNdefRecord.mediaType = JSON_MIME;
         jsonMojoNdefRecord.id = DUMMY_RECORD_ID;
@@ -482,6 +498,7 @@
 
         // Test unknown record conversion.
         NdefRecord unknownMojoNdefRecord = new NdefRecord();
+        unknownMojoNdefRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         unknownMojoNdefRecord.recordType = NdefMessageUtils.RECORD_TYPE_UNKNOWN;
         unknownMojoNdefRecord.id = DUMMY_RECORD_ID;
         unknownMojoNdefRecord.data = ApiCompatibilityUtils.getBytesUtf8(TEST_TEXT);
@@ -496,8 +513,8 @@
 
         // Test external record conversion.
         NdefRecord extMojoNdefRecord = new NdefRecord();
-        extMojoNdefRecord.recordType =
-                DUMMY_EXTERNAL_RECORD_DOMAIN + ':' + DUMMY_EXTERNAL_RECORD_TYPE;
+        extMojoNdefRecord.category = NdefRecordTypeCategory.EXTERNAL;
+        extMojoNdefRecord.recordType = DUMMY_EXTERNAL_TYPE;
         extMojoNdefRecord.id = DUMMY_RECORD_ID;
         extMojoNdefRecord.data = ApiCompatibilityUtils.getBytesUtf8(TEST_TEXT);
         NdefMessage extMojoNdefMessage = createMojoNdefMessage(extMojoNdefRecord);
@@ -505,13 +522,13 @@
         assertEquals(1, extNdefMessage.getRecords().length);
         assertEquals(
                 android.nfc.NdefRecord.TNF_EXTERNAL_TYPE, extNdefMessage.getRecords()[0].getTnf());
-        assertEquals(DUMMY_EXTERNAL_RECORD_DOMAIN + ':' + DUMMY_EXTERNAL_RECORD_TYPE,
-                new String(extNdefMessage.getRecords()[0].getType()));
+        assertEquals(DUMMY_EXTERNAL_TYPE, new String(extNdefMessage.getRecords()[0].getType()));
         assertEquals(DUMMY_RECORD_ID, new String(extNdefMessage.getRecords()[0].getId()));
         assertEquals(TEST_TEXT, new String(extNdefMessage.getRecords()[0].getPayload()));
 
         // Test EMPTY record conversion.
         NdefRecord emptyMojoNdefRecord = new NdefRecord();
+        emptyMojoNdefRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         emptyMojoNdefRecord.recordType = NdefMessageUtils.RECORD_TYPE_EMPTY;
         NdefMessage emptyMojoNdefMessage = createMojoNdefMessage(emptyMojoNdefRecord);
         android.nfc.NdefMessage emptyNdefMessage =
@@ -526,13 +543,49 @@
     @Test(expected = InvalidNdefMessageException.class)
     @Feature({"NFCTest"})
     public void testInvalidExternalRecordType() throws InvalidNdefMessageException {
-        NdefRecord extMojoNdefRecord = new NdefRecord();
-        // '/' is not allowed.
-        extMojoNdefRecord.recordType = "abc.com:xyz/";
-        extMojoNdefRecord.data = ApiCompatibilityUtils.getBytesUtf8(TEST_TEXT);
-        NdefMessage extMojoNdefMessage = createMojoNdefMessage(extMojoNdefRecord);
-        android.nfc.NdefMessage extNdefMessage = NdefMessageUtils.toNdefMessage(extMojoNdefMessage);
-        assertEquals(null, extNdefMessage);
+        {
+            NdefRecord extMojoNdefRecord = new NdefRecord();
+            extMojoNdefRecord.category = NdefRecordTypeCategory.EXTERNAL;
+            // Must have a ':'.
+            extMojoNdefRecord.recordType = "abc.com";
+            extMojoNdefRecord.data = ApiCompatibilityUtils.getBytesUtf8(TEST_TEXT);
+            NdefMessage extMojoNdefMessage = createMojoNdefMessage(extMojoNdefRecord);
+            android.nfc.NdefMessage extNdefMessage =
+                    NdefMessageUtils.toNdefMessage(extMojoNdefMessage);
+            assertNull(extNdefMessage);
+        }
+        {
+            NdefRecord extMojoNdefRecord = new NdefRecord();
+            extMojoNdefRecord.category = NdefRecordTypeCategory.EXTERNAL;
+            extMojoNdefRecord.data = ApiCompatibilityUtils.getBytesUtf8(TEST_TEXT);
+
+            char[] chars = new char[251];
+            Arrays.fill(chars, 'a');
+            String domain = new String(chars);
+
+            // |recordType|'s length is 255, OK.
+            extMojoNdefRecord.recordType = domain + ":xyz";
+            android.nfc.NdefMessage extNdefMessage_255 =
+                    NdefMessageUtils.toNdefMessage(createMojoNdefMessage(extMojoNdefRecord));
+            assertNotNull(extNdefMessage_255);
+
+            // Exceeding the maximum length 255, FAIL.
+            extMojoNdefRecord.recordType = domain + ":xyze";
+            android.nfc.NdefMessage extNdefMessage_256 =
+                    NdefMessageUtils.toNdefMessage(createMojoNdefMessage(extMojoNdefRecord));
+            assertNull(extNdefMessage_256);
+        }
+        {
+            NdefRecord extMojoNdefRecord = new NdefRecord();
+            extMojoNdefRecord.category = NdefRecordTypeCategory.EXTERNAL;
+            // '/' is not allowed.
+            extMojoNdefRecord.recordType = "abc.com:xyz/";
+            extMojoNdefRecord.data = ApiCompatibilityUtils.getBytesUtf8(TEST_TEXT);
+            NdefMessage extMojoNdefMessage = createMojoNdefMessage(extMojoNdefRecord);
+            android.nfc.NdefMessage extNdefMessage =
+                    NdefMessageUtils.toNdefMessage(extMojoNdefMessage);
+            assertNull(extNdefMessage);
+        }
     }
 
     /**
@@ -727,6 +780,15 @@
         verify(mockWatchCallback4).call(mErrorCaptor.capture());
         assertNull(mErrorCaptor.getValue());
 
+        // Should not match because the record type must match case-sensitive.
+        NdefScanOptions options5 = createNdefScanOptions();
+        options5.recordType = "Url";
+        int watchId5 = mNextWatchId++;
+        WatchResponse mockWatchCallback5 = mock(WatchResponse.class);
+        nfc.watch(options5, watchId5, mockWatchCallback5);
+        verify(mockWatchCallback5).call(mErrorCaptor.capture());
+        assertNull(mErrorCaptor.getValue());
+
         nfc.processPendingOperationsForTesting(mNfcTagHandler);
 
         // Check that client was notified and watch with correct id was triggered.
@@ -740,6 +802,63 @@
     }
 
     /**
+     * Test that Nfc.watch() matching function compares 2 external types in case-insensitive manner.
+     */
+    @Test
+    @Feature({"NFCTest"})
+    public void testWatchMatchingExternalType() {
+        TestNfcImpl nfc = new TestNfcImpl(mContext, mDelegate);
+        mDelegate.invokeCallback();
+        nfc.setClient(mNfcClient);
+
+        // Prepare the external type record.
+        android.nfc.NdefMessage extNdefMessage = new android.nfc.NdefMessage(
+                NdefMessageUtils.createPlatformExternalRecord(DUMMY_EXTERNAL_TYPE, DUMMY_RECORD_ID,
+                        ApiCompatibilityUtils.getBytesUtf8(TEST_TEXT)));
+        try {
+            doReturn(extNdefMessage).when(mNfcTagHandler).read();
+        } catch (IOException | FormatException e) {
+        }
+
+        // Should match, the record type is exactly equal.
+        NdefScanOptions options1 = createNdefScanOptions();
+        options1.recordType = DUMMY_EXTERNAL_TYPE;
+        int watchId1 = mNextWatchId++;
+        WatchResponse mockWatchCallback1 = mock(WatchResponse.class);
+        nfc.watch(options1, watchId1, mockWatchCallback1);
+        verify(mockWatchCallback1).call(mErrorCaptor.capture());
+        assertNull(mErrorCaptor.getValue());
+
+        // Should match, the record type is equal in case-insensitive manner.
+        NdefScanOptions options2 = createNdefScanOptions();
+        options2.recordType = "aBc.com:xyZ";
+        int watchId2 = mNextWatchId++;
+        WatchResponse mockWatchCallback2 = mock(WatchResponse.class);
+        nfc.watch(options2, watchId2, mockWatchCallback2);
+        verify(mockWatchCallback2).call(mErrorCaptor.capture());
+        assertNull(mErrorCaptor.getValue());
+
+        // Should not match, the record type is NOT equal even in case-insensitive manner.
+        NdefScanOptions options3 = createNdefScanOptions();
+        options3.recordType = "abcd.com:xyz";
+        int watchId3 = mNextWatchId++;
+        WatchResponse mockWatchCallback3 = mock(WatchResponse.class);
+        nfc.watch(options3, watchId3, mockWatchCallback3);
+        verify(mockWatchCallback3).call(mErrorCaptor.capture());
+        assertNull(mErrorCaptor.getValue());
+
+        nfc.processPendingOperationsForTesting(mNfcTagHandler);
+
+        // Check that client was notified and watch with correct id was triggered.
+        verify(mNfcClient, times(1))
+                .onWatch(mOnWatchCallbackCaptor.capture(), nullable(String.class),
+                        any(NdefMessage.class));
+        assertEquals(2, mOnWatchCallbackCaptor.getValue().length);
+        assertEquals(watchId1, mOnWatchCallbackCaptor.getValue()[0]);
+        assertEquals(watchId2, mOnWatchCallbackCaptor.getValue()[1]);
+    }
+
+    /**
      * Test that Nfc.watch() can be cancelled with Nfc.cancelWatch().
      */
     @Test
@@ -1093,6 +1212,7 @@
 
         // Create message with empty record.
         NdefRecord emptyNdefRecord = new NdefRecord();
+        emptyNdefRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         emptyNdefRecord.recordType = NdefMessageUtils.RECORD_TYPE_EMPTY;
         NdefMessage ndefMessage = createMojoNdefMessage(emptyNdefRecord);
 
@@ -1123,6 +1243,7 @@
         message.data = new NdefRecord[1];
 
         NdefRecord nfcRecord = new NdefRecord();
+        nfcRecord.category = NdefRecordTypeCategory.STANDARDIZED;
         nfcRecord.recordType = NdefMessageUtils.RECORD_TYPE_TEXT;
         nfcRecord.encoding = ENCODING_UTF8;
         nfcRecord.lang = LANG_EN_US;
diff --git a/services/device/public/mojom/nfc.mojom b/services/device/public/mojom/nfc.mojom
index ae6c439..0fd7d21 100644
--- a/services/device/public/mojom/nfc.mojom
+++ b/services/device/public/mojom/nfc.mojom
@@ -29,13 +29,36 @@
   ANY
 };
 
+// https://w3c.github.io/web-nfc/#the-record-type-string
+enum NDEFRecordTypeCategory {
+  // Standardized well known types, including "empty", "text", "url",
+  // "smart-poster", "absolute-url", "mime", and "unknown", etc.
+  // https://w3c.github.io/web-nfc/#dfn-well-known-type-name
+  kStandardized,
+
+  // External types that follow the rule defined by
+  // https://w3c.github.io/web-nfc/#dfn-external-type-name.
+  kExternal,
+
+  // Local types that follow the rule defined by
+  // https://w3c.github.io/web-nfc/#dfn-local-type-name.
+  kLocal
+};
+
 struct NDEFError {
   NDEFErrorType error_type;
 };
 
 // https://w3c.github.io/web-nfc/#dom-ndefrecord
 struct NDEFRecord {
+  // The category |record_type| belongs to. This field is not exposed to JS, but
+  // is used to transfer information internally and make code more readable,
+  // given that there're some complex logic around external types and local
+  // types (will be supported in the future).
+  NDEFRecordTypeCategory category;
+
   // The type of NDEFRecord.
+  // https://w3c.github.io/web-nfc/#the-record-type-string
   string record_type;
 
   // Represents the IANA media type of the NDEFRecord data field.
diff --git a/services/network/cors/cors_url_loader_factory.h b/services/network/cors/cors_url_loader_factory.h
index f55ed72a..ebf9480 100644
--- a/services/network/cors/cors_url_loader_factory.h
+++ b/services/network/cors/cors_url_loader_factory.h
@@ -52,7 +52,7 @@
   // URLLoaders.
   void ClearBindings();
 
-  uint32_t process_id() const { return process_id_; }
+  int32_t process_id() const { return process_id_; }
 
   // Set whether the factory allows CORS preflights. See IsSane.
   static void SetAllowExternalPreflightsForTesting(bool allow) {
@@ -91,7 +91,7 @@
 
   // Retained from URLLoaderFactoryParams:
   const bool disable_web_security_;
-  const uint32_t process_id_ = mojom::kInvalidProcessId;
+  const int32_t process_id_ = mojom::kInvalidProcessId;
   const base::Optional<url::Origin> request_initiator_site_lock_;
 
   // Relative order of |network_loader_factory_| and |loaders_| matters -
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index df45e2d..e275ed3 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -572,7 +572,7 @@
 
 void NetworkContext::DestroyURLLoaderFactory(
     cors::CorsURLLoaderFactory* url_loader_factory) {
-  const uint32_t process_id = url_loader_factory->process_id();
+  const int32_t process_id = url_loader_factory->process_id();
 
   auto it = url_loader_factories_.find(url_loader_factory);
   DCHECK(it != url_loader_factories_.end());
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index ef6c0b4..52e713a 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -531,7 +531,7 @@
 }
 
 void NetworkService::SetRawHeadersAccess(
-    uint32_t process_id,
+    int32_t process_id,
     const std::vector<url::Origin>& origins) {
   DCHECK(process_id);
   if (!origins.size()) {
@@ -558,7 +558,7 @@
       net::HttpNetworkSession::NORMAL_SOCKET_POOL, new_limit);
 }
 
-bool NetworkService::HasRawHeadersAccess(uint32_t process_id,
+bool NetworkService::HasRawHeadersAccess(int32_t process_id,
                                          const GURL& resource_url) const {
   // Allow raw headers for browser-initiated requests.
   if (!process_id)
@@ -638,12 +638,12 @@
 }
 #endif
 
-void NetworkService::AddCorbExceptionForPlugin(uint32_t process_id) {
+void NetworkService::AddCorbExceptionForPlugin(int32_t process_id) {
   DCHECK_NE(mojom::kBrowserProcessId, process_id);
   CrossOriginReadBlocking::AddExceptionForPlugin(process_id);
 }
 
-void NetworkService::RemoveCorbExceptionForPlugin(uint32_t process_id) {
+void NetworkService::RemoveCorbExceptionForPlugin(int32_t process_id) {
   DCHECK_NE(mojom::kBrowserProcessId, process_id);
   CrossOriginReadBlocking::RemoveExceptionForPlugin(process_id);
 }
@@ -773,7 +773,7 @@
   // For requests from the same {process_id, routing_id} pair, pick the most
   // important. For ones from the browser, return all of them.
   std::vector<mojom::LoadInfoPtr> infos;
-  std::map<std::pair<uint32_t, uint32_t>, mojom::LoadInfoPtr> frame_infos;
+  std::map<std::pair<int32_t, int32_t>, mojom::LoadInfoPtr> frame_infos;
 
   for (auto* network_context : network_contexts_) {
     for (auto* loader :
@@ -784,7 +784,7 @@
 
       auto process_id = url_loader->GetProcessId();
       auto routing_id = url_loader->GetRenderFrameId();
-      if (routing_id == static_cast<uint32_t>(MSG_ROUTING_NONE)) {
+      if (routing_id == MSG_ROUTING_NONE) {
         // If there is no routing_id, then the browser can't associate this with
         // a page so no need to send.
         continue;
diff --git a/services/network/network_service.h b/services/network/network_service.h
index d35f960d..3a2b6d2 100644
--- a/services/network/network_service.h
+++ b/services/network/network_service.h
@@ -127,7 +127,7 @@
       mojom::HttpAuthStaticParamsPtr http_auth_static_params) override;
   void ConfigureHttpAuthPrefs(
       mojom::HttpAuthDynamicParamsPtr http_auth_dynamic_params) override;
-  void SetRawHeadersAccess(uint32_t process_id,
+  void SetRawHeadersAccess(int32_t process_id,
                            const std::vector<url::Origin>& origins) override;
   void SetMaxConnectionsPerProxy(int32_t max_connections) override;
   void GetNetworkChangeManager(
@@ -152,8 +152,8 @@
 #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
   void SetEncryptionKey(const std::string& encryption_key) override;
 #endif
-  void AddCorbExceptionForPlugin(uint32_t process_id) override;
-  void RemoveCorbExceptionForPlugin(uint32_t process_id) override;
+  void AddCorbExceptionForPlugin(int32_t process_id) override;
+  void RemoveCorbExceptionForPlugin(int32_t process_id) override;
   void OnMemoryPressure(base::MemoryPressureListener::MemoryPressureLevel
                             memory_pressure_level) override;
   void OnPeerToPeerConnectionsCountChange(uint32_t count) override;
@@ -176,7 +176,7 @@
   void OnBeforeURLRequest();
 
   bool quic_disabled() const { return quic_disabled_; }
-  bool HasRawHeadersAccess(uint32_t process_id, const GURL& resource_url) const;
+  bool HasRawHeadersAccess(int32_t process_id, const GURL& resource_url) const;
 
   mojom::NetworkServiceClient* client() {
     return client_.is_bound() ? client_.get() : nullptr;
@@ -299,7 +299,7 @@
 
   // A per-process_id map of origins that are white-listed to allow
   // them to request raw headers for resources they request.
-  std::map<uint32_t, base::flat_set<url::Origin>>
+  std::map<int32_t, base::flat_set<url::Origin>>
       raw_headers_access_origins_by_pid_;
 
   bool quic_disabled_ = false;
diff --git a/services/network/network_service_unittest.cc b/services/network/network_service_unittest.cc
index c18b47f..4b2598355 100644
--- a/services/network/network_service_unittest.cc
+++ b/services/network/network_service_unittest.cc
@@ -1699,7 +1699,7 @@
       : receiver_(this, std::move(receiver)) {}
   ~ClearSiteDataNetworkContextClient() override = default;
 
-  void OnClearSiteData(uint32_t process_id,
+  void OnClearSiteData(int32_t process_id,
                        int32_t routing_id,
                        const GURL& url,
                        const std::string& header_value,
diff --git a/services/network/network_usage_accumulator_unittest.cc b/services/network/network_usage_accumulator_unittest.cc
index 58dc2dd..5e6df9c 100644
--- a/services/network/network_usage_accumulator_unittest.cc
+++ b/services/network/network_usage_accumulator_unittest.cc
@@ -10,8 +10,8 @@
 namespace {
 
 struct BytesTransferredKey {
-  uint32_t process_id;
-  uint32_t routing_id;
+  int32_t process_id;
+  int32_t routing_id;
 };
 
 }  // namespace
@@ -39,7 +39,7 @@
     return nullptr;
   }
 
-  void ClearBytesTransferredForProcess(uint32_t process_id) {
+  void ClearBytesTransferredForProcess(int32_t process_id) {
     network_usage_accumulator_.ClearBytesTransferredForProcess(process_id);
   }
 
diff --git a/services/network/public/cpp/host_resolver_mojom_traits.cc b/services/network/public/cpp/host_resolver_mojom_traits.cc
index f5303393..6211ddc 100644
--- a/services/network/public/cpp/host_resolver_mojom_traits.cc
+++ b/services/network/public/cpp/host_resolver_mojom_traits.cc
@@ -422,16 +422,11 @@
   return false;
 }
 
-// static
 bool StructTraits<
     network::mojom::ResolveErrorInfoDataView,
     net::ResolveErrorInfo>::Read(network::mojom::ResolveErrorInfoDataView data,
                                  net::ResolveErrorInfo* out) {
-  // There should not be a secure network error if the error code indicates no
-  // error.
-  if (data.error() == net::OK && data.is_secure_network_error())
-    return false;
-  *out = net::ResolveErrorInfo(data.error(), data.is_secure_network_error());
+  *out = net::ResolveErrorInfo(data.error());
   return true;
 }
 
diff --git a/services/network/public/cpp/host_resolver_mojom_traits.h b/services/network/public/cpp/host_resolver_mojom_traits.h
index 084ca52..80e1267 100644
--- a/services/network/public/cpp/host_resolver_mojom_traits.h
+++ b/services/network/public/cpp/host_resolver_mojom_traits.h
@@ -131,11 +131,6 @@
     return resolve_error_info.error;
   }
 
-  static bool is_secure_network_error(
-      net::ResolveErrorInfo resolve_error_info) {
-    return resolve_error_info.is_secure_network_error;
-  }
-
   static bool Read(network::mojom::ResolveErrorInfoDataView data,
                    net::ResolveErrorInfo* out);
 };
diff --git a/services/network/public/cpp/net_ipc_param_traits.cc b/services/network/public/cpp/net_ipc_param_traits.cc
index 8e79469..edd0c305 100644
--- a/services/network/public/cpp/net_ipc_param_traits.cc
+++ b/services/network/public/cpp/net_ipc_param_traits.cc
@@ -317,13 +317,11 @@
 void ParamTraits<net::ResolveErrorInfo>::Write(base::Pickle* m,
                                                const param_type& p) {
   WriteParam(m, p.error);
-  WriteParam(m, p.is_secure_network_error);
 }
 bool ParamTraits<net::ResolveErrorInfo>::Read(const base::Pickle* m,
                                               base::PickleIterator* iter,
                                               param_type* r) {
-  return ReadParam(m, iter, &r->error) &&
-         ReadParam(m, iter, &r->is_secure_network_error);
+  return ReadParam(m, iter, &r->error);
 }
 void ParamTraits<net::ResolveErrorInfo>::Log(const param_type& p,
                                              std::string* l) {
diff --git a/services/network/public/mojom/host_resolver.mojom b/services/network/public/mojom/host_resolver.mojom
index 2648dda..04fa980 100644
--- a/services/network/public/mojom/host_resolver.mojom
+++ b/services/network/public/mojom/host_resolver.mojom
@@ -117,11 +117,6 @@
   // Underlying network error code. See net/base/net_error_list.h for error
   // descriptions.
   int32 error;
-
-  // Whether |error| came from a DNS-over-HTTPS lookup. This will be false if
-  // the answer was obtained from the cache or if |error| is net::OK since this
-  // field is intended to identify secure DNS *network* failures.
-  bool is_secure_network_error = false;
 };
 
 // Control handle used to control outstanding NetworkContext::ResolveHost
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index ee5d586..369e6ad 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -490,11 +490,11 @@
 struct NetworkUsage {
   // |process_id| is 0 for the browser process, otherwise it's the child process
   // ID.
-  uint32 process_id;
+  int32 process_id;
 
   // The ID of the IPC route for the consumer, which identifies the RenderFrame
   // or like-thing.
-  uint32 routing_id;
+  int32 routing_id;
 
   // Raw bytes received from the network since the start/restart of the service.
   int64 total_bytes_received;
@@ -519,8 +519,8 @@
   mojo_base.mojom.TimeDelta elapsed_time;
 };
 
-const uint32 kBrowserProcessId = 0;
-const uint32 kInvalidProcessId = 0xffffffff;
+const int32 kBrowserProcessId = 0;
+const int32 kInvalidProcessId = -1;
 
 // URLLoaderFactory made by the network service consists of two layers of
 // factories, the "internal" one that directly talks to the //net layer, and
@@ -547,7 +547,7 @@
 struct URLLoaderFactoryParams {
   // Process requesting the URLLoaderFactory.
   // Set to kBrowserProcessId to indicate the browser process.
-  uint32 process_id = kInvalidProcessId;
+  int32 process_id = kInvalidProcessId;
 
   // If specified, then |request_initiator_site_lock| locks
   // |ResourceRequest::request_initiator| to the specified origin.
@@ -670,8 +670,8 @@
   // network::ResourceRequest::fetch_window_id.
   OnAuthRequired(
       mojo_base.mojom.UnguessableToken? window_id,
-      uint32 process_id,
-      uint32 routing_id,
+      int32 process_id,
+      int32 routing_id,
       uint32 request_id,
       url.mojom.Url url,
       bool first_auth_attempt,
@@ -689,8 +689,8 @@
   // making the request, see network::ResourceRequest::fetch_window_id.
   OnCertificateRequested(
       mojo_base.mojom.UnguessableToken? window_id,
-      uint32 process_id,
-      uint32 routing_id,
+      int32 process_id,
+      int32 routing_id,
       uint32 request_id,
       network.mojom.SSLCertRequestInfo cert_info,
       pending_remote<ClientCertificateResponder> cert_responder);
@@ -698,8 +698,8 @@
   // Called when an SSL certificate is encountered.
   // The callback argument is a net::ERROR value. If it's net::OK, then the
   // request is resumed. Otherwise it's cancelled with the given error.
-  OnSSLCertificateError(uint32 process_id,
-                        uint32 routing_id,
+  OnSSLCertificateError(int32 process_id,
+                        int32 routing_id,
                         url.mojom.Url url,
                         int32 net_error,
                         SSLInfo ssl_info,
@@ -713,7 +713,7 @@
   // If any files referenced by |file_paths| cannot be read, a net::ERROR will
   // be returned, and |files| will be an empty list. If the |async| parameter
   // is true, the files will be opened with FLAG_ASYNC.
-  OnFileUploadRequested(uint32 process_id,
+  OnFileUploadRequested(int32 process_id,
                         bool async,
                         array<mojo_base.mojom.FilePath> file_paths) =>
                         (int32 net_error, array<mojo_base.mojom.File> files);
@@ -731,7 +731,7 @@
   // https://www.w3.org/TR/clear-site-data/
   // TODO(crbug.com/876931): We might want to move header parsing work to
   // Network Service for security concerns (e.g. |header_value| => booleans).
-  OnClearSiteData(uint32 process_id,
+  OnClearSiteData(int32 process_id,
                   int32 routing_id,
                   url.mojom.Url url,
                   string header_value,
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index 088cd7c..66417bb 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -31,8 +31,8 @@
 import "mojo/public/mojom/base/application_state.mojom";
 
 struct LoadInfo {
-  uint32 process_id;
-  uint32 routing_id;
+  int32 process_id;
+  int32 routing_id;
   string host;
   uint32 load_state;  // net::LoadState enum
   mojo_base.mojom.String16 state_param;
@@ -247,7 +247,7 @@
   // Granting a permission increases risks in case the child process becomes
   // compromised, so this should be done only in specific cases
   // (e.g. DevTools attached).
-  SetRawHeadersAccess(uint32 process_id, array<url.mojom.Origin> origins);
+  SetRawHeadersAccess(int32 process_id, array<url.mojom.Origin> origins);
 
   // Sets the maximum number of connections for a proxy server.
   //   * Negative values will set the default proxy connection limit (32)
@@ -305,10 +305,10 @@
   // TODO(lukasza, laforge): https://crbug.com/702995: Remove the ...ForPlugin
   // methods once Flash support is removed from Chromium (probably around 2020
   // - see https://www.chromium.org/flash-roadmap).
-  AddCorbExceptionForPlugin(uint32 process_id);
+  AddCorbExceptionForPlugin(int32 process_id);
 
   // Reverts AddCorbExceptionForPlugin.
-  RemoveCorbExceptionForPlugin(uint32 process_id);
+  RemoveCorbExceptionForPlugin(int32 process_id);
 
   // Called when the system is low on memory.
   OnMemoryPressure(mojo_base.mojom.MemoryPressureLevel memory_pressure_level);
diff --git a/services/network/test/test_network_context_client.cc b/services/network/test/test_network_context_client.cc
index 011d717..e4a3b512 100644
--- a/services/network/test/test_network_context_client.cc
+++ b/services/network/test/test_network_context_client.cc
@@ -22,7 +22,7 @@
 TestNetworkContextClient::~TestNetworkContextClient() {}
 
 void TestNetworkContextClient::OnFileUploadRequested(
-    uint32_t process_id,
+    int32_t process_id,
     bool async,
     const std::vector<base::FilePath>& file_paths,
     OnFileUploadRequestedCallback callback) {
diff --git a/services/network/test/test_network_context_client.h b/services/network/test/test_network_context_client.h
index 8f7fd0a..45dc9f8 100644
--- a/services/network/test/test_network_context_client.h
+++ b/services/network/test/test_network_context_client.h
@@ -29,8 +29,8 @@
   }
 
   void OnAuthRequired(const base::Optional<base::UnguessableToken>& window_id,
-                      uint32_t process_id,
-                      uint32_t routing_id,
+                      int32_t process_id,
+                      int32_t routing_id,
                       uint32_t request_id,
                       const GURL& url,
                       bool first_auth_attempt,
@@ -40,20 +40,20 @@
                           auth_challenge_responder) override {}
   void OnCertificateRequested(
       const base::Optional<base::UnguessableToken>& window_id,
-      uint32_t process_id,
-      uint32_t routing_id,
+      int32_t process_id,
+      int32_t routing_id,
       uint32_t request_id,
       const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
       mojo::PendingRemote<mojom::ClientCertificateResponder>
           client_cert_responder) override {}
-  void OnSSLCertificateError(uint32_t process_id,
-                             uint32_t routing_id,
+  void OnSSLCertificateError(int32_t process_id,
+                             int32_t routing_id,
                              const GURL& url,
                              int net_error,
                              const net::SSLInfo& ssl_info,
                              bool fatal,
                              OnSSLCertificateErrorCallback response) override {}
-  void OnFileUploadRequested(uint32_t process_id,
+  void OnFileUploadRequested(int32_t process_id,
                              bool async,
                              const std::vector<base::FilePath>& file_paths,
                              OnFileUploadRequestedCallback callback) override;
@@ -63,7 +63,7 @@
   void OnCanSendDomainReliabilityUpload(
       const GURL& origin,
       OnCanSendDomainReliabilityUploadCallback callback) override {}
-  void OnClearSiteData(uint32_t process_id,
+  void OnClearSiteData(int32_t process_id,
                        int32_t routing_id,
                        const GURL& url,
                        const std::string& header_value,
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 44ba9000..98f7fd4 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -511,7 +511,7 @@
 
   FileOpenerForUpload(std::vector<base::FilePath> paths,
                       URLLoader* url_loader,
-                      uint32_t process_id,
+                      int32_t process_id,
                       mojom::NetworkContextClient* const network_context_client,
                       SetUpUploadCallback set_up_upload_callback)
       : paths_(std::move(paths)),
@@ -595,7 +595,7 @@
   // The paths of files for upload
   const std::vector<base::FilePath> paths_;
   URLLoader* const url_loader_;
-  const uint32_t process_id_;
+  const int32_t process_id_;
   mojom::NetworkContextClient* const network_context_client_;
   SetUpUploadCallback set_up_upload_callback_;
   // The files opened so far.
@@ -1256,11 +1256,11 @@
   return url_request_->GetLoadState().state;
 }
 
-uint32_t URLLoader::GetRenderFrameId() const {
+int32_t URLLoader::GetRenderFrameId() const {
   return render_frame_id_;
 }
 
-uint32_t URLLoader::GetProcessId() const {
+int32_t URLLoader::GetProcessId() const {
   return factory_params_->process_id;
 }
 
diff --git a/services/network/url_loader.h b/services/network/url_loader.h
index f56c990..532ecdc 100644
--- a/services/network/url_loader.h
+++ b/services/network/url_loader.h
@@ -138,8 +138,8 @@
 
   net::LoadState GetLoadStateForTesting() const;
 
-  uint32_t GetRenderFrameId() const;
-  uint32_t GetProcessId() const;
+  int32_t GetRenderFrameId() const;
+  int32_t GetProcessId() const;
   uint32_t GetResourceType() const;
 
   // Whether this URLLoader should allow sending/setting cookies for requests
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc
index 2adfb166..9ee1de8 100644
--- a/services/network/url_loader_unittest.cc
+++ b/services/network/url_loader_unittest.cc
@@ -1579,7 +1579,7 @@
 
 class CallbackSavingNetworkContextClient : public TestNetworkContextClient {
  public:
-  void OnFileUploadRequested(uint32_t process_id,
+  void OnFileUploadRequested(int32_t process_id,
                              bool async,
                              const std::vector<base::FilePath>& file_paths,
                              OnFileUploadRequestedCallback callback) override {
@@ -2679,8 +2679,8 @@
   ~MockNetworkContextClient() override = default;
 
   void OnAuthRequired(const base::Optional<base::UnguessableToken>& window_id,
-                      uint32_t process_id,
-                      uint32_t routing_id,
+                      int32_t process_id,
+                      int32_t routing_id,
                       uint32_t request_id,
                       const GURL& url,
                       bool first_auth_attempt,
@@ -2713,8 +2713,8 @@
 
   void OnCertificateRequested(
       const base::Optional<base::UnguessableToken>& window_id,
-      uint32_t process_id,
-      uint32_t routing_id,
+      int32_t process_id,
+      int32_t routing_id,
       uint32_t request_id,
       const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
       mojo::PendingRemote<mojom::ClientCertificateResponder>
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index 0e4de3a..653c9f2 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -229,8 +229,9 @@
   // See comments on ImageDecoder::max_decoded_bytes_.
   virtual size_t MaxDecodedImageBytes() { return kNoDecodedImageByteLimit; }
 
-  // Returns true if this is a low-end device.
-  // This is the same as base::SysInfo::IsLowEndDevice.
+  // See: SysUtils::IsLowEndDevice for the full details of what "low-end" means.
+  // This returns true for devices that can use more extreme tradeoffs for
+  // performance. Many low memory devices (<=1GB) are not considered low-end.
   virtual bool IsLowEndDevice() { return false; }
 
   // Process -------------------------------------------------------------
diff --git a/third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc b/third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc
index 7a5b86f..00aacbd 100644
--- a/third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc
+++ b/third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc
@@ -269,11 +269,11 @@
   }
 
   // Search named items in the document.
-  Document* doc = To<LocalFrame>(frame)->GetDocument();
-  if (!doc || !doc->IsHTMLDocument())
+  auto* doc = DynamicTo<HTMLDocument>(To<LocalFrame>(frame)->GetDocument());
+  if (!doc)
     return;
 
-  bool has_named_item = ToHTMLDocument(doc)->HasNamedItem(name);
+  bool has_named_item = doc->HasNamedItem(name);
   bool has_id_item = doc->HasElementWithId(name);
 
   if (!has_named_item && !has_id_item)
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_context_snapshot.cc b/third_party/blink/renderer/bindings/core/v8/v8_context_snapshot.cc
index 37e233d..d17aca6 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_context_snapshot.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_context_snapshot.cc
@@ -209,7 +209,7 @@
   // The below code handles window.document on the main world.
   {
     CHECK(document);
-    DCHECK(document->IsHTMLDocument());
+    DCHECK(IsA<HTMLDocument>(document));
     CHECK(document->ContainsWrapper());
     v8::Local<v8::Object> document_wrapper =
         ToV8(document, global_proxy, isolate).As<v8::Object>();
@@ -426,7 +426,7 @@
   // When creating a context for the main world from snapshot, we also need a
   // HTMLDocument instance. If typeof window.document is not HTMLDocument, e.g.
   // SVGDocument or XMLDocument, we can't create contexts from the snapshot.
-  return !world.IsMainWorld() || document->IsHTMLDocument();
+  return !world.IsMainWorld() || IsA<HTMLDocument>(document);
 }
 
 void V8ContextSnapshot::EnsureInterfaceTemplatesForWorld(
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
index 8b8768a..c70bfec 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
@@ -94,6 +94,8 @@
         self._propagate_extattrs_to_overload_group()
         self._calculate_group_exposure()
 
+        self._fill_exposed_constructs()
+
         self._sort_dictionary_members()
 
         # Updates on IRs are finished.  Create API objects.
@@ -420,6 +422,32 @@
                         ]))
                     group.exposure.set_only_in_secure_contexts(flag_names)
 
+    def _fill_exposed_constructs(self):
+        old_interfaces = self._ir_map.irs_of_kind(IRMap.IR.Kind.INTERFACE)
+        old_namespaces = self._ir_map.irs_of_kind(IRMap.IR.Kind.NAMESPACE)
+
+        exposed_map = {}  # global name: [construct's identifier...]
+        for ir in itertools.chain(old_interfaces, old_namespaces):
+            for pair in ir.exposure.global_names_and_features:
+                exposed_map.setdefault(pair.global_name,
+                                       []).append(ir.identifier)
+
+        self._ir_map.move_to_new_phase()
+
+        for old_ir in old_interfaces:
+            new_ir = make_copy(old_ir)
+            self._ir_map.add(new_ir)
+
+            assert not new_ir.exposed_constructs
+            global_names = new_ir.extended_attributes.values_of("Global")
+            if not global_names:
+                continue
+            constructs = set()
+            for global_name in global_names:
+                constructs.update(exposed_map.get(global_name, []))
+            new_ir.exposed_constructs = map(
+                self._ref_to_idl_def_factory.create, sorted(constructs))
+
     def _sort_dictionary_members(self):
         """Sorts dictionary members in alphabetical order."""
         old_irs = self._ir_map.irs_of_kind(IRMap.IR.Kind.DICTIONARY)
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/interface.py b/third_party/blink/renderer/bindings/scripts/web_idl/interface.py
index 3a9922615..25ec9011 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/interface.py
@@ -102,6 +102,7 @@
             self.constructor_groups = []
             self.operations = list(operations)
             self.operation_groups = []
+            self.exposed_constructs = []
             self.stringifier = stringifier
             self.iterable = iterable
             self.maplike = maplike
@@ -162,6 +163,7 @@
                        self._operations),
                 owner=self) for operation_group_ir in ir.operation_groups
         ])
+        self._exposed_constructs = tuple(ir.exposed_constructs)
         self._stringifier = None
         if ir.stringifier:
             operations = filter(lambda x: x.is_stringifier, self._operations)
@@ -265,20 +267,19 @@
         return self._operation_groups
 
     @property
+    def exposed_constructs(self):
+        """
+        Returns a list of the constructs that are exposed on this global object.
+        """
+        return tuple(
+            map(lambda ref: ref.target_object, self._exposed_constructs))
+
+    @property
     def named_constructor(self):
         """Returns a named constructor or None."""
         assert False, "Not implemented yet."
 
     @property
-    def exposed_interfaces(self):
-        """
-        Returns a tuple of interfaces that are exposed to this interface, if
-        this is a global interface.  Returns None otherwise.
-        """
-        assert False, "Not implemented yet."
-
-    # Special operations
-    @property
     def indexed_property_handler(self):
         """
         Returns a set of handlers (getter/setter/deleter) for the indexed
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/ir_map.py b/third_party/blink/renderer/bindings/scripts/web_idl/ir_map.py
index 4651900f8..f95cbd9 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/ir_map.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/ir_map.py
@@ -194,7 +194,7 @@
                 accumulated.extend(irs)
             return accumulated
         else:
-            return self.find_by_kind(kind).itervalues()
+            return list(self.find_by_kind(kind).itervalues())
 
     def irs_of_kinds(self, *kinds):
         """
diff --git a/third_party/blink/renderer/core/animation/compositor_animations_test.cc b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
index f07ac5b..e05f7da7 100644
--- a/third_party/blink/renderer/core/animation/compositor_animations_test.cc
+++ b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
@@ -466,7 +466,7 @@
     DCHECK_EQ(keyframe_timing_function->GetType(),
               TimingFunction::Type::CUBIC_BEZIER);
     const auto& cubic_timing_function =
-        ToCubicBezierTimingFunction(*keyframe_timing_function);
+        To<CubicBezierTimingFunction>(*keyframe_timing_function);
     EXPECT_EQ(cubic_timing_function.GetEaseType(), ease_type);
   }
 
@@ -1573,7 +1573,7 @@
   EXPECT_EQ(curve_timing_function->GetType(),
             TimingFunction::Type::CUBIC_BEZIER);
   const auto& cubic_timing_function =
-      ToCubicBezierTimingFunction(*curve_timing_function);
+      To<CubicBezierTimingFunction>(*curve_timing_function);
   EXPECT_EQ(cubic_timing_function.GetEaseType(),
             CubicBezierTimingFunction::EaseType::CUSTOM);
   EXPECT_EQ(cubic_timing_function.X1(), 1.0);
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_context.cc b/third_party/blink/renderer/core/css/parser/css_parser_context.cc
index 8f0f746..b99070e 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_context.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_context.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/deprecation.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/imports/html_imports_controller.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/page/page.h"
@@ -118,7 +119,7 @@
           profile,
           Referrer(base_url_override.StrippedForUseAsReferrer(),
                    referrer_policy_override),
-          document.IsHTMLDocument(),
+          IsA<HTMLDocument>(document),
           document.GetSettings()
               ? document.GetSettings()
                     ->GetUseLegacyBackgroundSizeShorthandBehavior()
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index 8cbc4df..ddb858a 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -1617,8 +1617,8 @@
     const TimingFunction* timing_function) {
   switch (timing_function->GetType()) {
     case TimingFunction::Type::CUBIC_BEZIER: {
-      const CubicBezierTimingFunction* bezier_timing_function =
-          ToCubicBezierTimingFunction(timing_function);
+      const auto* bezier_timing_function =
+          To<CubicBezierTimingFunction>(timing_function);
       if (bezier_timing_function->GetEaseType() !=
           CubicBezierTimingFunction::EaseType::CUSTOM) {
         CSSValueID value_id = CSSValueID::kInvalid;
@@ -1647,8 +1647,8 @@
     }
 
     case TimingFunction::Type::STEPS: {
-      const StepsTimingFunction* steps_timing_function =
-          ToStepsTimingFunction(timing_function);
+      const auto* steps_timing_function =
+          To<StepsTimingFunction>(timing_function);
       StepsTimingFunction::StepPosition position =
           steps_timing_function->GetStepPosition();
       int steps = steps_timing_function->NumberOfSteps();
diff --git a/third_party/blink/renderer/core/css/selector_checker.cc b/third_party/blink/renderer/core/css/selector_checker.cc
index 8227d41..8a98fce 100644
--- a/third_party/blink/renderer/core/css/selector_checker.cc
+++ b/third_party/blink/renderer/core/css/selector_checker.cc
@@ -101,7 +101,7 @@
   const AtomicString& local_name = tag_q_name.LocalName();
   if (local_name != CSSSelector::UniversalSelectorAtom() &&
       local_name != element.localName()) {
-    if (element.IsHTMLElement() || !element.GetDocument().IsHTMLDocument())
+    if (element.IsHTMLElement() || !IsA<HTMLDocument>(element.GetDocument()))
       return false;
     // Non-html elements in html documents are normalized to their camel-cased
     // version during parsing if applicable. Yet, type selectors are lower-cased
@@ -132,7 +132,7 @@
 
   if (local_name != CSSSelector::UniversalSelectorAtom() &&
       local_name != element.localName()) {
-    if (element.IsHTMLElement() || !element.GetDocument().IsHTMLDocument())
+    if (element.IsHTMLElement() || !IsA<HTMLDocument>(element.GetDocument()))
       return false;
 
     // Non-html elements in html documents are normalized to their camel-cased
@@ -773,7 +773,7 @@
   AttributeCollection attributes = element.AttributesWithoutUpdate();
   for (const auto& attribute_item : attributes) {
     if (!attribute_item.Matches(selector_attr)) {
-      if (element.IsHTMLElement() || !element.GetDocument().IsHTMLDocument())
+      if (element.IsHTMLElement() || !IsA<HTMLDocument>(element.GetDocument()))
         continue;
       // Non-html attributes in html documents are normalized to their camel-
       // cased version during parsing if applicable. Yet, attribute selectors
@@ -798,7 +798,7 @@
     // a case-insensitive manner regardless of whether the case insensitive
     // flag is set or not.
     bool legacy_case_insensitive =
-        element.GetDocument().IsHTMLDocument() &&
+        IsA<HTMLDocument>(element.GetDocument()) &&
         !HTMLDocument::IsCaseSensitiveAttribute(selector_attr);
 
     // If case-insensitive, re-check, and count if result differs.
@@ -1295,7 +1295,7 @@
       return MatchesSpatialNavigationInterestPseudoClass(element);
     case CSSSelector::kPseudoIsHtml:
       DCHECK(is_ua_rule_);
-      return element.GetDocument().IsHTMLDocument();
+      return IsA<HTMLDocument>(element.GetDocument());
     case CSSSelector::kPseudoListBox:
       DCHECK(is_ua_rule_);
       return MatchesListBoxPseudoClass(element);
diff --git a/third_party/blink/renderer/core/css/selector_query.cc b/third_party/blink/renderer/core/css/selector_query.cc
index 39140ed8..722b751f 100644
--- a/third_party/blink/renderer/core/css/selector_query.cc
+++ b/third_party/blink/renderer/core/css/selector_query.cc
@@ -38,6 +38,7 @@
 #include "third_party/blink/renderer/core/dom/nth_index_cache.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
 #include "third_party/blink/renderer/core/dom/static_node_list.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
@@ -172,7 +173,7 @@
   // version during parsing if applicable. Yet, type selectors are lower-cased
   // for selectors in html documents. Compare the upper case converted names
   // instead to allow matching SVG elements like foreignObject.
-  if (!element.IsHTMLElement() && element.GetDocument().IsHTMLDocument())
+  if (!element.IsHTMLElement() && IsA<HTMLDocument>(element.GetDocument()))
     return element.TagQName().LocalNameUpper() == tag_name.LocalNameUpper();
   return false;
 }
diff --git a/third_party/blink/renderer/core/css/style_sheet_candidate.cc b/third_party/blink/renderer/core/css/style_sheet_candidate.cc
index 644416a..6336519 100644
--- a/third_party/blink/renderer/core/css/style_sheet_candidate.cc
+++ b/third_party/blink/renderer/core/css/style_sheet_candidate.cc
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/processing_instruction.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_link_element.h"
 #include "third_party/blink/renderer/core/html/html_style_element.h"
 #include "third_party/blink/renderer/core/html/imports/html_import.h"
@@ -44,7 +45,7 @@
 }
 
 bool StyleSheetCandidate::IsXSL() const {
-  return !GetNode().GetDocument().IsHTMLDocument() && type_ == kPi &&
+  return !IsA<HTMLDocument>(GetNode().GetDocument()) && type_ == kPi &&
          To<ProcessingInstruction>(GetNode()).IsXSL();
 }
 
diff --git a/third_party/blink/renderer/core/dom/container_node.cc b/third_party/blink/renderer/core/dom/container_node.cc
index a83ce2b..3fae6f3 100644
--- a/third_party/blink/renderer/core/dom/container_node.cc
+++ b/third_party/blink/renderer/core/dom/container_node.cc
@@ -47,6 +47,7 @@
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/html/forms/radio_node_list.h"
 #include "third_party/blink/renderer/core/html/html_collection.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/html/html_tag_collection.h"
 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
@@ -1493,7 +1494,7 @@
     const AtomicString& qualified_name) {
   DCHECK(!qualified_name.IsNull());
 
-  if (GetDocument().IsHTMLDocument()) {
+  if (IsA<HTMLDocument>(GetDocument())) {
     return EnsureCachedCollection<HTMLTagCollection>(kHTMLTagCollectionType,
                                                      qualified_name);
   }
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 7d154ea..70577a1d 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -1404,7 +1404,7 @@
   // documents there may never be a <body> (since the parser won't always
   // insert one), so we resume here too. That does mean XHTML documents make
   // frames when there's only a <head>, but such documents are pretty rare.
-  if (document_element_ && !IsHTMLDocument())
+  if (document_element_ && !IsA<HTMLDocument>(this))
     BeginLifecycleUpdatesIfRenderingReady();
 }
 
@@ -1421,7 +1421,7 @@
 }
 
 AtomicString Document::ConvertLocalName(const AtomicString& name) {
-  return IsHTMLDocument() ? name.LowerASCII() : name;
+  return IsA<HTMLDocument>(this) ? name.LowerASCII() : name;
 }
 
 // Just creates an element with specified qualified name without any
@@ -1480,7 +1480,7 @@
     return nullptr;
   }
 
-  if (IsXHTMLDocument() || IsHTMLDocument()) {
+  if (IsXHTMLDocument() || IsA<HTMLDocument>(this)) {
     // 2. If the context object is an HTML document, let localName be
     // converted to ASCII lowercase.
     AtomicString local_name = ConvertLocalName(name);
@@ -1545,7 +1545,7 @@
   // 2. localName converted to ASCII lowercase
   const AtomicString& converted_local_name = ConvertLocalName(local_name);
   QualifiedName q_name(g_null_atom, converted_local_name,
-                       IsXHTMLDocument() || IsHTMLDocument()
+                       IsXHTMLDocument() || IsA<HTMLDocument>(this)
                            ? html_names::xhtmlNamespaceURI
                            : g_null_atom);
 
@@ -1789,7 +1789,7 @@
 
 CDATASection* Document::createCDATASection(const String& data,
                                            ExceptionState& exception_state) {
-  if (IsHTMLDocument()) {
+  if (IsA<HTMLDocument>(this)) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kNotSupportedError,
         "This operation is not supported for HTML documents.");
@@ -1821,7 +1821,7 @@
         "The data provided ('" + data + "') contains '?>'.");
     return nullptr;
   }
-  if (IsHTMLDocument()) {
+  if (IsA<HTMLDocument>(this)) {
     UseCounter::Count(*this,
                       WebFeature::kHTMLDocumentCreateProcessingInstruction);
   }
@@ -2049,7 +2049,7 @@
   }
   if (xmlStandalone())
     return "text/xml";
-  if (IsHTMLDocument())
+  if (IsA<HTMLDocument>(this))
     return "text/html";
 
   if (DocumentLoader* document_loader = Loader())
@@ -2247,7 +2247,7 @@
   title_element_ = nullptr;
 
   // Update title based on first title element in the document, if one exists.
-  if (IsHTMLDocument() || IsXHTMLDocument()) {
+  if (IsA<HTMLDocument>(this) || IsXHTMLDocument()) {
     if (HTMLTitleElement* title =
             Traversal<HTMLTitleElement>::FirstWithin(*this))
       SetTitleElement(title);
@@ -3618,8 +3618,8 @@
 }
 
 DocumentParser* Document::CreateParser() {
-  if (IsHTMLDocument()) {
-    return MakeGarbageCollected<HTMLDocumentParser>(ToHTMLDocument(*this),
+  if (auto* html_document = DynamicTo<HTMLDocument>(this)) {
+    return MakeGarbageCollected<HTMLDocumentParser>(*html_document,
                                                     parser_sync_policy_);
   }
   // FIXME: this should probably pass the frame instead
@@ -3627,7 +3627,7 @@
 }
 
 bool Document::IsFrameSet() const {
-  if (!IsHTMLDocument())
+  if (!IsA<HTMLDocument>(this))
     return false;
   return IsA<HTMLFrameSetElement>(body());
 }
@@ -3680,7 +3680,7 @@
 
   // If |document| is an XML document, then throw an "InvalidStateError"
   // DOMException exception.
-  if (!IsHTMLDocument()) {
+  if (!IsA<HTMLDocument>(this)) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "Only HTML documents support open().");
     return;
@@ -4012,7 +4012,7 @@
 
   // If the Document object is an XML document, then throw an
   // "InvalidStateError" DOMException.
-  if (!IsHTMLDocument()) {
+  if (!IsA<HTMLDocument>(this)) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "Only HTML documents support close().");
     return;
@@ -4489,7 +4489,7 @@
     return;
   }
 
-  if (!IsHTMLDocument()) {
+  if (!IsA<HTMLDocument>(this)) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "Only HTML documents support write().");
     return;
@@ -4868,11 +4868,11 @@
       WTF::Bind(&Document::ExecuteScriptsWaitingForResources,
                 WrapWeakPersistent(this)));
 
-  if (IsHTMLDocument() && body()) {
+  if (IsA<HTMLDocument>(this) && body()) {
     // For HTML if we have no more stylesheets to load and we're past the body
     // tag, we should have something to paint so resume.
     BeginLifecycleUpdatesIfRenderingReady();
-  } else if (!IsHTMLDocument() && documentElement()) {
+  } else if (!IsA<HTMLDocument>(this) && documentElement()) {
     // For non-HTML there is no body so resume as soon as the sheets are loaded.
     BeginLifecycleUpdatesIfRenderingReady();
   }
@@ -7529,7 +7529,7 @@
   if (plugin_document && !node) {
     node = plugin_document->PluginNode();
   }
-  if (!node && doc->IsHTMLDocument())
+  if (!node && IsA<HTMLDocument>(doc))
     node = doc->body();
   if (!node)
     node = doc->documentElement();
@@ -7741,7 +7741,7 @@
   if (template_document_)
     return *template_document_;
 
-  if (IsHTMLDocument()) {
+  if (IsA<HTMLDocument>(this)) {
     template_document_ = MakeGarbageCollected<HTMLDocument>(
         DocumentInit::Create()
             .WithContextDocument(ContextDocument())
diff --git a/third_party/blink/renderer/core/dom/document_init.cc b/third_party/blink/renderer/core/dom/document_init.cc
index 09e4240..25a96b4 100644
--- a/third_party/blink/renderer/core/dom/document_init.cc
+++ b/third_party/blink/renderer/core/dom/document_init.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/html/custom/v0_custom_element_registration_context.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/html/imports/html_imports_controller.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
@@ -239,7 +240,7 @@
 
 V0CustomElementRegistrationContext* DocumentInit::RegistrationContext(
     Document* document) const {
-  if (!document->IsHTMLDocument() && !document->IsXHTMLDocument())
+  if (!IsA<HTMLDocument>(document) && !document->IsXHTMLDocument())
     return nullptr;
 
   if (create_new_registration_context_)
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 2c38e5b..7d3a4db 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -990,8 +990,8 @@
 }
 
 AtomicString Element::LowercaseIfNecessary(const AtomicString& name) const {
-  return IsHTMLElement() && GetDocument().IsHTMLDocument() ? name.LowerASCII()
-                                                           : name;
+  return IsHTMLElement() && IsA<HTMLDocument>(GetDocument()) ? name.LowerASCII()
+                                                             : name;
 }
 
 const AtomicString& Element::nonce() const {
@@ -2684,7 +2684,7 @@
 }
 
 AtomicString Element::LocalNameForSelectorMatching() const {
-  if (IsHTMLElement() || !GetDocument().IsHTMLDocument())
+  if (IsHTMLElement() || !IsA<HTMLDocument>(GetDocument()))
     return localName();
   return localName().LowerASCII();
 }
@@ -3862,7 +3862,7 @@
     return nullptr;
   }
 
-  if (!IsHTMLElement() && attr_node->GetDocument().IsHTMLDocument() &&
+  if (!IsHTMLElement() && IsA<HTMLDocument>(attr_node->GetDocument()) &&
       attr_node->name() != attr_node->name().LowerASCII())
     UseCounter::Count(
         GetDocument(),
@@ -4863,7 +4863,7 @@
   // Step 2 of http://domparsing.spec.whatwg.org/#insertadjacenthtml()
   Element* context_element;
   if (!IsA<Element>(context_node) ||
-      (context_node->GetDocument().IsHTMLDocument() &&
+      (IsA<HTMLDocument>(context_node->GetDocument()) &&
        IsA<HTMLHtmlElement>(context_node))) {
     context_element =
         MakeGarbageCollected<HTMLBodyElement>(context_node->GetDocument());
@@ -5823,23 +5823,23 @@
 void Element::UpdateNamedItemRegistration(NamedItemType type,
                                           const AtomicString& old_name,
                                           const AtomicString& new_name) {
-  if (!GetDocument().IsHTMLDocument())
+  auto* doc = DynamicTo<HTMLDocument>(GetDocument());
+  if (!doc)
     return;
-  HTMLDocument& doc = ToHTMLDocument(GetDocument());
 
   if (!old_name.IsEmpty())
-    doc.RemoveNamedItem(old_name);
+    doc->RemoveNamedItem(old_name);
 
   if (!new_name.IsEmpty())
-    doc.AddNamedItem(new_name);
+    doc->AddNamedItem(new_name);
 
   if (type == NamedItemType::kNameOrIdWithName) {
     const AtomicString id = GetIdAttribute();
     if (!id.IsEmpty()) {
       if (!old_name.IsEmpty() && new_name.IsEmpty())
-        doc.RemoveNamedItem(id);
+        doc->RemoveNamedItem(id);
       else if (old_name.IsEmpty() && !new_name.IsEmpty())
-        doc.AddNamedItem(id);
+        doc->AddNamedItem(id);
     }
   }
 }
@@ -5847,17 +5847,18 @@
 void Element::UpdateIdNamedItemRegistration(NamedItemType type,
                                             const AtomicString& old_id,
                                             const AtomicString& new_id) {
-  if (!GetDocument().IsHTMLDocument())
+  auto* doc = DynamicTo<HTMLDocument>(GetDocument());
+  if (!doc)
     return;
 
   if (type == NamedItemType::kNameOrIdWithName && GetNameAttribute().IsEmpty())
     return;
 
   if (!old_id.IsEmpty())
-    ToHTMLDocument(GetDocument()).RemoveNamedItem(old_id);
+    doc->RemoveNamedItem(old_id);
 
   if (!new_id.IsEmpty())
-    ToHTMLDocument(GetDocument()).AddNamedItem(new_id);
+    doc->AddNamedItem(new_id);
 }
 
 ScrollOffset Element::SavedLayerScrollOffset() const {
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 1203bb9..5045281 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -621,8 +621,11 @@
   }
 
   bool IsDefined() const {
-    return !(static_cast<int>(GetCustomElementState()) &
-             static_cast<int>(CustomElementState::kNotDefinedFlag));
+    // An element whose custom element state is "uncustomized" or "custom"
+    // is said to be defined.
+    // https://dom.spec.whatwg.org/#concept-element-defined
+    return GetCustomElementState() == CustomElementState::kUncustomized ||
+           GetCustomElementState() == CustomElementState::kCustom;
   }
   bool IsUpgradedV0CustomElement() {
     return GetV0CustomElementState() == kV0Upgraded;
diff --git a/third_party/blink/renderer/core/dom/named_node_map.cc b/third_party/blink/renderer/core/dom/named_node_map.cc
index af97e374..e112039 100644
--- a/third_party/blink/renderer/core/dom/named_node_map.cc
+++ b/third_party/blink/renderer/core/dom/named_node_map.cc
@@ -28,6 +28,7 @@
 #include "third_party/blink/renderer/core/dom/attr.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/html/html_document.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
@@ -105,7 +106,7 @@
   // 3. Return names.
   const AttributeCollection attributes = element_->Attributes();
   names.ReserveInitialCapacity(attributes.size());
-  if (element_->IsHTMLElement() && element_->GetDocument().IsHTMLDocument()) {
+  if (element_->IsHTMLElement() && IsA<HTMLDocument>(element_->GetDocument())) {
     for (const Attribute& attribute : attributes) {
       if ((attribute.Prefix() == attribute.Prefix().LowerASCII()) &&
           (attribute.LocalName() == attribute.LocalName().LowerASCII())) {
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index b5ec431..18191cc 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -3148,7 +3148,8 @@
       break;
 
     case CustomElementState::kCustom:
-      DCHECK_EQ(CustomElementState::kUndefined, old_state);
+      DCHECK(old_state == CustomElementState::kUndefined ||
+             old_state == CustomElementState::kFailed);
       break;
 
     case CustomElementState::kFailed:
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h
index cbe914ae..bb5906e 100644
--- a/third_party/blink/renderer/core/dom/node.h
+++ b/third_party/blink/renderer/core/dom/node.h
@@ -102,8 +102,6 @@
   kCustom = 1 << kNodeCustomElementShift,
   kUndefined = 2 << kNodeCustomElementShift,
   kFailed = 3 << kNodeCustomElementShift,
-
-  kNotDefinedFlag = 2 << kNodeCustomElementShift,
 };
 
 enum class SlotChangeType {
diff --git a/third_party/blink/renderer/core/editing/serializers/serialization.cc b/third_party/blink/renderer/core/editing/serializers/serialization.cc
index a4b1be6..192c795 100644
--- a/third_party/blink/renderer/core/editing/serializers/serialization.cc
+++ b/third_party/blink/renderer/core/editing/serializers/serialization.cc
@@ -56,6 +56,7 @@
 #include "third_party/blink/renderer/core/html/html_body_element.h"
 #include "third_party/blink/renderer/core/html/html_br_element.h"
 #include "third_party/blink/renderer/core/html/html_div_element.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/html/html_quote_element.h"
 #include "third_party/blink/renderer/core/html/html_span_element.h"
@@ -451,7 +452,7 @@
     return "";
 
   MarkupAccumulator accumulator(should_resolve_urls,
-                                node->GetDocument().IsHTMLDocument()
+                                IsA<HTMLDocument>(node->GetDocument())
                                     ? SerializationType::kHTML
                                     : SerializationType::kXML);
   return accumulator.SerializeNodes<EditingStrategy>(*node, children_only);
@@ -609,7 +610,7 @@
           : context_element->GetDocument();
   DocumentFragment* fragment = DocumentFragment::Create(document);
 
-  if (document.IsHTMLDocument()) {
+  if (IsA<HTMLDocument>(document)) {
     fragment->ParseHTML(markup, context_element, parser_content_policy);
     return fragment;
   }
@@ -783,7 +784,7 @@
 
   Document* document = frame->GetDocument();
   DCHECK(document);
-  DCHECK(document->IsHTMLDocument());
+  DCHECK(IsA<HTMLDocument>(document));
   DCHECK(document->body());
 
   document->SetIsForMarkupSanitization(true);
diff --git a/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.cc b/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.cc
index 7563aaaf..1159a2a6 100644
--- a/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.cc
+++ b/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.cc
@@ -34,6 +34,7 @@
 #include "third_party/blink/renderer/core/editing/editing_utilities.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
 #include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
@@ -55,8 +56,8 @@
     Document* document,
     const CreateMarkupOptions& options)
     : formatter_(options.ShouldResolveURLs(),
-                 document->IsHTMLDocument() ? SerializationType::kHTML
-                                            : SerializationType::kXML),
+                 IsA<HTMLDocument>(document) ? SerializationType::kHTML
+                                             : SerializationType::kXML),
       start_(start),
       end_(end),
       document_(document),
@@ -106,8 +107,9 @@
     DCHECK(document_);
 
     result_.Append("<span style=\"");
-    MarkupFormatter::AppendAttributeValue(
-        result_, inline_style->Style()->AsText(), document_->IsHTMLDocument());
+    MarkupFormatter::AppendAttributeValue(result_,
+                                          inline_style->Style()->AsText(),
+                                          IsA<HTMLDocument>(document_.Get()));
     result_.Append("\">");
   }
   if (!ShouldAnnotate()) {
@@ -141,7 +143,7 @@
     StringBuilder& out,
     const Element& element,
     EditingStyle* style) {
-  const bool document_is_html = element.GetDocument().IsHTMLDocument();
+  const bool document_is_html = IsA<HTMLDocument>(element.GetDocument());
   formatter_.AppendStartTagOpen(out, element);
   AttributeCollection attributes = element.Attributes();
   for (const auto& attribute : attributes) {
@@ -194,7 +196,7 @@
   StringBuilder open_tag;
   open_tag.Append("<div style=\"");
   MarkupFormatter::AppendAttributeValue(open_tag, style->AsText(),
-                                        document_->IsHTMLDocument());
+                                        IsA<HTMLDocument>(document_.Get()));
   open_tag.Append("\">");
   reversed_preceding_markup_.push_back(open_tag.ToString());
 
diff --git a/third_party/blink/renderer/core/exported/web_document.cc b/third_party/blink/renderer/core/exported/web_document.cc
index c150a60..c9550aca 100644
--- a/third_party/blink/renderer/core/exported/web_document.cc
+++ b/third_party/blink/renderer/core/exported/web_document.cc
@@ -53,6 +53,7 @@
 #include "third_party/blink/renderer/core/html/html_all_collection.h"
 #include "third_party/blink/renderer/core/html/html_body_element.h"
 #include "third_party/blink/renderer/core/html/html_collection.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/html/html_head_element.h"
 #include "third_party/blink/renderer/core/html/html_link_element.h"
@@ -119,7 +120,7 @@
 }
 
 bool WebDocument::IsHTMLDocument() const {
-  return ConstUnwrap<Document>()->IsHTMLDocument();
+  return IsA<HTMLDocument>(ConstUnwrap<Document>());
 }
 
 bool WebDocument::IsXHTMLDocument() const {
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index aa0db8ba..1b539e3d 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -99,6 +99,7 @@
 #include "third_party/blink/renderer/core/html/forms/external_date_time_chooser.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
 #include "third_party/blink/renderer/core/html/html_object_element.h"
 #include "third_party/blink/renderer/core/inspector/dev_tools_emulator.h"
@@ -612,7 +613,7 @@
   web_view->MainFrameWidget()->SetFocus(true);
   web_view->SetIsActive(true);
   WebLocalFrameImpl* frame = web_view->MainFrameImpl();
-  EXPECT_TRUE(frame->GetFrame()->GetDocument()->IsHTMLDocument());
+  EXPECT_TRUE(IsA<HTMLDocument>(frame->GetFrame()->GetDocument()));
 
   Document* document = frame->GetFrame()->GetDocument();
   EXPECT_TRUE(document->hasFocus());
diff --git a/third_party/blink/renderer/core/frame/frame_serializer.cc b/third_party/blink/renderer/core/frame/frame_serializer.cc
index bdf1513..b6c4f24 100644
--- a/third_party/blink/renderer/core/frame/frame_serializer.cc
+++ b/third_party/blink/renderer/core/frame/frame_serializer.cc
@@ -124,8 +124,8 @@
     FrameSerializerResourceDelegate& resource_delegate,
     Document& document)
     : MarkupAccumulator(kResolveAllURLs,
-                        document.IsHTMLDocument() ? SerializationType::kHTML
-                                                  : SerializationType::kXML),
+                        IsA<HTMLDocument>(document) ? SerializationType::kHTML
+                                                    : SerializationType::kXML),
       delegate_(delegate),
       resource_delegate_(resource_delegate),
       document_(&document) {}
@@ -273,7 +273,7 @@
 void SerializerMarkupAccumulator::AppendAttributeValue(
     const String& attribute_value) {
   MarkupFormatter::AppendAttributeValue(markup_, attribute_value,
-                                        document_->IsHTMLDocument());
+                                        IsA<HTMLDocument>(document_.Get()));
 }
 
 void SerializerMarkupAccumulator::AppendRewrittenAttribute(
diff --git a/third_party/blink/renderer/core/frame/visual_viewport.cc b/third_party/blink/renderer/core/frame/visual_viewport.cc
index 2892126..8762c43 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport.cc
@@ -370,16 +370,17 @@
   TRACE_EVENT_INSTANT1("loading", "viewport", TRACE_EVENT_SCOPE_THREAD, "data",
                        ViewportToTracedValue());
 
+  if (!MainFrame())
+    return;
+
   // Need to re-compute sizes for the overlay scrollbars.
   if (scrollbar_layer_horizontal_) {
     DCHECK(scrollbar_layer_vertical_);
     UpdateScrollbarLayer(kHorizontalScrollbar);
     UpdateScrollbarLayer(kVerticalScrollbar);
+    MainFrame()->View()->SetVisualViewportNeedsRepaint();
   }
 
-  if (!MainFrame())
-    return;
-
   EnqueueResizeEvent();
 }
 
diff --git a/third_party/blink/renderer/core/frame/visual_viewport_test.cc b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
index 4832d69..9705b25 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport_test.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
@@ -2794,5 +2794,41 @@
             visual_viewport.LayerForScrolling()->CurrentScrollOffset());
 }
 
+TEST_P(VisualViewportTest, ScrollbarGeometryOnSizeChange) {
+  InitializeWithAndroidSettings();
+  WebView()->MainFrameWidget()->Resize(WebSize(100, 100));
+  RegisterMockedHttpURLLoad("content-width-1000.html");
+  NavigateTo(base_url_ + "content-width-1000.html");
+
+  auto& visual_viewport = GetFrame()->GetPage()->GetVisualViewport();
+  EXPECT_EQ(IntSize(100, 100), visual_viewport.Size());
+  auto* horizontal_scrollbar = visual_viewport.LayerForHorizontalScrollbar();
+  auto* vertical_scrollbar = visual_viewport.LayerForVerticalScrollbar();
+  ASSERT_TRUE(horizontal_scrollbar);
+  ASSERT_TRUE(vertical_scrollbar);
+  EXPECT_EQ(gfx::Vector2dF(0, 93),
+            horizontal_scrollbar->offset_to_transform_parent());
+  EXPECT_EQ(gfx::Vector2dF(93, 0),
+            vertical_scrollbar->offset_to_transform_parent());
+  EXPECT_EQ(gfx::Size(93, 7), horizontal_scrollbar->bounds());
+  EXPECT_EQ(gfx::Size(7, 93), vertical_scrollbar->bounds());
+
+  // Simulate hiding of the top controls.
+  WebView()->MainFrameWidget()->Resize(WebSize(100, 120));
+  UpdateAllLifecyclePhasesExceptPaint();
+  EXPECT_FALSE(GetFrame()->View()->VisualViewportNeedsRepaint());
+  UpdateAllLifecyclePhases();
+  EXPECT_EQ(IntSize(100, 120), visual_viewport.Size());
+  ASSERT_EQ(horizontal_scrollbar,
+            visual_viewport.LayerForHorizontalScrollbar());
+  ASSERT_EQ(vertical_scrollbar, visual_viewport.LayerForVerticalScrollbar());
+  EXPECT_EQ(gfx::Vector2dF(0, 113),
+            horizontal_scrollbar->offset_to_transform_parent());
+  EXPECT_EQ(gfx::Vector2dF(93, 0),
+            vertical_scrollbar->offset_to_transform_parent());
+  EXPECT_EQ(gfx::Size(93, 7), horizontal_scrollbar->bounds());
+  EXPECT_EQ(gfx::Size(7, 113), vertical_scrollbar->bounds());
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/web_frame_serializer_impl.cc b/third_party/blink/renderer/core/frame/web_frame_serializer_impl.cc
index ed21e87..5a4df2a 100644
--- a/third_party/blink/renderer/core/frame/web_frame_serializer_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_serializer_impl.cc
@@ -86,6 +86,7 @@
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
 #include "third_party/blink/renderer/core/html/html_all_collection.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/html/html_frame_element_base.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
@@ -123,7 +124,7 @@
     : url(url),
       text_encoding(text_encoding),
       document(document),
-      is_html_document(document->IsHTMLDocument()),
+      is_html_document(IsA<HTMLDocument>(document)),
       have_seen_doc_type(false),
       have_added_charset_declaration(false),
       skip_meta_element(nullptr),
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_definition.cc b/third_party/blink/renderer/core/html/custom/custom_element_definition.cc
index 6126e0d..f530712a 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element_definition.cc
+++ b/third_party/blink/renderer/core/html/custom/custom_element_definition.cc
@@ -112,7 +112,7 @@
         document);
   }
   // TODO(davaajav): write this as one call to setCustomElementState instead of
-  // two
+  // two.
   element->SetCustomElementState(CustomElementState::kUndefined);
   element->SetCustomElementDefinition(this);
   return element;
@@ -190,21 +190,39 @@
 
 // https://html.spec.whatwg.org/C/#concept-upgrade-an-element
 void CustomElementDefinition::Upgrade(Element& element) {
-  DCHECK_EQ(element.GetCustomElementState(), CustomElementState::kUndefined);
+  // 4.13.5.1 If element is custom, then return.
+  // 4.13.5.2 If element's custom element state is "failed", then return.
+  if (element.GetCustomElementState() == CustomElementState::kCustom ||
+      element.GetCustomElementState() == CustomElementState::kFailed) {
+    return;
+  }
 
+  // 4.13.5.3. Set element's custom element state to "failed".
+  element.SetCustomElementState(CustomElementState::kFailed);
+
+  // 4.13.5.4: For each attribute in element's attribute list, in order, enqueue
+  // a custom element callback reaction with element, callback name
+  // "attributeChangedCallback", and an argument list containing attribute's
+  // local name, null, attribute's value, and attribute's namespace.
   if (!observed_attributes_.IsEmpty())
     EnqueueAttributeChangedCallbackForAllAttributes(element);
 
+  // 4.13.5.5: If element is connected, then enqueue a custom element callback
+  // reaction with element, callback name "connectedCallback", and an empty
+  // argument list.
   if (element.isConnected() && HasConnectedCallback())
     EnqueueConnectedCallback(element);
 
   bool succeeded = false;
   {
+    // 4.13.5.6: Add element to the end of definition's construction stack.
     ConstructionStackScope construction_stack_scope(*this, element);
+    // 4.13.5.8: Run the constructor, catching exceptions.
     succeeded = RunConstructor(element);
   }
   if (!succeeded) {
-    element.SetCustomElementState(CustomElementState::kFailed);
+    // 4.13.5.?: If the above steps threw an exception, then element's custom
+    // element state will remain "failed".
     CustomElementReactionStack::Current().ClearQueue(element);
     return;
   }
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_test.cc b/third_party/blink/renderer/core/html/custom/custom_element_test.cc
index 1ea5764c..5c40f3d 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element_test.cc
+++ b/third_party/blink/renderer/core/html/custom/custom_element_test.cc
@@ -234,7 +234,7 @@
 
   // create an element with an uppercase tag name
   Document& document = holder->GetDocument();
-  EXPECT_TRUE(document.IsHTMLDocument())
+  EXPECT_TRUE(IsA<HTMLDocument>(document))
       << "this test requires a HTML document";
   Element* element = document.CreateElementForBinding("A-A", should_not_throw);
   EXPECT_EQ(definition, element->GetCustomElementDefinition());
diff --git a/third_party/blink/renderer/core/html/custom/element_internals.cc b/third_party/blink/renderer/core/html/custom/element_internals.cc
index 946528c..be7a900 100644
--- a/third_party/blink/renderer/core/html/custom/element_internals.cc
+++ b/third_party/blink/renderer/core/html/custom/element_internals.cc
@@ -328,8 +328,13 @@
 bool ElementInternals::IsTargetFormAssociated() const {
   if (Target().IsFormAssociatedCustomElement())
     return true;
-  if (Target().GetCustomElementState() != CustomElementState::kUndefined)
+  // Custom element could be in the process of upgrading here, during which
+  // it will have state kFailed according to:
+  // https://html.spec.whatwg.org/multipage/custom-elements.html#upgrades
+  if (Target().GetCustomElementState() != CustomElementState::kUndefined &&
+      Target().GetCustomElementState() != CustomElementState::kFailed) {
     return false;
+  }
   // An element is in "undefined" state in its constructor JavaScript code.
   // ElementInternals needs to handle elements to be form-associated same as
   // form-associated custom elements because web authors want to call
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.cc
index 888e83b..473e952a5 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.cc
@@ -275,6 +275,17 @@
   return isConnected() && ShouldAutocomplete();
 }
 
+void HTMLFormControlElementWithState::QueueInputAndChangeEvents() {
+  auto task_runner = GetDocument().GetTaskRunner(TaskType::kUserInteraction);
+  task_runner->PostTask(
+      FROM_HERE, WTF::Bind(&HTMLFormControlElementWithState::DispatchInputEvent,
+                           WrapWeakPersistent(this)));
+  task_runner->PostTask(
+      FROM_HERE,
+      WTF::Bind(&HTMLFormControlElementWithState::DispatchChangeEvent,
+                WrapWeakPersistent(this)));
+}
+
 void HTMLFormControlElementWithState::FinishParsingChildren() {
   HTMLFormControlElement::FinishParsingChildren();
   ListedElement::TakeStateAndRestore();
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h b/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h
index 6f93afe..196b6bc7 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h
@@ -54,6 +54,8 @@
   bool user_has_edited_the_field_ = false;
   HTMLFormControlElementWithState(const QualifiedName& tag_name, Document&);
 
+  void QueueInputAndChangeEvents();
+
   void FinishParsingChildren() override;
   bool IsFormControlElementWithState() const final;
 
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.cc b/third_party/blink/renderer/core/html/forms/html_input_element.cc
index 1be9d7e..052cab5 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.cc
@@ -586,6 +586,7 @@
 void HTMLInputElement::RestoreFormControlState(const FormControlState& state) {
   input_type_view_->RestoreFormControlState(state);
   state_restored_ = true;
+  QueueInputAndChangeEvents();
 }
 
 bool HTMLInputElement::CanStartSelection() const {
@@ -1780,22 +1781,6 @@
   return input_type_->ShouldAppearIndeterminate();
 }
 
-bool HTMLInputElement::IsInRequiredRadioButtonGroup() {
-  // TODO(tkent): Remove type check.
-  DCHECK_EQ(type(), input_type_names::kRadio);
-  if (RadioButtonGroupScope* scope = GetRadioButtonGroupScope())
-    return scope->IsInRequiredGroup(this);
-  return false;
-}
-
-HTMLInputElement* HTMLInputElement::CheckedRadioButtonForGroup() {
-  if (checked())
-    return this;
-  if (RadioButtonGroupScope* scope = GetRadioButtonGroupScope())
-    return scope->CheckedButtonForGroup(GetName());
-  return nullptr;
-}
-
 RadioButtonGroupScope* HTMLInputElement::GetRadioButtonGroupScope() const {
   // FIXME: Remove type check.
   if (type() != input_type_names::kRadio)
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.h b/third_party/blink/renderer/core/html/forms/html_input_element.h
index 67291f9..564e283 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.h
@@ -127,6 +127,9 @@
   bool ShouldAppearChecked() const;
   bool ShouldAppearIndeterminate() const override;
 
+  // Returns null if this isn't associated with any radio button group.
+  RadioButtonGroupScope* GetRadioButtonGroupScope() const;
+
   unsigned size() const;
   bool SizeShouldIncludeDecoration(int& preferred_size) const;
 
@@ -245,9 +248,6 @@
   // Associated <datalist> options which match to the current INPUT value.
   HeapVector<Member<HTMLOptionElement>> FilteredDataListOptions() const;
 
-  HTMLInputElement* CheckedRadioButtonForGroup();
-  bool IsInRequiredRadioButtonGroup();
-
   // Functions for InputType classes.
   void SetNonAttributeValue(const String&);
   void SetNonAttributeValueByUserEdit(const String&);
@@ -409,8 +409,6 @@
   void SetListAttributeTargetObserver(ListAttributeTargetObserver*);
   void ResetListAttributeTargetObserver();
 
-  // Returns null if this isn't associated with any radio button group.
-  RadioButtonGroupScope* GetRadioButtonGroupScope() const;
   void AddToRadioButtonGroup();
   void RemoveFromRadioButtonGroup();
   scoped_refptr<ComputedStyle> CustomStyleForLayoutObject() override;
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc
index b25fcd2d..68ec949 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -1237,6 +1237,7 @@
   }
 
   SetNeedsValidityCheck();
+  QueueInputAndChangeEvents();
 }
 
 void HTMLSelectElement::ParseMultipleAttribute(const AtomicString& value) {
diff --git a/third_party/blink/renderer/core/html/forms/html_text_area_element.cc b/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
index 78a93fe7..d73223d 100644
--- a/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
@@ -94,7 +94,11 @@
 
 void HTMLTextAreaElement::RestoreFormControlState(
     const FormControlState& state) {
+  // We don't add kDispatchInputAndChangeEvent to setValue(), and we
+  // post tasks to dispatch events instead. This function can be called
+  // while we should not dispatch any events.
   setValue(state[0]);
+  QueueInputAndChangeEvents();
 }
 
 void HTMLTextAreaElement::ChildrenChanged(const ChildrenChange& change) {
diff --git a/third_party/blink/renderer/core/html/forms/radio_input_type.cc b/third_party/blink/renderer/core/html/forms/radio_input_type.cc
index 5b69b334..de9ca99b3 100644
--- a/third_party/blink/renderer/core/html/forms/radio_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/radio_input_type.cc
@@ -56,8 +56,12 @@
 }
 
 bool RadioInputType::ValueMissing(const String&) const {
-  return GetElement().IsInRequiredRadioButtonGroup() &&
-         !GetElement().CheckedRadioButtonForGroup();
+  HTMLInputElement& input = GetElement();
+  if (auto* scope = input.GetRadioButtonGroupScope())
+    return scope->IsInRequiredGroup(&input) && !CheckedRadioButtonForGroup();
+  // TODO(crbug.com/883723): This function should work even if this radio
+  // button doesn't belong to any RadioButtonGroupScope.
+  return false;
 }
 
 String RadioInputType::ValueMissingText() const {
@@ -175,7 +179,7 @@
 
   // Allow keyboard focus if we're checked or if nothing in the group is
   // checked.
-  return GetElement().checked() || !GetElement().CheckedRadioButtonForGroup();
+  return GetElement().checked() || !CheckedRadioButtonForGroup();
 }
 
 bool RadioInputType::ShouldSendChangeEventAfterCheckedChanged() {
@@ -197,7 +201,7 @@
   ClickHandlingState* state = MakeGarbageCollected<ClickHandlingState>();
 
   state->checked = GetElement().checked();
-  state->checked_radio_button = GetElement().CheckedRadioButtonForGroup();
+  state->checked_radio_button = CheckedRadioButtonForGroup();
   GetElement().setChecked(true, TextFieldEventBehavior::kDispatchChangeEvent);
   is_in_click_handler_ = true;
   return state;
@@ -225,7 +229,7 @@
 }
 
 bool RadioInputType::ShouldAppearIndeterminate() const {
-  return !GetElement().CheckedRadioButtonForGroup();
+  return !CheckedRadioButtonForGroup();
 }
 
 HTMLInputElement* RadioInputType::NextRadioButtonInGroup(
@@ -247,4 +251,13 @@
   return nullptr;
 }
 
+HTMLInputElement* RadioInputType::CheckedRadioButtonForGroup() const {
+  HTMLInputElement& input = GetElement();
+  if (input.checked())
+    return &input;
+  if (auto* scope = input.GetRadioButtonGroupScope())
+    return scope->CheckedButtonForGroup(input.GetName());
+  return nullptr;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/radio_input_type.h b/third_party/blink/renderer/core/html/forms/radio_input_type.h
index 5c0b43d..89cfdbf 100644
--- a/third_party/blink/renderer/core/html/forms/radio_input_type.h
+++ b/third_party/blink/renderer/core/html/forms/radio_input_type.h
@@ -59,6 +59,7 @@
 
   HTMLInputElement* FindNextFocusableRadioButtonInGroup(HTMLInputElement*,
                                                         bool);
+  HTMLInputElement* CheckedRadioButtonForGroup() const;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_document.h b/third_party/blink/renderer/core/html/html_document.h
index 55ab1e2..fa4c536d 100644
--- a/third_party/blink/renderer/core/html/html_document.h
+++ b/third_party/blink/renderer/core/html/html_document.h
@@ -55,7 +55,12 @@
   return named_item_counts_.Contains(name);
 }
 
-DEFINE_DOCUMENT_TYPE_CASTS(HTMLDocument);
+template <>
+struct DowncastTraits<HTMLDocument> {
+  static bool AllowFrom(const Document& document) {
+    return document.IsHTMLDocument();
+  }
+};
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index b865b1a..cf956a71 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -61,6 +61,7 @@
 #include "third_party/blink/renderer/core/html/forms/labels_node_list.h"
 #include "third_party/blink/renderer/core/html/html_br_element.h"
 #include "third_party/blink/renderer/core/html/html_dimension.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/html/html_template_element.h"
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
@@ -135,7 +136,7 @@
 }  // anonymous namespace
 
 String HTMLElement::DebugNodeName() const {
-  if (GetDocument().IsHTMLDocument()) {
+  if (IsA<HTMLDocument>(GetDocument())) {
     return TagQName().HasPrefix() ? Element::nodeName().UpperASCII()
                                   : TagQName().LocalName().UpperASCII();
   }
@@ -150,7 +151,7 @@
   // chars that does not have to copy the string on a hit in the hash.
   // FIXME: We should have a way to detect XHTML elements and replace the
   // hasPrefix() check with it.
-  if (GetDocument().IsHTMLDocument()) {
+  if (IsA<HTMLDocument>(GetDocument())) {
     if (!TagQName().HasPrefix())
       return TagQName().LocalNameUpper();
     return Element::nodeName().UpperASCII();
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.cc b/third_party/blink/renderer/core/html/html_iframe_element.cc
index dc33dba..8f3b8f8 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.cc
+++ b/third_party/blink/renderer/core/html/html_iframe_element.cc
@@ -133,10 +133,10 @@
   const QualifiedName& name = params.name;
   const AtomicString& value = params.new_value;
   if (name == html_names::kNameAttr) {
-    if (IsInDocumentTree() && GetDocument().IsHTMLDocument()) {
-      HTMLDocument& document = ToHTMLDocument(GetDocument());
-      document.RemoveNamedItem(name_);
-      document.AddNamedItem(value);
+    auto* document = DynamicTo<HTMLDocument>(GetDocument());
+    if (document && IsInDocumentTree()) {
+      document->RemoveNamedItem(name_);
+      document->AddNamedItem(value);
     }
     AtomicString old_name = name_;
     name_ = value;
@@ -368,9 +368,9 @@
   InsertionNotificationRequest result =
       HTMLFrameElementBase::InsertedInto(insertion_point);
 
-  if (insertion_point.IsInDocumentTree() && GetDocument().IsHTMLDocument()) {
-    ToHTMLDocument(GetDocument()).AddNamedItem(name_);
-
+  auto* html_doc = DynamicTo<HTMLDocument>(GetDocument());
+  if (html_doc && insertion_point.IsInDocumentTree()) {
+    html_doc->AddNamedItem(name_);
     if (!ContentSecurityPolicy::IsValidCSPAttr(
             required_csp_, GetDocument().RequiredCSP().GetString())) {
       if (!required_csp_.IsEmpty()) {
@@ -391,8 +391,9 @@
 
 void HTMLIFrameElement::RemovedFrom(ContainerNode& insertion_point) {
   HTMLFrameElementBase::RemovedFrom(insertion_point);
-  if (insertion_point.IsInDocumentTree() && GetDocument().IsHTMLDocument())
-    ToHTMLDocument(GetDocument()).RemoveNamedItem(name_);
+  auto* html_doc = DynamicTo<HTMLDocument>(GetDocument());
+  if (html_doc && insertion_point.IsInDocumentTree())
+    html_doc->RemoveNamedItem(name_);
 }
 
 bool HTMLIFrameElement::IsInteractiveContent() const {
diff --git a/third_party/blink/renderer/core/html/html_map_element.cc b/third_party/blink/renderer/core/html/html_map_element.cc
index 7874efc..6f3d1200 100644
--- a/third_party/blink/renderer/core/html/html_map_element.cc
+++ b/third_party/blink/renderer/core/html/html_map_element.cc
@@ -27,6 +27,7 @@
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/html/html_area_element.h"
 #include "third_party/blink/renderer/core/html/html_collection.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_image_element.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
@@ -83,7 +84,7 @@
     if (params.name == html_names::kIdAttr) {
       // Call base class so that hasID bit gets set.
       HTMLElement::ParseAttribute(params);
-      if (GetDocument().IsHTMLDocument())
+      if (IsA<HTMLDocument>(GetDocument()))
         return;
     }
     if (isConnected())
diff --git a/third_party/blink/renderer/core/html/html_param_element.cc b/third_party/blink/renderer/core/html/html_param_element.cc
index 68d6bbc8..de430de8 100644
--- a/third_party/blink/renderer/core/html/html_param_element.cc
+++ b/third_party/blink/renderer/core/html/html_param_element.cc
@@ -24,6 +24,7 @@
 
 #include "third_party/blink/renderer/core/dom/attribute.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html_names.h"
 
 namespace blink {
@@ -34,7 +35,7 @@
 const AtomicString& HTMLParamElement::GetName() const {
   if (HasName())
     return GetNameAttribute();
-  return GetDocument().IsHTMLDocument() ? g_empty_atom : GetIdAttribute();
+  return IsA<HTMLDocument>(GetDocument()) ? g_empty_atom : GetIdAttribute();
 }
 
 const AtomicString& HTMLParamElement::Value() const {
diff --git a/third_party/blink/renderer/core/html/html_tag_collection.cc b/third_party/blink/renderer/core/html/html_tag_collection.cc
index 7ad0c358..ced904819 100644
--- a/third_party/blink/renderer/core/html/html_tag_collection.cc
+++ b/third_party/blink/renderer/core/html/html_tag_collection.cc
@@ -23,6 +23,7 @@
  */
 
 #include "third_party/blink/renderer/core/html/html_tag_collection.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 
 namespace blink {
 
@@ -30,7 +31,7 @@
                                      const AtomicString& qualified_name)
     : TagCollection(root_node, kHTMLTagCollectionType, qualified_name),
       lowered_qualified_name_(qualified_name.LowerASCII()) {
-  DCHECK(root_node.GetDocument().IsHTMLDocument());
+  DCHECK(IsA<HTMLDocument>(root_node.GetDocument()));
 }
 
 HTMLTagCollection::HTMLTagCollection(ContainerNode& root_node,
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser_test.cc b/third_party/blink/renderer/core/html/parser/html_document_parser_test.cc
index 33750ce..de34efd 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser_test.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser_test.cc
@@ -72,7 +72,7 @@
 }  // namespace
 
 TEST_F(HTMLDocumentParserTest, AppendPrefetch) {
-  HTMLDocument& document = ToHTMLDocument(GetDocument());
+  auto& document = To<HTMLDocument>(GetDocument());
   ProvidePrerendererClientTo(
       *document.GetPage(),
       MakeGarbageCollected<MockPrerendererClient>(*document.GetPage(), true));
@@ -94,7 +94,7 @@
 }
 
 TEST_F(HTMLDocumentParserTest, AppendNoPrefetch) {
-  HTMLDocument& document = ToHTMLDocument(GetDocument());
+  auto& document = To<HTMLDocument>(GetDocument());
   EXPECT_FALSE(document.IsPrefetchOnly());
   // Use ForceSynchronousParsing to allow calling append().
   HTMLDocumentParser* parser = CreateParser(document);
diff --git a/third_party/blink/renderer/core/html/portal/html_portal_element.cc b/third_party/blink/renderer/core/html/portal/html_portal_element.cc
index dc78314..4e53db23 100644
--- a/third_party/blink/renderer/core/html/portal/html_portal_element.cc
+++ b/third_party/blink/renderer/core/html/portal/html_portal_element.cc
@@ -23,6 +23,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/frame/remote_frame.h"
 #include "third_party/blink/renderer/core/frame/window_post_message_options.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_unknown_element.h"
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
 #include "third_party/blink/renderer/core/html/portal/document_portals.h"
@@ -101,7 +102,7 @@
 HTMLPortalElement::GuestContentsEligibility
 HTMLPortalElement::GetGuestContentsEligibility() const {
   // Non-HTML documents aren't eligible at all.
-  if (!GetDocument().IsHTMLDocument())
+  if (!IsA<HTMLDocument>(GetDocument()))
     return GuestContentsEligibility::kIneligible;
 
   LocalFrame* frame = GetDocument().GetFrame();
diff --git a/third_party/blink/renderer/core/inspector/dom_patch_support.cc b/third_party/blink/renderer/core/inspector/dom_patch_support.cc
index e2b1666..e4a16c7 100644
--- a/third_party/blink/renderer/core/inspector/dom_patch_support.cc
+++ b/third_party/blink/renderer/core/inspector/dom_patch_support.cc
@@ -62,7 +62,7 @@
 void DOMPatchSupport::PatchDocument(const String& markup) {
   Document* new_document = nullptr;
   DocumentInit init = DocumentInit::Create();
-  if (GetDocument().IsHTMLDocument())
+  if (IsA<HTMLDocument>(GetDocument()))
     new_document = MakeGarbageCollected<HTMLDocument>(init);
   else if (GetDocument().IsSVGDocument())
     new_document = XMLDocument::CreateSVG(init);
@@ -73,7 +73,7 @@
 
   DCHECK(new_document);
   new_document->SetContextFeatures(GetDocument().GetContextFeatures());
-  if (!GetDocument().IsHTMLDocument()) {
+  if (!IsA<HTMLDocument>(GetDocument())) {
     DocumentParser* parser =
         MakeGarbageCollected<XMLDocumentParser>(*new_document, nullptr);
     parser->Append(markup);
@@ -119,7 +119,7 @@
   auto* target_element = To<Element>(target_node);
 
   // FIXME: This code should use one of createFragment* in Serialization.h
-  if (GetDocument().IsHTMLDocument())
+  if (IsA<HTMLDocument>(GetDocument()))
     fragment->ParseHTML(markup, target_element);
   else
     fragment->ParseXML(markup, target_element);
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
index a1d5e2cf..ed6a79c11 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
@@ -64,6 +64,7 @@
 #include "third_party/blink/renderer/core/dom/text.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/html/html_head_element.h"
 #include "third_party/blink/renderer/core/inspector/identifiers_factory.h"
@@ -1917,7 +1918,7 @@
   if (!document)
     return nullptr;
 
-  if (!document->IsHTMLDocument() && !document->IsSVGDocument())
+  if (!IsA<HTMLDocument>(document) && !document->IsSVGDocument())
     return nullptr;
 
   CSSStyleSheet& inspector_sheet =
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
index 6332c4d..67476f5f 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
@@ -58,6 +58,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/remote_frame.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/html/html_link_element.h"
 #include "third_party/blink/renderer/core/html/html_slot_element.h"
@@ -759,7 +760,7 @@
   DocumentFragment* fragment = element->GetDocument().createDocumentFragment();
 
   bool should_ignore_case =
-      element->GetDocument().IsHTMLDocument() && element->IsHTMLElement();
+      IsA<HTMLDocument>(element->GetDocument()) && element->IsHTMLElement();
   // Not all elements can represent the context (i.e. IFRAME), hence using
   // document.body.
   if (should_ignore_case && element->GetDocument().body()) {
@@ -900,7 +901,8 @@
 
   Document* document =
       IsA<Document>(node) ? To<Document>(node) : node->ownerDocument();
-  if (!document || (!document->IsHTMLDocument() && !IsA<XMLDocument>(document)))
+  if (!document ||
+      (!IsA<HTMLDocument>(document) && !IsA<XMLDocument>(document)))
     return Response::Error("Not an HTML/XML document");
 
   Node* new_node = nullptr;
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 41f21a19..3b34642 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -53,6 +53,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
 #include "third_party/blink/renderer/core/input/event_handler.h"
@@ -1526,7 +1527,7 @@
     // commits on same origin loads to avoid confusing users. We also require
     // that this be an html document served via http.
     document->SetDeferredCompositorCommitIsAllowed(is_same_origin_navigation_ &&
-                                                   document->IsHTMLDocument());
+                                                   IsA<HTMLDocument>(document));
   } else {
     document->SetDeferredCompositorCommitIsAllowed(false);
   }
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc
index 30f63c8..7f13e01 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -53,6 +53,7 @@
 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html/html_anchor_element.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_frame_element_base.h"
 #include "third_party/blink/renderer/core/html/html_image_element.h"
 #include "third_party/blink/renderer/core/html/html_plugin_element.h"
@@ -153,7 +154,7 @@
     edit_flags |= ContextMenuDataEditFlags::kCanDelete;
   if (editor.CanEditRichly())
     edit_flags |= ContextMenuDataEditFlags::kCanEditRichly;
-  if (selected_document.IsHTMLDocument() ||
+  if (IsA<HTMLDocument>(selected_document) ||
       selected_document.IsXHTMLDocument()) {
     edit_flags |= ContextMenuDataEditFlags::kCanTranslate;
     if (selected_document.queryCommandEnabled("selectAll", ASSERT_NO_EXCEPTION))
diff --git a/third_party/blink/renderer/core/page/context_menu_controller_test.cc b/third_party/blink/renderer/core/page/context_menu_controller_test.cc
index 8bcad24..0d098fb 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller_test.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller_test.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/media/html_video_element.h"
 #include "third_party/blink/renderer/core/input/context_menu_allowed_scope.h"
 #include "third_party/blink/renderer/core/page/context_menu_controller.h"
@@ -535,7 +536,7 @@
 
   Document* document = GetDocument();
   ASSERT_TRUE(IsA<XMLDocument>(document));
-  ASSERT_FALSE(document->IsHTMLDocument());
+  ASSERT_FALSE(IsA<HTMLDocument>(document));
 
   Element* text_element = document->getElementById("t");
   document->UpdateStyleAndLayout();
diff --git a/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc
index db3ebbc..0d8e0886 100644
--- a/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc
+++ b/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc
@@ -7,6 +7,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h"
 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
@@ -68,7 +69,7 @@
   // Track how often we have a element fragment that we can't find. Only track
   // if we didn't match a text fragment since we expect those would inflate the
   // "failed" case.
-  if (frame.GetDocument()->IsHTMLDocument() && url.HasFragmentIdentifier() &&
+  if (IsA<HTMLDocument>(frame.GetDocument()) && url.HasFragmentIdentifier() &&
       !text_fragment_anchor_created) {
     UMA_HISTOGRAM_BOOLEAN("TextFragmentAnchor.ElementIdFragmentFound",
                           element_id_anchor_found);
diff --git a/third_party/blink/renderer/core/paint/table_section_painter.cc b/third_party/blink/renderer/core/paint/table_section_painter.cc
index 6641f3e..374063c 100644
--- a/third_party/blink/renderer/core/paint/table_section_painter.cc
+++ b/third_party/blink/renderer/core/paint/table_section_painter.cc
@@ -19,6 +19,7 @@
 #include "third_party/blink/renderer/core/paint/table_row_painter.h"
 #include "third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/scoped_display_item_fragment.h"
 
 namespace blink {
 
@@ -43,11 +44,14 @@
     return;
   }
 
+  unsigned fragment_index = 0;
   for (const auto* fragment = &layout_table_section_.FirstFragment(); fragment;
        fragment = fragment->NextFragment()) {
     PaintInfo fragment_paint_info = paint_info;
     fragment_paint_info.SetFragmentLogicalTopInFlowThread(
         fragment->LogicalTopInFlowThread());
+    ScopedDisplayItemFragment scoped_display_item_fragment(
+        fragment_paint_info.context, fragment_index++);
     PaintSection(fragment_paint_info);
   }
 }
diff --git a/third_party/blink/renderer/core/script/script_loader.cc b/third_party/blink/renderer/core/script/script_loader.cc
index 4d09c84e9..3a58430ca 100644
--- a/third_party/blink/renderer/core/script/script_loader.cc
+++ b/third_party/blink/renderer/core/script/script_loader.cc
@@ -35,6 +35,7 @@
 #include "third_party/blink/renderer/core/dom/text.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/imports/html_import.h"
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
 #include "third_party/blink/renderer/core/html_names.h"
@@ -765,7 +766,7 @@
   if (GetScriptType() == mojom::ScriptType::kClassic &&
       element_->HasSourceAttribute() &&
       context_document->GetFrame()->ShouldForceDeferScript() &&
-      context_document->IsHTMLDocument() && parser_inserted_ &&
+      IsA<HTMLDocument>(context_document) && parser_inserted_ &&
       !element_->AsyncAttributeValue()) {
     // In terms of ScriptLoader flags, force deferred scripts behave like
     // parser-blocking scripts, except that |force_deferred_| is set.
@@ -857,7 +858,7 @@
 
   // Check for inline script that should be force deferred.
   if (context_document->GetFrame()->ShouldForceDeferScript() &&
-      context_document->IsHTMLDocument() && parser_inserted_) {
+      IsA<HTMLDocument>(context_document) && parser_inserted_) {
     force_deferred_ = true;
     will_be_parser_executed_ = true;
     return true;
diff --git a/third_party/blink/renderer/core/xml/xpath_parser.cc b/third_party/blink/renderer/core/xml/xpath_parser.cc
index c2abeec7..39fe46e 100644
--- a/third_party/blink/renderer/core/xml/xpath_parser.cc
+++ b/third_party/blink/renderer/core/xml/xpath_parser.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/core/xml/xpath_grammar_generated.h"
 #include "third_party/blink/renderer/core/xml/xpath_ns_resolver.h"
 #include "third_party/blink/renderer/core/xml/xpath_path.h"
+#include "third_party/blink/renderer/core/xml/xpath_util.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
@@ -142,8 +143,9 @@
   }
 }
 
+// See https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-ExprWhitespace .
 void Parser::SkipWS() {
-  while (next_pos_ < data_.length() && IsSpaceOrNewline(data_[next_pos_]))
+  while (next_pos_ < data_.length() && IsXMLSpace(data_[next_pos_]))
     ++next_pos_;
 }
 
diff --git a/third_party/blink/renderer/core/xml/xpath_step.cc b/third_party/blink/renderer/core/xml/xpath_step.cc
index af72e0d0..977a3dfb 100644
--- a/third_party/blink/renderer/core/xml/xpath_step.cc
+++ b/third_party/blink/renderer/core/xml/xpath_step.cc
@@ -31,6 +31,7 @@
 #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_traversal.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/xml/xpath_parser.h"
 #include "third_party/blink/renderer/core/xml/xpath_util.h"
 #include "third_party/blink/renderer/core/xmlns_names.h"
@@ -218,7 +219,7 @@
                namespace_uri == element->namespaceURI();
       }
 
-      if (element->GetDocument().IsHTMLDocument()) {
+      if (IsA<HTMLDocument>(element->GetDocument())) {
         if (element->IsHTMLElement()) {
           // Paths without namespaces should match HTML elements in HTML
           // documents despite those having an XHTML namespace. Names are
diff --git a/third_party/blink/renderer/core/xml/xslt_processor.cc b/third_party/blink/renderer/core/xml/xslt_processor.cc
index 1a2ebe4..d4864635 100644
--- a/third_party/blink/renderer/core/xml/xslt_processor.cc
+++ b/third_party/blink/renderer/core/xml/xslt_processor.cc
@@ -33,6 +33,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/xml/document_xslt.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
@@ -151,7 +152,7 @@
   String result_encoding;
 
   // If the output document is HTML, default to HTML method.
-  if (output_doc->IsHTMLDocument())
+  if (IsA<HTMLDocument>(output_doc))
     result_mime_type = "text/html";
 
   if (!TransformToString(source_node, result_mime_type, result_string,
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
index 796b0106..3ebd1c7 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -816,7 +816,7 @@
   scoped_refptr<EncodedFormData> http_body;
 
   if (AreMethodAndURLValidForSend()) {
-    if (document->IsHTMLDocument())
+    if (IsA<HTMLDocument>(document))
       UpdateContentTypeAndCharset("text/html;charset=UTF-8", "UTF-8");
     else if (IsA<XMLDocument>(document))
       UpdateContentTypeAndCharset("application/xml;charset=UTF-8", "UTF-8");
diff --git a/third_party/blink/renderer/modules/nfc/ndef_record.cc b/third_party/blink/renderer/modules/nfc/ndef_record.cc
index 5dfc4b8..10c9fee 100644
--- a/third_party/blink/renderer/modules/nfc/ndef_record.cc
+++ b/third_party/blink/renderer/modules/nfc/ndef_record.cc
@@ -18,7 +18,6 @@
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/network/http_parsers.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
-#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
 
@@ -70,43 +69,39 @@
   return true;
 }
 
-// https://w3c.github.io/web-nfc/#ndef-record-types
-// Derives a formatted custom type for the external type record from |input|.
-// Returns a null string for an invalid |input|.
-//
-// TODO(https://crbug.com/520391): Refine the validation algorithm here
-// accordingly once there is a conclusion on some case-sensitive things at
-// https://github.com/w3c/web-nfc/issues/331.
-String ValidateCustomRecordType(const String& input) {
-  static const String kOtherCharsForCustomType("()+,-:=@;$_!*'.");
+// https://w3c.github.io/web-nfc/#dfn-validate-external-type
+// Validates |input| as an external type.
+bool IsValidExternalType(const String& input) {
+  static const String kOtherCharsForCustomType("()+,-=@;$_*'.");
 
-  if (input.IsEmpty())
-    return String();
+  if (input.IsEmpty() || input.length() > 255)
+    return false;
 
-  // Finds the separator ':'.
-  wtf_size_t colon_index = input.find(':');
+  // Finds the last occurrence of ':'.
+  wtf_size_t colon_index = input.ReverseFind(':');
   if (colon_index == kNotFound)
-    return String();
+    return false;
 
-  // Derives the domain (FQDN) from the part before ':'.
-  String left = input.Left(colon_index);
-  bool success = false;
-  String domain = SecurityOrigin::CanonicalizeHost(left, &success);
-  if (!success || domain.IsEmpty())
-    return String();
+  // Validates the domain (the part before ':').
+  String domain = input.Left(colon_index);
+  if (domain.IsEmpty())
+    return false;
+  // TODO(https://crbug.com/520391): Make sure |domain| can be converted
+  // successfully to ASCII using IDN rules and does not contain any forbidden
+  // host code point.
 
-  // Validates the part after ':'.
-  String right = input.Substring(colon_index + 1);
-  if (right.length() == 0)
-    return String();
-  for (wtf_size_t i = 0; i < right.length(); i++) {
-    if (!IsASCIIAlphanumeric(right[i]) &&
-        !kOtherCharsForCustomType.Contains(right[i])) {
-      return String();
+  // Validates the type (the part after ':').
+  String type = input.Substring(colon_index + 1);
+  if (type.IsEmpty())
+    return false;
+  for (wtf_size_t i = 0; i < type.length(); i++) {
+    if (!IsASCIIAlphanumeric(type[i]) &&
+        !kOtherCharsForCustomType.Contains(type[i])) {
+      return false;
     }
   }
 
-  return domain + ':' + right;
+  return true;
 }
 
 String getDocumentLanguage(const ExecutionContext* execution_context) {
@@ -245,7 +240,9 @@
   if (!GetBytesOfBufferSource(data, &bytes, exception_state)) {
     return nullptr;
   }
-  return MakeGarbageCollected<NDEFRecord>(custom_type, bytes);
+  NDEFRecord* record = MakeGarbageCollected<NDEFRecord>(
+      device::mojom::NDEFRecordTypeCategory::kExternal, custom_type, bytes);
+  return record;
 }
 
 }  // namespace
@@ -303,19 +300,12 @@
     // TODO(https://crbug.com/520391): Support creating smart-poster records.
     exception_state.ThrowTypeError("smart-poster type is not supported yet");
     return nullptr;
+  } else if (IsValidExternalType(record_type)) {
+    instance = CreateExternalRecord(record_type, init->data(), exception_state);
   } else {
-    // TODO(https://crbug.com/520391): Here |record_type| may be a custom type
-    // name for an external type record, or a local type name for an external
-    // type record embedded within a ndef message as payload of a parent record,
-    // in either case we should try to create an external type record from
-    // |data|.
-    String formated_type = ValidateCustomRecordType(record_type);
-    if (formated_type.IsNull()) {
-      exception_state.ThrowTypeError("Invalid NDEFRecord type.");
-      return nullptr;
-    }
-    instance =
-        CreateExternalRecord(formated_type, init->data(), exception_state);
+    // TODO(https://crbug.com/520391): Support local type records.
+    exception_state.ThrowTypeError("Invalid NDEFRecord type.");
+    return nullptr;
   }
 
   if (instance && init->hasId()) {
@@ -324,30 +314,44 @@
   return instance;
 }
 
-NDEFRecord::NDEFRecord(const String& record_type,
-                       WTF::Vector<uint8_t> data)
-    : record_type_(record_type),
+NDEFRecord::NDEFRecord(const String& record_type, WTF::Vector<uint8_t> data)
+    : category_(device::mojom::NDEFRecordTypeCategory::kStandardized),
+      record_type_(record_type),
       payload_data_(std::move(data)) {}
 
 NDEFRecord::NDEFRecord(const String& record_type,
                        const String& encoding,
                        const String& lang,
                        WTF::Vector<uint8_t> data)
-    : record_type_(record_type),
+    : category_(device::mojom::NDEFRecordTypeCategory::kStandardized),
+      record_type_(record_type),
       encoding_(encoding),
       lang_(lang),
       payload_data_(std::move(data)) {}
 
+NDEFRecord::NDEFRecord(device::mojom::NDEFRecordTypeCategory category,
+                       const String& record_type,
+                       WTF::Vector<uint8_t> data)
+    : category_(category),
+      record_type_(record_type),
+      payload_data_(std::move(data)) {
+  DCHECK_EQ(category_ == device::mojom::NDEFRecordTypeCategory::kExternal,
+            IsValidExternalType(record_type_));
+}
+
 NDEFRecord::NDEFRecord(const ExecutionContext* execution_context,
                        const String& text)
-    : record_type_("text"),
+    : category_(device::mojom::NDEFRecordTypeCategory::kStandardized),
+      record_type_("text"),
       encoding_("utf-8"),
       lang_(getDocumentLanguage(execution_context)),
       payload_data_(GetUTF8DataFromString(text)) {}
 
 NDEFRecord::NDEFRecord(WTF::Vector<uint8_t> payload_data,
                        const String& media_type)
-    : record_type_("mime"), payload_data_(std::move(payload_data)) {
+    : category_(device::mojom::NDEFRecordTypeCategory::kStandardized),
+      record_type_("mime"),
+      payload_data_(std::move(payload_data)) {
   // ExtractMIMETypeFromMediaType() ignores parameters of the MIME type.
   media_type_ = ExtractMIMETypeFromMediaType(AtomicString(media_type));
   if (media_type_.IsEmpty()) {
@@ -356,40 +360,27 @@
 }
 
 NDEFRecord::NDEFRecord(const device::mojom::blink::NDEFRecord& record)
-    : record_type_(record.record_type),
+    : category_(record.category),
+      record_type_(record.record_type),
       media_type_(record.media_type),
       id_(record.id),
       encoding_(record.encoding),
       lang_(record.lang),
       payload_data_(record.data) {
   DCHECK_NE(record_type_ == "mime", media_type_.IsNull());
+  DCHECK_EQ(category_ == device::mojom::NDEFRecordTypeCategory::kExternal,
+            IsValidExternalType(record_type_));
   if (record.payload_message) {
     payload_message_ =
         MakeGarbageCollected<NDEFMessage>(*record.payload_message);
   }
 }
 
-const String& NDEFRecord::recordType() const {
-  return record_type_;
-}
-
 const String& NDEFRecord::mediaType() const {
   DCHECK_NE(record_type_ == "mime", media_type_.IsNull());
   return media_type_;
 }
 
-const String& NDEFRecord::id() const {
-  return id_;
-}
-
-const String& NDEFRecord::encoding() const {
-  return encoding_;
-}
-
-const String& NDEFRecord::lang() const {
-  return lang_;
-}
-
 DOMDataView* NDEFRecord::data() const {
   // Step 4 in https://w3c.github.io/web-nfc/#dfn-parse-an-ndef-record
   if (record_type_ == "empty") {
@@ -401,15 +392,11 @@
   return DOMDataView::Create(dom_buffer, 0, payload_data_.size());
 }
 
-const WTF::Vector<uint8_t>& NDEFRecord::payloadData() const {
-  return payload_data_;
-}
-
 // https://w3c.github.io/web-nfc/#dfn-convert-ndefrecord-payloaddata-bytes
 base::Optional<HeapVector<Member<NDEFRecord>>> NDEFRecord::toRecords(
     ExceptionState& exception_state) const {
   if (record_type_ != "smart-poster" &&
-      ValidateCustomRecordType(record_type_).IsNull()) {
+      category_ != device::mojom::NDEFRecordTypeCategory::kExternal) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kNotSupportedError,
         "Only smart-poster records and external type records could have a ndef "
@@ -423,10 +410,6 @@
   return payload_message_->records();
 }
 
-const NDEFMessage* NDEFRecord::payload_message() const {
-  return payload_message_.Get();
-}
-
 void NDEFRecord::Trace(blink::Visitor* visitor) {
   visitor->Trace(payload_message_);
   ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/nfc/ndef_record.h b/third_party/blink/renderer/modules/nfc/ndef_record.h
index 57611a3..1c4701fa 100644
--- a/third_party/blink/renderer/modules/nfc/ndef_record.h
+++ b/third_party/blink/renderer/modules/nfc/ndef_record.h
@@ -35,29 +35,39 @@
   explicit NDEFRecord(WTF::Vector<uint8_t> /* payload_data */,
                       const String& /* media_type */);
 
-  NDEFRecord(const String& /* record_type */, WTF::Vector<uint8_t> /* data */);
-  NDEFRecord(const String& /* record_type */,
-             const String& /* encoding */,
-             const String& /* lang */,
-             WTF::Vector<uint8_t> /* data */);
+  explicit NDEFRecord(const String& /* record_type */,
+                      WTF::Vector<uint8_t> /* data */);
+  explicit NDEFRecord(const String& /* record_type */,
+                      const String& /* encoding */,
+                      const String& /* lang */,
+                      WTF::Vector<uint8_t> /* data */);
+
+  // All above Ctors by default set the type category to
+  // device::mojom::NDEFRecordTypeCategory::kStandardized.
+
+  explicit NDEFRecord(device::mojom::NDEFRecordTypeCategory /* category */,
+                      const String& /* record_type */,
+                      WTF::Vector<uint8_t> /* data */);
   explicit NDEFRecord(const device::mojom::blink::NDEFRecord&);
 
-  const String& recordType() const;
+  const String& recordType() const { return record_type_; }
   const String& mediaType() const;
-  const String& id() const;
-  const String& encoding() const;
-  const String& lang() const;
+  const String& id() const { return id_; }
+  const String& encoding() const { return encoding_; }
+  const String& lang() const { return lang_; }
   DOMDataView* data() const;
   base::Optional<HeapVector<Member<NDEFRecord>>> toRecords(
       ExceptionState& exception_state) const;
 
-  const WTF::Vector<uint8_t>& payloadData() const;
-  const NDEFMessage* payload_message() const;
+  device::mojom::NDEFRecordTypeCategory category() const { return category_; }
+  const WTF::Vector<uint8_t>& payloadData() const { return payload_data_; }
+  const NDEFMessage* payload_message() const { return payload_message_.Get(); }
 
   void Trace(blink::Visitor*) override;
 
  private:
-  String record_type_;
+  const device::mojom::NDEFRecordTypeCategory category_;
+  const String record_type_;
   String media_type_;
   String id_;
   String encoding_;
diff --git a/third_party/blink/renderer/modules/nfc/nfc_type_converters.cc b/third_party/blink/renderer/modules/nfc/nfc_type_converters.cc
index 141d93c..94e5dcb7 100644
--- a/third_party/blink/renderer/modules/nfc/nfc_type_converters.cc
+++ b/third_party/blink/renderer/modules/nfc/nfc_type_converters.cc
@@ -30,8 +30,8 @@
 NDEFRecordPtr TypeConverter<NDEFRecordPtr, blink::NDEFRecord*>::Convert(
     const blink::NDEFRecord* record) {
   return NDEFRecord::New(
-      record->recordType(), record->mediaType(), record->id(),
-      record->encoding(), record->lang(), record->payloadData(),
+      record->category(), record->recordType(), record->mediaType(),
+      record->id(), record->encoding(), record->lang(), record->payloadData(),
       TypeConverter<NDEFMessagePtr, blink::NDEFMessage*>::Convert(
           record->payload_message()));
 }
diff --git a/third_party/blink/renderer/platform/animation/timing_function.cc b/third_party/blink/renderer/platform/animation/timing_function.cc
index 2ea4d36..5d8e9c8 100644
--- a/third_party/blink/renderer/platform/animation/timing_function.cc
+++ b/third_party/blink/renderer/platform/animation/timing_function.cc
@@ -200,7 +200,7 @@
   if (rhs.GetType() != TimingFunction::Type::CUBIC_BEZIER)
     return false;
 
-  const CubicBezierTimingFunction& ctf = ToCubicBezierTimingFunction(rhs);
+  const auto& ctf = To<CubicBezierTimingFunction>(rhs);
   if ((lhs.GetEaseType() == CubicBezierTimingFunction::EaseType::CUSTOM) &&
       (ctf.GetEaseType() == CubicBezierTimingFunction::EaseType::CUSTOM))
     return (lhs.X1() == ctf.X1()) && (lhs.Y1() == ctf.Y1()) &&
@@ -213,7 +213,7 @@
   if (rhs.GetType() != TimingFunction::Type::STEPS)
     return false;
 
-  const StepsTimingFunction& stf = ToStepsTimingFunction(rhs);
+  const auto& stf = To<StepsTimingFunction>(rhs);
   return (lhs.NumberOfSteps() == stf.NumberOfSteps()) &&
          (lhs.GetStepPosition() == stf.GetStepPosition());
 }
@@ -223,15 +223,15 @@
 bool operator==(const TimingFunction& lhs, const TimingFunction& rhs) {
   switch (lhs.GetType()) {
     case TimingFunction::Type::LINEAR: {
-      const LinearTimingFunction& linear = ToLinearTimingFunction(lhs);
+      const auto& linear = To<LinearTimingFunction>(lhs);
       return (linear == rhs);
     }
     case TimingFunction::Type::CUBIC_BEZIER: {
-      const CubicBezierTimingFunction& cubic = ToCubicBezierTimingFunction(lhs);
+      const auto& cubic = To<CubicBezierTimingFunction>(lhs);
       return (cubic == rhs);
     }
     case TimingFunction::Type::STEPS: {
-      const StepsTimingFunction& step = ToStepsTimingFunction(lhs);
+      const auto& step = To<StepsTimingFunction>(lhs);
       return (step == rhs);
     }
     default:
diff --git a/third_party/blink/renderer/platform/animation/timing_function.h b/third_party/blink/renderer/platform/animation/timing_function.h
index f7a9eae..8cf6ef4 100644
--- a/third_party/blink/renderer/platform/animation/timing_function.h
+++ b/third_party/blink/renderer/platform/animation/timing_function.h
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
@@ -217,14 +218,24 @@
 PLATFORM_EXPORT bool operator==(const TimingFunction&, const TimingFunction&);
 PLATFORM_EXPORT bool operator!=(const TimingFunction&, const TimingFunction&);
 
-#define DEFINE_TIMING_FUNCTION_TYPE_CASTS(typeName, enumName)           \
-  DEFINE_TYPE_CASTS(typeName##TimingFunction, TimingFunction, value,    \
-                    value->GetType() == TimingFunction::Type::enumName, \
-                    value.GetType() == TimingFunction::Type::enumName)
-
-DEFINE_TIMING_FUNCTION_TYPE_CASTS(Linear, LINEAR);
-DEFINE_TIMING_FUNCTION_TYPE_CASTS(CubicBezier, CUBIC_BEZIER);
-DEFINE_TIMING_FUNCTION_TYPE_CASTS(Steps, STEPS);
+template <>
+struct DowncastTraits<LinearTimingFunction> {
+  static bool AllowFrom(const TimingFunction& value) {
+    return value.GetType() == TimingFunction::Type::LINEAR;
+  }
+};
+template <>
+struct DowncastTraits<CubicBezierTimingFunction> {
+  static bool AllowFrom(const TimingFunction& value) {
+    return value.GetType() == TimingFunction::Type::CUBIC_BEZIER;
+  }
+};
+template <>
+struct DowncastTraits<StepsTimingFunction> {
+  static bool AllowFrom(const TimingFunction& value) {
+    return value.GetType() == TimingFunction::Type::STEPS;
+  }
+};
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h b/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h
index 9bab204..cd221b10 100644
--- a/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h
+++ b/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h
@@ -31,7 +31,9 @@
  public:
   static MemoryPressureListenerRegistry& Instance();
 
-  // Whether the device Blink runs on is a low-end device.
+  // See: SysUtils::IsLowEndDevice for the full details of what "low-end" means.
+  // This returns true for devices that can use more extreme tradeoffs for
+  // performance. Many low memory devices (<=1GB) are not considered low-end.
   // Can be overridden in web tests via internals.
   static bool IsLowEndDevice();
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 4701bde..93e9a21 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1924,7 +1924,6 @@
 crbug.com/305376 external/wpt/css/css-overflow/webkit-line-clamp-014.html [ Failure ]
 crbug.com/305376 external/wpt/css/css-overflow/webkit-line-clamp-018.html [ Failure ]
 crbug.com/305376 external/wpt/css/css-overflow/webkit-line-clamp-024.html [ Failure ]
-crbug.com/965137 external/wpt/css/css-overflow/webkit-line-clamp-026.html [ Failure ]
 crbug.com/305376 external/wpt/css/css-overflow/webkit-line-clamp-027.html [ Failure ]
 crbug.com/1007065 external/wpt/css/css-overflow/overflow-codependent-scrollbars.html [ Failure ]
 
@@ -2659,17 +2658,12 @@
 
 crbug.com/1029514 external/wpt/html/semantics/embedded-content/the-video-element/resize-during-playback.html [ Pass Failure ]
 
-# These need WPT fuzzy matching:
-crbug.com/1035194 [ Linux ] external/wpt/css/filter-effects/css-filters-animation-opacity.html [ Failure ]
-crbug.com/1035194 [ Linux ] external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2.html [ Failure ]
-crbug.com/1035194 [ Linux ] virtual/scalefactor200/external/wpt/css/filter-effects/css-filters-animation-opacity.html [ Failure ]
-crbug.com/1035194 [ Linux ] virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2.html [ Failure ]
-
 # css-pseudo-4 ::spelling-error and ::grammar-error not implemented
 crbug.com/1035708 external/wpt/css/css-pseudo/spelling-error-001.html [ Failure ]
 crbug.com/1035708 external/wpt/css/css-pseudo/grammar-error-001.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac10.13 ] virtual/reporting-api/external/wpt/content-security-policy/reporting-api/reporting-api-doesnt-send-reports-without-violation.https.sub.html [ Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/infrastructure/reftest/reftest_match_and_mismatch-6.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/infrastructure/reftest/reftest_match_and_mismatch-6.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/infrastructure/reftest/reftest_match_and_mismatch-6.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index 0f3b0d8..a17e7aec 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -164778,9 +164778,6 @@
    "html/semantics/forms/the-input-element/resources/image-submit-click.html": [
     []
    ],
-   "html/semantics/forms/the-input-element/time-expected.txt": [
-    []
-   ],
    "html/semantics/forms/the-input-element/type-change-state-expected.txt": [
     []
    ],
@@ -232458,6 +232455,12 @@
      {}
     ]
    ],
+   "domxpath/lexical-structure.html": [
+    [
+     "domxpath/lexical-structure.html",
+     {}
+    ]
+   ],
    "domxpath/node-sets.html": [
     [
      "domxpath/node-sets.html",
@@ -252274,6 +252277,12 @@
      {}
     ]
    ],
+   "html/rendering/replaced-elements/attributes-for-embedded-content-and-images/content-aspect-ratio.html": [
+    [
+     "html/rendering/replaced-elements/attributes-for-embedded-content-and-images/content-aspect-ratio.html",
+     {}
+    ]
+   ],
    "html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.html": [
     [
      "html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.html",
@@ -442224,7 +442233,7 @@
    "testharness"
   ],
   "custom-elements/pseudo-class-defined.html": [
-   "60d88cffb517c0062db338e5ba89e98f7748c280",
+   "24cb5fe4cd392246e292d255c0858aa7f2b5dd0e",
    "testharness"
   ],
   "custom-elements/range-and-constructors-expected.txt": [
@@ -442476,7 +442485,7 @@
    "testharness"
   ],
   "custom-elements/upgrading.html": [
-   "d6b8ed387540ae9572774e5557b1168f844a017f",
+   "ac0fdff7a2111507632ac2c36fd5c60a6f832b20",
    "testharness"
   ],
   "custom-elements/upgrading/Document-importNode.html": [
@@ -444587,6 +444596,10 @@
    "0805bd682b48461588250a22018a6bd90990d002",
    "support"
   ],
+  "domxpath/lexical-structure.html": [
+   "f93820b0e4176d8c96518268b6453024dd43f1df",
+   "testharness"
+  ],
   "domxpath/node-sets.html": [
    "a47314fb0869885db01fd13bd8aa35687e283d0d",
    "testharness"
@@ -462820,7 +462833,11 @@
    "testharness"
   ],
   "html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-aspect-ratio.html": [
-   "1d231d52cde8b4758cf834db42231720c374b060",
+   "ab36eeede733f094d2e6340ddf6ce4d82d7a24d6",
+   "testharness"
+  ],
+  "html/rendering/replaced-elements/attributes-for-embedded-content-and-images/content-aspect-ratio.html": [
+   "42be8ce7a81b824f0e62553490ae406b6b736f9f",
    "testharness"
   ],
   "html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.html": [
@@ -467815,16 +467832,12 @@
    "2964032e35283ef4cae28af2b736fa9ba3924260",
    "testharness"
   ],
-  "html/semantics/forms/the-input-element/time-expected.txt": [
-   "80c8abe818f7824433179193bb075846034f6f18",
-   "support"
-  ],
   "html/semantics/forms/the-input-element/time-focus-dynamic-value-change.html": [
    "95ccb1ff69ba9b86f47ad4fdf7468aa1d83f3eb6",
    "testharness"
   ],
   "html/semantics/forms/the-input-element/time.html": [
-   "ad4e38cbc74f50dc764fc958b8cf68f4dd790cbe",
+   "ec815d4cb302cfb68db7976ba79bec1de762b7b7",
    "testharness"
   ],
   "html/semantics/forms/the-input-element/type-change-file-to-text-crash.html": [
@@ -505868,7 +505881,7 @@
    "support"
   ],
   "resources/chromium/nfc-mock.js": [
-   "7eee8ce04c4630ae82d2520def13cfa107b7ef13",
+   "4bafc9b0ad8f7f8ad4185914fffef16949695ab3",
    "support"
   ],
   "resources/chromium/sensor.mojom.js": [
@@ -519920,7 +519933,7 @@
    "support"
   ],
   "tools/wptrunner/wptrunner/manifestinclude.py": [
-   "b3ab2c0776571ffe4ca49e599e0a898c4a7c79a3",
+   "79b5b19b3a3f617e5dd40118042da0b3ea8e4115",
    "support"
   ],
   "tools/wptrunner/wptrunner/manifestupdate.py": [
@@ -525840,7 +525853,7 @@
    "testharness"
   ],
   "web-nfc/NDEFRecord_constructor.https.html": [
-   "d46503ab172bf358bff005ce8c2c56941e333eea",
+   "c62ea7b0d620b6f38f589c751329b40be096e557",
    "testharness"
   ],
   "web-nfc/NDEFWriter-document-hidden-manual.https-expected.txt": [
diff --git a/third_party/blink/web_tests/external/wpt/custom-elements/pseudo-class-defined.html b/third_party/blink/web_tests/external/wpt/custom-elements/pseudo-class-defined.html
index 60d88cf..24cb5fe 100644
--- a/third_party/blink/web_tests/external/wpt/custom-elements/pseudo-class-defined.html
+++ b/third_party/blink/web_tests/external/wpt/custom-elements/pseudo-class-defined.html
@@ -90,4 +90,22 @@
     assert_equals(style.color, expected ? defined : not_defined, 'getComputedStyle');
   }, `${description} should ${expected ? 'be' : 'not be'} :defined`);
 }
+
+test(function () {
+    var log = [];
+    var instance = document.createElement('my-custom-element-2');
+    document.body.appendChild(instance);
+    customElements.define('my-custom-element-2',class extends HTMLElement {
+        constructor() {
+            super();
+            log.push([this, 'begin']);
+            assert_false(this.matches(":defined"), "During construction, this should not match :defined");
+            log.push([this, 'end']);
+        }
+    });
+    assert_equals(log.length, 2);
+    assert_array_equals(log[0], [instance, 'begin']);
+    assert_array_equals(log[1], [instance, 'end']);
+}, 'this.matches(:defined) should not match during an upgrade');
+
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/custom-elements/upgrading.html b/third_party/blink/web_tests/external/wpt/custom-elements/upgrading.html
index d6b8ed3..ac0fdff 100644
--- a/third_party/blink/web_tests/external/wpt/custom-elements/upgrading.html
+++ b/third_party/blink/web_tests/external/wpt/custom-elements/upgrading.html
@@ -12,6 +12,9 @@
 <script src="resources/custom-elements-helpers.js"></script>
 </head>
 <body>
+<infinite-cloning-element-1></infinite-cloning-element-1>
+<infinite-cloning-element-2 id="a"></infinite-cloning-element-2>
+<infinite-cloning-element-2 id="b"></infinite-cloning-element-2>
 <div id="log"></div>
 <script>
 setup({allow_uncaught_exception:true});
@@ -203,6 +206,53 @@
 }, 'If definition\'s disable shadow is true and element\'s shadow root is ' +
     'non-null, then throw a "NotSupportedError" DOMException.');
 
+test(() => {
+    var log = [];
+
+    customElements.define('infinite-cloning-element-1',class extends HTMLElement {
+        constructor() {
+            super();
+            log.push([this, 'begin']);
+            // Potential infinite recursion:
+            customElements.upgrade(this);
+            log.push([this, 'end']);
+        }
+    });
+
+    assert_equals(log.length, 2);
+    const instance = document.querySelector("infinite-cloning-element-1");
+    assert_array_equals(log[0], [instance, 'begin']);
+    assert_array_equals(log[1], [instance, 'end']);
+}, 'Infinite constructor recursion with upgrade(this) should not be possible');
+
+test(() => {
+    var log = [];
+
+    customElements.define('infinite-cloning-element-2',class extends HTMLElement {
+        constructor() {
+            super();
+            log.push([this, 'begin']);
+            const b = document.querySelector("#b");
+            b.remove();
+            // While this constructor is running for "a", "b" is still
+            // undefined, and so inserting it into the document will enqueue a
+            // second upgrade reaction for "b" in addition to the one enqueued
+            // by defining x-foo.
+            document.body.appendChild(b);
+            log.push([this, 'end']);
+        }
+    });
+
+    assert_equals(log.length, 4);
+    const instanceA = document.querySelector("#a");
+    const instanceB = document.querySelector("#b");
+    assert_array_equals(log[0], [instanceA, 'begin']);
+    assert_array_equals(log[1], [instanceB, 'begin']);
+    assert_array_equals(log[2], [instanceB, 'end']);
+    assert_array_equals(log[3], [instanceA, 'end']);
+}, 'Infinite constructor recursion with appendChild should not be possible');
+
+
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/domxpath/lexical-structure.html b/third_party/blink/web_tests/external/wpt/domxpath/lexical-structure.html
new file mode 100644
index 0000000..f93820b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/domxpath/lexical-structure.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<link rel="help" href="https://www.w3.org/TR/1999/REC-xpath-19991116/#exprlex">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+function parse(expression) {
+  document.evaluate(expression, document, null, XPathResult.ANY_TYPE, null);
+}
+
+// https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-Literal
+test(() => {
+  parse(' \'a"bc\' ');
+  parse(' "a\'bc" ');
+
+  assert_throws(new SyntaxError(), () => { parse(' \u2019xyz\u2019 '); });
+}, 'Literal: Only \' and " should be handled as literal quotes.');
+
+// https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-ExprWhitespace
+test(() => {
+  parse(' \t\r\n.\r\n\t ');
+
+  assert_throws(new SyntaxError(), () => { parse('\x0B\x0C .'); });
+  assert_throws(new SyntaxError(), () => { parse('\x0E\x0F .'); });
+  assert_throws(new SyntaxError(), () => { parse('\u3000 .'); });
+  assert_throws(new SyntaxError(), () => { parse('\u2029 .'); });
+}, 'ExprWhitespace: Only #x20 #x9 #xD or #xA must be handled as a whitespace.');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-aspect-ratio.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-aspect-ratio.html
index 1d231d5..ab36eeed 100644
--- a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-aspect-ratio.html
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-aspect-ratio.html
@@ -1,5 +1,5 @@
 <!doctype html>
-<title>Canvas width and height attributes are used to infer aspect-ratio</title>
+<title>Canvas width and height attributes are used as the surface size</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <style>
@@ -11,7 +11,7 @@
 </style>
 <body>
 <script>
-let t = async_test("Canvas width and height attributes are used to infer aspect-ratio");
+let t = async_test("Canvas width and height attributes are used as the surface size");
 function assert_ratio(img, expected) {
   let epsilon = 0.001;
   assert_approx_equals(parseInt(getComputedStyle(img).width, 10) / parseInt(getComputedStyle(img).height, 10), expected, epsilon);
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/content-aspect-ratio.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/content-aspect-ratio.html
new file mode 100644
index 0000000..42be8ce
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/content-aspect-ratio.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>div with content style's width and height attributes are not used to infer aspect-ratio</title>
+<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=201641#c22">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  video {
+    width: 100%;
+    max-width: 100px;
+    height: auto;
+  }
+</style>
+<body>
+<script>
+// Create and append a div with content style and immediately check the height.
+let t = test(function() {
+  var div = document.createElement("div");
+  div.setAttribute("style", "content: url('/images/blue.png')");
+  div.setAttribute("width", "250");
+  div.setAttribute("height", "100");
+  document.body.appendChild(div);
+  assert_equals(getComputedStyle(div).height, "0px");
+}, "div with content style's width and height attributes are not used to infer aspect-ratio");
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/time-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/time-expected.txt
deleted file mode 100644
index 80c8abe..0000000
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/time-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-This is a testharness.js-based test.
-PASS time element of default time value
-PASS step attribute on default value check
-PASS max  attribute on default value check
-PASS min  attribute on default value check
-PASS type attribute support on input element
-PASS max attribute support on input element
-PASS min attribute support on input element
-PASS step attribute support on input element
-PASS stepUp function support on input Element
-PASS stepDown function support on input Element
-PASS stepUp step value empty on default step value 
-PASS stepDown step value empty default step value
-PASS stepUp on step value minus
-PASS stepDown on step value minus
-PASS stepUp on step value zero 
-PASS stepDown on step value zero 
-PASS stepUp on step value 24 hour
-PASS stepDown on step value 24 hour 
-PASS stepUp on step value hour  
-PASS stepDown on step value hour 
-PASS stepUp on step value second 
-PASS stepDown on step value second 
-PASS stepUp on step value with fractional seconds
-PASS stepDown on step value with fractional seconds
-PASS stepUp argument 2 times
-PASS stepDown argument 2 times
-PASS stepUp stop because it exceeds the maximum value
-PASS stepDown stop so lower than the minimum value
-FAIL stop at border on stepUp assert_in_array: a valid time string representing 1 minute after 3pm value "15:00" not in array ["15:01", "15:01:00", "15:01:00.0", "15:01:00.00", "15:01:00.000"]
-PASS stop at border on stepDown
-PASS  empty value of stepUp
-PASS set value on not time format value
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/time.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/time.html
index ad4e38c..ec815d4 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/time.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/time.html
@@ -291,8 +291,11 @@
 } , "stepDown stop so lower than the minimum value");
 
 test(function(){
+  // Set min value to ensure that 15:01 - base is a multiple of 2 min (i.e., a
+  // valid value).
+  _StepTest.min = "14:01";
   _StepTest.max = "15:01";
-  this.add_cleanup(function() { _StepTest.max = ""; });
+  this.add_cleanup(function() { _StepTest.min = _StepTest.max = ""; });
   _StepTest.value = "15:00";
   _StepTest.step = "120";
   _StepTest.stepUp();
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/nfc-mock.js b/third_party/blink/web_tests/external/wpt/resources/chromium/nfc-mock.js
index 7eee8ce..4bafc9b 100644
--- a/third_party/blink/web_tests/external/wpt/resources/chromium/nfc-mock.js
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/nfc-mock.js
@@ -24,6 +24,14 @@
 
 function toMojoNDEFRecord(record) {
   let nfcRecord = new device.mojom.NDEFRecord();
+  if (record.recordType.search(':') != -1) {
+    // Simply checks the existence of ':' to decide whether it's an external
+    // type. As a mock, no need to really implement the validation algo at
+    // https://w3c.github.io/web-nfc/#dfn-validate-external-type.
+    nfcRecord.category = device.mojom.NDEFRecordTypeCategory.kExternal;
+  } else {
+    nfcRecord.category = device.mojom.NDEFRecordTypeCategory.kStandardized;
+  }
   nfcRecord.recordType = record.recordType;
   nfcRecord.mediaType = record.mediaType;
   nfcRecord.id = record.id;
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/manifestinclude.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/manifestinclude.py
index b3ab2c0..79b5b19 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/manifestinclude.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/manifestinclude.py
@@ -6,6 +6,7 @@
 """
 import glob
 import os
+from six import iteritems
 from six.moves.urllib.parse import urlparse, urlsplit
 
 from .wptmanifest.node import DataNode
@@ -94,7 +95,7 @@
         if paths:
             urls = []
             for path in paths:
-                for manifest, data in test_manifests.iteritems():
+                for manifest, data in iteritems(test_manifests):
                     found = False
                     rel_path = os.path.relpath(path, data["tests_path"])
                     iterator = manifest.iterpath if os.path.isfile(path) else manifest.iterdir
diff --git a/third_party/blink/web_tests/external/wpt/web-nfc/NDEFRecord_constructor.https.html b/third_party/blink/web_tests/external/wpt/web-nfc/NDEFRecord_constructor.https.html
index d46503ab..c62ea7b 100644
--- a/third_party/blink/web_tests/external/wpt/web-nfc/NDEFRecord_constructor.https.html
+++ b/third_party/blink/web_tests/external/wpt/web-nfc/NDEFRecord_constructor.https.html
@@ -260,15 +260,15 @@
 
   test(() => {
     assert_throws(new TypeError, () => new NDEFRecord(
-        createRecord('foo.eXamPle.coM:bAr*-', "A string is not a BufferSource or NDEFMessage")),
+        createRecord('foo.eXamPle.com:bAr*-', "A string is not a BufferSource or NDEFMessage")),
         'Only BufferSource and NDEFMessage are allowed to be the record data.');
 
     let buffer = new ArrayBuffer(4);
     new Uint8Array(buffer).set([1, 2, 3, 4]);
     // Feed ArrayBuffer.
     {
-      const record = new NDEFRecord(createRecord('foo.eXamPle.coM:bAr*-', buffer, test_record_id));
-      assert_equals(record.recordType, 'foo.example.com:bAr*-', 'recordType');
+      const record = new NDEFRecord(createRecord('foo.eXamPle.com:bAr*-', buffer, test_record_id));
+      assert_equals(record.recordType, 'foo.eXamPle.com:bAr*-', 'recordType');
       assert_equals(record.mediaType, null, 'mediaType');
       assert_equals(record.id, test_record_id, 'id');
       assert_array_equals(new Uint8Array(record.data.buffer), [1, 2, 3, 4],
@@ -280,8 +280,8 @@
     {
       let buffer_view = new Uint8Array(buffer, 1);
       const record = new NDEFRecord(createRecord(
-            'foo.eXamPle.coM:bAr*-', buffer_view, test_record_id));
-      assert_equals(record.recordType, 'foo.example.com:bAr*-', 'recordType');
+            'foo.eXamPle.com:bAr*-', buffer_view, test_record_id));
+      assert_equals(record.recordType, 'foo.eXamPle.com:bAr*-', 'recordType');
       assert_equals(record.mediaType, null, 'mediaType');
       assert_equals(record.id, test_record_id, 'id');
       assert_array_equals(new Uint8Array(record.data.buffer), [2, 3, 4],
@@ -305,13 +305,23 @@
   }, 'NDEFRecord constructor with record type string being treated as case sensitive');
 
   test(() => {
+    // Length of the external type is 255, OK.
+    const record = new NDEFRecord(createRecord(
+        [...Array(251)].map(_ => 'a').join('') + ':xyz', test_buffer_data));
+    // Exceeding 255, Throws.
+    assert_throws(new TypeError, () => new NDEFRecord(createRecord(
+        [...Array(252)].map(_ => 'a').join('') + ':xyz', test_buffer_data)),
+        'The external type should not be longer than 255.');
+
+    assert_throws(new TypeError, () => new NDEFRecord(createRecord(
+        'xyz', test_buffer_data)), 'The external type must have a \':\'.');
     assert_throws(new TypeError, () => new NDEFRecord(createRecord(
         ':xyz', test_buffer_data)), 'The domain should not be empty.');
     assert_throws(new TypeError, () => new NDEFRecord(createRecord(
-        '[:xyz', test_buffer_data)), '"[" is not a valid FQDN.');
-    assert_throws(new TypeError, () => new NDEFRecord(createRecord(
         'example.com:', test_buffer_data)), 'The type should not be empty.');
     assert_throws(new TypeError, () => new NDEFRecord(createRecord(
+        'example.com:xyz[', test_buffer_data)), 'The type should not contain \'[\'.');
+    assert_throws(new TypeError, () => new NDEFRecord(createRecord(
         'example.com:xyz~', test_buffer_data)), 'The type should not contain \'~\'.');
     assert_throws(new TypeError, () => new NDEFRecord(createRecord(
         'example.com:xyz/', test_buffer_data)), 'The type should not contain \'/\'.');
diff --git a/third_party/blink/web_tests/fast/forms/resources/state-restore-dynamic-controls-frame.html b/third_party/blink/web_tests/fast/forms/resources/state-restore-dynamic-controls-frame.html
index a87c14e6..3ec30e2 100644
--- a/third_party/blink/web_tests/fast/forms/resources/state-restore-dynamic-controls-frame.html
+++ b/third_party/blink/web_tests/fast/forms/resources/state-restore-dynamic-controls-frame.html
@@ -1,7 +1,17 @@
 <!DOCTYPE html>
+<script>
+document.addEventListener('change', e => {
+  parent.inputOrChangeEvents.push('change/' + e.target.type);
+});
+document.addEventListener('input', e => {
+  parent.inputOrChangeEvents.push('input/' + e.target.type);
+});
+</script>
 <div></div>
 <input type=checkbox disabled>
 <input type=text disabled>
+<textarea></textarea>
+<select><option>1<option>2</select>
 <script>
 document.querySelector('div').innerHTML = '<input type=checkbox><input type=text>'
 </script>
diff --git a/third_party/blink/web_tests/fast/forms/state-restore-dynamic-controls.html b/third_party/blink/web_tests/fast/forms/state-restore-dynamic-controls.html
index af57ac4..902e76d 100644
--- a/third_party/blink/web_tests/fast/forms/state-restore-dynamic-controls.html
+++ b/third_party/blink/web_tests/fast/forms/state-restore-dynamic-controls.html
@@ -5,30 +5,52 @@
 <body>
 <iframe src="resources/state-restore-dynamic-controls-frame.html"></iframe>
 <script>
+window.inputOrChangeEvents = [];
+
 promise_test(async t => {
   await waitForEvent(window, 'load', {once:true});
   const iframe = document.querySelector('iframe');
-  const container = iframe.contentDocument.querySelector('div');
+  let doc = iframe.contentDocument;
+  const container = doc.querySelector('div');
   // Change states of controls
   container.firstChild.click();
   container.lastChild.focus();
   eventSender.keyDown('z');
+  doc.querySelector('textarea').focus();
+  eventSender.keyDown('y');
+  doc.querySelector('select').focus();
+  eventSender.keyDown('2');
   assert_true(container.firstChild.checked, 'sanity check for a checkbox');
   assert_equals(container.lastChild.value, 'z', 'sanity check for a text field');
+  assert_equals(doc.querySelector('textarea').value, 'y', 'sanity check for a textarea');
+  assert_equals(doc.querySelector('select').value, '2', 'sanity check for a select');
+
+  // Flush asynchronous input/change events
+  await timeOut(t, 0);
 
   // Navigate
   iframe.src = 'data:text/html,<h1></h1>';
   await waitForEvent(iframe, 'load', {once:true});
 
+  inputOrChangeEvents = []
   // Navigate back
   history.back();
   await waitForEvent(iframe, 'load', {once:true});
   // Wait until finishing the restore task.
   await timeOut(t, 0);
-  const inputs = iframe.contentDocument.querySelectorAll('input');
+  doc = iframe.contentDocument;
+  const inputs = doc.querySelectorAll('input');
   assert_true(inputs[0].checked, 'Checkbox state should be restored.');
   assert_equals(inputs[1].value, 'z', 'Text field state should be restored.');
   assert_false(inputs[2].checked, 'Checkbox should have initial value.');
   assert_equals(inputs[3].value, '', 'Text field should have initial value.');
+  assert_equals(doc.querySelector('textarea').value, 'y');
+  assert_equals(doc.querySelector('select').value, '2');
+
+  // Wait for asynchronous input/change events
+  await timeOut(t, 0);
+  assert_array_equals(inputOrChangeEvents, [
+      'input/checkbox', 'change/checkbox', 'input/text', 'change/text',
+      'input/textarea', 'change/textarea', 'input/select-one', 'change/select-one']);
 }, 'Control states should be restored correctly even if controls were inserted before existing controls.');
 </script>
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/search/search-a11y-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/search/search-a11y-test-expected.txt
new file mode 100644
index 0000000..635e051
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/search/search-a11y-test-expected.txt
@@ -0,0 +1,22 @@
+Tests accessibility in the Search drawer devtool using the axe-core linter.
+
+Running: testSearchNoResults
+Testing search tool with no results.
+aXe violations: []
+
+
+Running: testSearchMultipleResults
+Testing search tool with multiple results.
+aXe violations: []
+
+
+Running: testSearchShowMoreResults
+Testing search tool with many results (generates "Show more" button).
+aXe violations: []
+
+
+Running: testSearchMultipleHighlightedResults
+Testing search tool with multiple highlighted results.
+aXe violations: []
+
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/search/search-a11y-test.js b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/search/search-a11y-test.js
new file mode 100644
index 0000000..4c89905
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/search/search-a11y-test.js
@@ -0,0 +1,95 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.cd
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  TestRunner.addResult('Tests accessibility in the Search drawer devtool using the axe-core linter.');
+  await TestRunner.loadModule('axe_core_test_runner');
+  await TestRunner.showPanel('sources');
+  const view = 'sources.search-sources-tab';
+  await UI.viewManager.showView(view);
+  const searchView = await UI.viewManager.view(view).widget();
+
+  const tests = [
+    testSearchNoResults,
+    testSearchMultipleResults,
+    testSearchShowMoreResults,
+    testSearchMultipleHighlightedResults
+  ];
+
+  async function testSearchNoResults() {
+    TestRunner.addResult('Testing search tool with no results.');
+    searchView._nothingFound();
+    await AxeCoreTestRunner.runValidation(searchView.element);
+  }
+
+  async function testSearchMultipleResults() {
+    TestRunner.addResult('Testing search tool with multiple results.');
+    const searchMatches = [
+      new Common.ContentProvider.SearchMatch(1, 'elle'),
+      new Common.ContentProvider.SearchMatch(2, 'letter'),
+      new Common.ContentProvider.SearchMatch(3, 'couple'),
+      new Common.ContentProvider.SearchMatch(4, 'mole'),
+      new Common.ContentProvider.SearchMatch(5, 'tolerate'),
+      new Common.ContentProvider.SearchMatch(6, 'personable')
+    ];
+    await runTestValidation(searchMatches, 'le');
+  }
+
+  async function testSearchShowMoreResults() {
+    TestRunner.addResult('Testing search tool with many results (generates "Show more" button).');
+    const searchMatches = [
+      new Common.ContentProvider.SearchMatch(1, 'jump'),
+      new Common.ContentProvider.SearchMatch(2, 'maze'),
+      new Common.ContentProvider.SearchMatch(3, 'zoom'),
+      new Common.ContentProvider.SearchMatch(4, 'minx'),
+      new Common.ContentProvider.SearchMatch(5, 'mux'),
+      new Common.ContentProvider.SearchMatch(6, 'comics'),
+      new Common.ContentProvider.SearchMatch(7, 'jimmy'),
+      new Common.ContentProvider.SearchMatch(8, 'jumps'),
+      new Common.ContentProvider.SearchMatch(9, 'bumpy'),
+      new Common.ContentProvider.SearchMatch(10, 'major'),
+      new Common.ContentProvider.SearchMatch(11, 'menace'),
+      new Common.ContentProvider.SearchMatch(12, 'mom'),
+      new Common.ContentProvider.SearchMatch(13, 'animal'),
+      new Common.ContentProvider.SearchMatch(14, 'ham'),
+      new Common.ContentProvider.SearchMatch(15, 'tame'),
+      new Common.ContentProvider.SearchMatch(16, 'pygmy'),
+      new Common.ContentProvider.SearchMatch(17, 'blimp'),
+      new Common.ContentProvider.SearchMatch(18, 'dummy'),
+      new Common.ContentProvider.SearchMatch(19, 'mumsy'),
+      new Common.ContentProvider.SearchMatch(20, 'milch'),
+      new Common.ContentProvider.SearchMatch(21, 'femme'),
+      new Common.ContentProvider.SearchMatch(22, 'grump'),
+      new Common.ContentProvider.SearchMatch(23, 'cramp'),
+      new Common.ContentProvider.SearchMatch(24, 'lumps'),
+      new Common.ContentProvider.SearchMatch(25, 'mumus'),
+      new Common.ContentProvider.SearchMatch(26, 'skelm')
+    ];
+    await runTestValidation(searchMatches, 'm');
+  }
+
+  async function testSearchMultipleHighlightedResults() {
+    TestRunner.addResult('Testing search tool with multiple highlighted results.');
+    const searchMatches = [
+      new Common.ContentProvider.SearchMatch(1, 'test      test'),
+      new Common.ContentProvider.SearchMatch(2, 'testaaaaaaaaaaaaatest'),
+      new Common.ContentProvider.SearchMatch(3, 'test      test'),
+      new Common.ContentProvider.SearchMatch(4, 'g test'),
+    ];
+    await runTestValidation(searchMatches, 'test');
+  }
+
+  async function runTestValidation(searchMatches, queryText) {
+    const uiSourceCode = new Workspace.UISourceCode(/* project */ null, 'test', /* contentType */ null);
+    uiSourceCode.fullDisplayName = function() {return 'test';};
+    const searchResult = new Sources.FileBasedSearchResult(uiSourceCode, searchMatches);
+    const searchConfig = new Search.SearchConfig(queryText, /* ignoreCase */ true, /* isRegex */ false);
+    const searchResultsPane = new Search.SearchResultsPane(searchConfig);
+    searchResultsPane.addSearchResult(searchResult);
+    searchView._showPane(searchResultsPane);
+    await AxeCoreTestRunner.runValidation(searchView.element);
+  }
+
+  TestRunner.runAsyncTestSuite(tests);
+})();
diff --git a/third_party/blink/web_tests/paint/invalidation/duplicated-display-item-ids-table-section-fragments-crash.html b/third_party/blink/web_tests/paint/invalidation/duplicated-display-item-ids-table-section-fragments-crash.html
new file mode 100644
index 0000000..7dd438d0
--- /dev/null
+++ b/third_party/blink/web_tests/paint/invalidation/duplicated-display-item-ids-table-section-fragments-crash.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<style>
+  html, body, span {
+    column-count: 2;
+  }
+  span {
+    display: table;
+  }
+  a {
+    vertical-align: 10000000000000000em;
+  }
+</style>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<span>
+  <a>foo</a>
+</span>
+<script>
+    test(() => {
+    }, "Bug #1025253 (https://crbug.com/1025253): Test passes if it doesn't crash (on display items with duplicated IDs because of the table section having fragments).");
+</script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/calendar-picker/date-picker-open-with-enter-key.html b/third_party/blink/web_tests/virtual/controls-refresh/calendar-picker/date-picker-open-with-enter-key.html
index 36caf17a..3207751 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/calendar-picker/date-picker-open-with-enter-key.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/calendar-picker/date-picker-open-with-enter-key.html
@@ -13,11 +13,7 @@
 test(() => {
   document.getElementById('date').focus();
   eventSender.keyDown("Enter");
-  if (internals.runtimeFlags.formControlsRefreshEnabled) {
-    assert_not_equals(internals.pagePopupWindow, null);
-  } else {
-    assert_equals(internals.pagePopupWindow, null);
-  }
+  assert_not_equals(internals.pagePopupWindow, null);
 }, "Test opening date picker with Enter key.");
 
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/calendar-picker/date-picker-open-with-space-key.html b/third_party/blink/web_tests/virtual/controls-refresh/calendar-picker/date-picker-open-with-space-key.html
index 71bb4e8..a1e1d493 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/calendar-picker/date-picker-open-with-space-key.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/calendar-picker/date-picker-open-with-space-key.html
@@ -13,11 +13,7 @@
 test(() => {
   document.getElementById('date').focus();
   eventSender.keyDown(" ");
-  if (internals.runtimeFlags.formControlsRefreshEnabled) {
-    assert_not_equals(internals.pagePopupWindow, null);
-  } else {
-    assert_equals(internals.pagePopupWindow, null);
-  }
+  assert_not_equals(internals.pagePopupWindow, null);
 }, "Test opening date picker with Space key.");
 
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-accelerated-keyboard-navigation.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-accelerated-keyboard-navigation.html
index 17d7523..4a813aa 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-accelerated-keyboard-navigation.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-accelerated-keyboard-navigation.html
@@ -41,8 +41,6 @@
     assert_equals(rValueContainer.value, '60');
     assert_equals(gValueContainer.value, '144');
     assert_equals(bValueContainer.value, '180');
-  }), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
   }));
 });
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-down-accelerated-keyboard-navigation-from-top-left-corner.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-down-accelerated-keyboard-navigation-from-top-left-corner.html
index 2c332ac..5eaf94d 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-down-accelerated-keyboard-navigation-from-top-left-corner.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-down-accelerated-keyboard-navigation-from-top-left-corner.html
@@ -35,8 +35,6 @@
     assert_equals(rValueContainer.value, '69');
     assert_equals(gValueContainer.value, '69');
     assert_equals(bValueContainer.value, '69');
-  }), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
   }));
 });
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-down-accelerated-keyboard-navigation-from-top-right-corner.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-down-accelerated-keyboard-navigation-from-top-right-corner.html
index 1f1a6f57..6bb0de0c 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-down-accelerated-keyboard-navigation-from-top-right-corner.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-down-accelerated-keyboard-navigation-from-top-right-corner.html
@@ -35,8 +35,6 @@
     assert_equals(rValueContainer.value, '66');
     assert_equals(gValueContainer.value, '0');
     assert_equals(bValueContainer.value, '0');
-  }), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
   }));
 });
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-after-drag.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-after-drag.html
index d5fc13a..d5c8f9f 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-after-drag.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-after-drag.html
@@ -42,8 +42,6 @@
     assert_equals(rValueContainer.value, '123');
     assert_equals(gValueContainer.value, '50');
     assert_equals(bValueContainer.value, '93');
-  }), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
   }));
 });
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-left-accelerated-keyboard-navigation-from-bottom-right-corner.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-left-accelerated-keyboard-navigation-from-bottom-right-corner.html
index f867a36..645a2d2 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-left-accelerated-keyboard-navigation-from-bottom-right-corner.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-left-accelerated-keyboard-navigation-from-bottom-right-corner.html
@@ -35,8 +35,6 @@
     assert_equals(rValueContainer.value, '0');
     assert_equals(gValueContainer.value, '0');
     assert_equals(bValueContainer.value, '0');
-  }), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
   }));
 });
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-left-accelerated-keyboard-navigation-from-top-right-corner.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-left-accelerated-keyboard-navigation-from-top-right-corner.html
index 40aff73..b04455eb 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-left-accelerated-keyboard-navigation-from-top-right-corner.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-left-accelerated-keyboard-navigation-from-top-right-corner.html
@@ -35,8 +35,6 @@
     assert_equals(rValueContainer.value, '255');
     assert_equals(gValueContainer.value, '112');
     assert_equals(bValueContainer.value, '112');
-  }), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
   }));
 });
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-right-accelerated-keyboard-navigation-from-bottom-left-corner.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-right-accelerated-keyboard-navigation-from-bottom-left-corner.html
index cdbdef2..4372d1fb 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-right-accelerated-keyboard-navigation-from-bottom-left-corner.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-right-accelerated-keyboard-navigation-from-bottom-left-corner.html
@@ -35,8 +35,6 @@
     assert_equals(rValueContainer.value, '0');
     assert_equals(gValueContainer.value, '0');
     assert_equals(bValueContainer.value, '0');
-  }), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
   }));
 });
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-right-accelerated-keyboard-navigation-from-top-left-corner.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-right-accelerated-keyboard-navigation-from-top-left-corner.html
index 5a91f98..15fd2a0 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-right-accelerated-keyboard-navigation-from-top-left-corner.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-right-accelerated-keyboard-navigation-from-top-left-corner.html
@@ -35,8 +35,6 @@
     assert_equals(rValueContainer.value, '255');
     assert_equals(gValueContainer.value, '143');
     assert_equals(bValueContainer.value, '143');
-  }), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
   }));
 });
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-up-accelerated-keyboard-navigation-from-bottom-left-corner.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-up-accelerated-keyboard-navigation-from-bottom-left-corner.html
index 3763d2b..7a46a3e 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-up-accelerated-keyboard-navigation-from-bottom-left-corner.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-up-accelerated-keyboard-navigation-from-bottom-left-corner.html
@@ -35,8 +35,6 @@
     assert_equals(rValueContainer.value, '186');
     assert_equals(gValueContainer.value, '186');
     assert_equals(bValueContainer.value, '186');
-  }), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
   }));
 });
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-up-accelerated-keyboard-navigation-from-bottom-right-corner.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-up-accelerated-keyboard-navigation-from-bottom-right-corner.html
index e0e5fa9..47998279 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-up-accelerated-keyboard-navigation-from-bottom-right-corner.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-appearance-color-well-up-accelerated-keyboard-navigation-from-bottom-right-corner.html
@@ -35,8 +35,6 @@
     assert_equals(rValueContainer.value, '189');
     assert_equals(gValueContainer.value, '0');
     assert_equals(bValueContainer.value, '0');
-  }), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
   }));
 });
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-color-well-selection-ring-no-scroll.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-color-well-selection-ring-no-scroll.html
index b5c989298..13ed4fe 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-color-well-selection-ring-no-scroll.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-color-well-selection-ring-no-scroll.html
@@ -28,8 +28,6 @@
     eventSender.mouseDown();
     eventSender.mouseUp();
     assert_false(didColorWellSelectionRingMove(colorWellSelectionRingOriginalRect, colorWellSelectionRing.getBoundingClientRect()), "Color well selection ring should not have moved after it was focused via click.");
-  }), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
   }));
 });
 
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-manual-color-change-invalid-values.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-manual-color-change-invalid-values.html
index 0dfa0bcd..29ba2c9 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-manual-color-change-invalid-values.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-manual-color-change-invalid-values.html
@@ -54,8 +54,6 @@
     assert_equals(hexValueContainer.value, '');
     eventSender.keyDown('Tab');
     assert_equals(hexValueContainer.value, '#00000a', 'Invalid value should be reset when the value container is blurred.');
-  }), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
   }));
 });
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-no-tab-character.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-no-tab-character.html
index 9eba10eb..d7f7243 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-no-tab-character.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-no-tab-character.html
@@ -34,8 +34,6 @@
     eventSender.keyDown('Tab');
     assert_equals(popupDocument.activeElement, bValueContainer, 'bValueContainer should be the active element');
     assert_equals(bValueContainer.value, '255', 'bValueContainer\'s value should not have changed');
-  }), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
   }));
 });
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-zoom150-bottom-edge-no-nan.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-zoom150-bottom-edge-no-nan.html
index aa9a1c6a3..0973219 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-zoom150-bottom-edge-no-nan.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-zoom150-bottom-edge-no-nan.html
@@ -25,8 +25,6 @@
       eventSender.keyDown('ArrowDown', ['ctrlKey']);
     }
     assert_equals(hexValueContainer.value, '#000000', 'Selected color value should be \'#000000\'');
-  }), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup should only not open when the formControlsRefresh flag is disabled.");
   }));
 });
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-suggestion-picker-css-variable-validation.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-suggestion-picker-css-variable-validation.html
index 73b9634..834bfb5 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-suggestion-picker-css-variable-validation.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-suggestion-picker-css-variable-validation.html
@@ -39,10 +39,8 @@
     assert_true(colorSwatchHeight.trim() === '0' || colorSwatchHeight.endsWith('px'), 'Color swatch height must be 0 or end in \'px\'');
     assert_true(colorSwatchPadding.trim() === '0' || colorSwatchPadding.endsWith('px'), 'Color swatch padding must be 0 or end in \'px\'');
     assert_true(colorSwatchWidth.trim() === '0' || colorSwatchWidth.endsWith('px'), 'Color swatch width must be 0 or end in \'px\'');
-    if (internals.runtimeFlags.formControlsRefreshEnabled) {
-      const scrollbarWidth = popupWindow.getComputedStyle(popupDocumentBody).getPropertyValue('--scrollbar-width');
-      assert_true(scrollbarWidth.trim() === '0' || scrollbarWidth.endsWith('px'), 'Scrollbar width must be 0 or end in \'px\'');
-    }
+    const scrollbarWidth = popupWindow.getComputedStyle(popupDocumentBody).getPropertyValue('--scrollbar-width');
+    assert_true(scrollbarWidth.trim() === '0' || scrollbarWidth.endsWith('px'), 'Scrollbar width must be 0 or end in \'px\'');
   }));
 });
 </script>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/datetimelocal-picker/datetimelocal-cancel-select-value.html b/third_party/blink/web_tests/virtual/controls-refresh/datetimelocal-picker/datetimelocal-cancel-select-value.html
index ebdbe687..e66e71de 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/datetimelocal-picker/datetimelocal-cancel-select-value.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/datetimelocal-picker/datetimelocal-cancel-select-value.html
@@ -14,10 +14,6 @@
 let t = async_test('Test cancel select value in datetime-local popup');
 
 function selectValue() {
-  if (!internals.runtimeFlags.formControlsRefreshEnabled) {
-    t.done();
-  }
-
   let dateTimeElement = document.getElementById("datetime-local");
   clickDayCellAt(3, 4);
   clickTimeCellAt(0, 4);
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/datetimelocal-picker/datetimelocal-select-value.html b/third_party/blink/web_tests/virtual/controls-refresh/datetimelocal-picker/datetimelocal-select-value.html
index bb9fe9f..6f5f4ff 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/datetimelocal-picker/datetimelocal-select-value.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/datetimelocal-picker/datetimelocal-select-value.html
@@ -14,10 +14,6 @@
 let t = async_test('Test select value in datetime-local popup');
 
 function selectValue() {
-  if (!internals.runtimeFlags.formControlsRefreshEnabled) {
-    t.done();
-  }
-
   let dateTimeElement = document.getElementById("datetime-local");
   dateTimeElement.addEventListener("change", t.step_func_done(() => {
     assert_equals(dateTimeElement.value, "2019-02-27T17:03");
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-cancel-select-value-with-keyboard.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-cancel-select-value-with-keyboard.html
index a27dfc42..cd096d6 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-cancel-select-value-with-keyboard.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-cancel-select-value-with-keyboard.html
@@ -51,9 +51,7 @@
 }
 
 t.step(() => {
-  openPicker(document.getElementById('time'), t.step_func(cancelSelectValue),  t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup did not open.");
-  }));
+  openPicker(document.getElementById('time'), t.step_func(cancelSelectValue));
 });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-cancel-select-value.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-cancel-select-value.html
index 63045f9..90edc99 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-cancel-select-value.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-cancel-select-value.html
@@ -23,9 +23,7 @@
 }
 
 t.step(() => {
-  openPicker(document.getElementById('time'), t.step_func(cancelSelectValue),  t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup did not open.");
-  }));
+  openPicker(document.getElementById('time'), t.step_func(cancelSelectValue));
 });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-max-value.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-max-value.html
index b145fa3aa..0eb49fcd 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-max-value.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-max-value.html
@@ -26,9 +26,7 @@
 }
 
 t.step(() => {
-  openPicker(document.getElementById('time'), t.step_func(selectValue),  t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup did not open.");
-  }));
+  openPicker(document.getElementById('time'), t.step_func(selectValue));
 });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-min-value.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-min-value.html
index e9bb28d..5e58ac1 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-min-value.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-min-value.html
@@ -27,9 +27,7 @@
 }
 
 t.step(() => {
-  openPicker(document.getElementById('time'), t.step_func(selectValue),  t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup did not open.");
-  }));
+  openPicker(document.getElementById('time'), t.step_func(selectValue));
 });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-value-12-AM.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-value-12-AM.html
index 264c79e..d5ba8b23 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-value-12-AM.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-value-12-AM.html
@@ -28,9 +28,7 @@
 }
 
 t.step(() => {
-  openPicker(document.getElementById('time'), t.step_func(selectValue),  t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup did not open.");
-  }));
+  openPicker(document.getElementById('time'), t.step_func(selectValue));
 });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-value-12-PM.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-value-12-PM.html
index 3d092843..81f3ba56 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-value-12-PM.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-value-12-PM.html
@@ -27,9 +27,7 @@
 }
 
 t.step(() => {
-  openPicker(document.getElementById('time'), t.step_func(selectValue),  t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup did not open.");
-  }));
+  openPicker(document.getElementById('time'), t.step_func(selectValue));
 });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-value-24-hour.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-value-24-hour.html
index b368359f..3d0c7ad 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-value-24-hour.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-invalid-value-24-hour.html
@@ -31,9 +31,7 @@
 }
 
 t.step(() => {
-  openPicker(document.getElementById('time'), t.step_func(selectValue),  t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup did not open.");
-  }));
+  openPicker(document.getElementById('time'), t.step_func(selectValue));
 });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-12-AM.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-12-AM.html
index 7ae07ad..eb3cee6 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-12-AM.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-12-AM.html
@@ -25,9 +25,7 @@
 }
 
 t.step(() => {
-  openPicker(document.getElementById('time'), t.step_func(selectValue),  t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup did not open.");
-  }));
+  openPicker(document.getElementById('time'), t.step_func(selectValue));
 });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-12-PM.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-12-PM.html
index 5656a9b..04f01d6 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-12-PM.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-12-PM.html
@@ -25,9 +25,7 @@
 }
 
 t.step(() => {
-  openPicker(document.getElementById('time'), t.step_func(selectValue),  t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup did not open.");
-  }));
+  openPicker(document.getElementById('time'), t.step_func(selectValue));
 });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-PM-to-AM.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-PM-to-AM.html
index d0ef6f6..3bb1d790 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-PM-to-AM.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-PM-to-AM.html
@@ -25,9 +25,7 @@
 }
 
 t.step(() => {
-  openPicker(document.getElementById('time'), t.step_func(selectValue),  t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup did not open.");
-  }));
+  openPicker(document.getElementById('time'), t.step_func(selectValue));
 });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-step2.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-step2.html
index c31f972..897ddfb 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-step2.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-step2.html
@@ -27,9 +27,7 @@
 }
 
 t.step(() => {
-  openPicker(document.getElementById('time'), t.step_func(selectValue),  t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup did not open.");
-  }));
+  openPicker(document.getElementById('time'), t.step_func(selectValue));
 });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-with-keyboard.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-with-keyboard.html
index aff8437..ea6b1123 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-with-keyboard.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value-with-keyboard.html
@@ -49,9 +49,7 @@
 }
 
 t.step(() => {
-  openPicker(document.getElementById('time'), t.step_func(selectValue),  t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup did not open.");
-  }));
+  openPicker(document.getElementById('time'), t.step_func(selectValue));
 });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value.html
index 224a058..f6622d6 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-select-value.html
@@ -24,9 +24,7 @@
 }
 
 t.step(() => {
-  openPicker(document.getElementById('time'), t.step_func(selectValue),  t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup did not open.");
-  }));
+  openPicker(document.getElementById('time'), t.step_func(selectValue));
 });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-wheel.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-wheel.html
index 9169b4ac..abfbd5a 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-wheel.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-wheel.html
@@ -32,7 +32,5 @@
   }, 200);
 }
 
-openPicker(document.getElementById('time'), t.step_func(test1), t.step_func_done(() => {
-    assert_false(internals.runtimeFlags.formControlsRefreshEnabled, "Popup did not open.");
-  }));
+openPicker(document.getElementById('time'), t.step_func(test1));
 </script>
\ No newline at end of file
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index f84ff66..85769e6f6 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -51031,10 +51031,10 @@
   <int value="40" label="OPEN_URL"/>
   <int value="41" label="WOULD_HAVE_BEEN_USED (Obsolete)"/>
   <int value="42" label="REGISTER_PROTOCOL_HANDLER"/>
-  <int value="43" label="CREATING_AUDIO_STREAM"/>
+  <int value="43" label="CREATING_AUDIO_STREAM (Obsolete)"/>
   <int value="44" label="PAGE_BEING_CAPTURED (Obsolete)"/>
   <int value="45" label="BAD_DEFERRED_REDIRECT"/>
-  <int value="46" label="NAVIGATION_UNCOMMITTED"/>
+  <int value="46" label="NAVIGATION_UNCOMMITTED (Obsolete)"/>
   <int value="47" label="NEW_NAVIGATION_ENTRY (Obsolete)"/>
   <int value="48" label="COOKIE_STORE_NOT_LOADED (Obsolete)"/>
   <int value="49" label="COOKIE_CONFLICT (Obsolete)"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 6f9d10b4..c318dac 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -746,7 +746,7 @@
 </histogram>
 
 <histogram name="AccountManager.LegacySetPrimaryAccountAndUpdateAccountInfo"
-    enum="BooleanUsage" expires_after="2020-04-26">
+    enum="BooleanUsage" expires_after="2020-06-28">
   <owner>sinhak@chromium.org</owner>
   <summary>
     Tracks the usage of the legacy Primary Account setting flow vs the new flow
@@ -1892,7 +1892,7 @@
 </histogram>
 
 <histogram name="Android.ChromeBrowserProvider.CallerHasPermission"
-    enum="ClientAppId" expires_after="M81">
+    enum="ClientAppId" expires_after="2020-06-28">
   <owner>yfriedman@chromium.org</owner>
   <summary>
     Android: Count of requests to Chrome browser which are granted to other apps
@@ -1902,7 +1902,7 @@
 </histogram>
 
 <histogram name="Android.ChromeBrowserProvider.SignaturePassed"
-    enum="ClientAppId" expires_after="M81">
+    enum="ClientAppId" expires_after="2020-06-28">
   <owner>yfriedman@chromium.org</owner>
   <summary>
     Android: Count of requests to Chrome browser which are granted to other apps
@@ -2388,7 +2388,7 @@
 </histogram>
 
 <histogram name="Android.DownloadManager.InitialCount.Viewed" units="units"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>dtrainor@chromium.org</owner>
   <owner>clank-downloads@google.com</owner>
   <summary>
@@ -2457,7 +2457,7 @@
 </histogram>
 
 <histogram name="Android.DownloadManager.NotificationInteraction"
-    enum="DownloadNotificationInteractions" expires_after="M81">
+    enum="DownloadNotificationInteractions" expires_after="2020-06-28">
   <owner>xingliu@chromium.org</owner>
   <owner>clank-downloads@google.com</owner>
   <summary>
@@ -2945,7 +2945,7 @@
 </histogram>
 
 <histogram name="Android.IntentHeaders" enum="IntentHeadersResult"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>peconn@chromium.org</owner>
   <summary>
     Records the usage of the Browser.EXTRA_HEADERS field for Intents that Chrome
@@ -2977,7 +2977,7 @@
 </histogram>
 
 <histogram name="Android.KernelVersion" enum="AndroidKernelVersion"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>rsesek@chromium.org</owner>
   <summary>
     Reports the kernel major and minor version from the utsname.release field.
@@ -3021,7 +3021,7 @@
 
 <histogram
     name="Android.MainActivity.ExplicitMainViewIntentDispatched.OnNewIntent"
-    enum="BooleanDispatched" expires_after="M81">
+    enum="BooleanDispatched" expires_after="2020-06-28">
   <owner>wnwen@chromium.org</owner>
   <summary>
     Whether VIEW intent sent explicitly to .Main activity was dispatched by
@@ -3887,7 +3887,7 @@
 </histogram>
 
 <histogram name="Android.StrictMode.OverrideUrlLoadingTime" units="ms"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>yfriedman@chromium.org</owner>
   <owner>wnwen@chromium.org</owner>
   <summary>
@@ -3909,7 +3909,7 @@
 </histogram>
 
 <histogram name="Android.StrictMode.TabPersistentStore" units="ms"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>wnwen@chromium.org</owner>
   <owner>yfriedman@chromium.org</owner>
   <summary>
@@ -4025,7 +4025,7 @@
 </histogram>
 
 <histogram name="Android.SysUtilsLowEndMatches" enum="BooleanEqual"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>ssid@chromium.org</owner>
   <summary>
     Measures whether Chrome low-end detection logic based on RAM size matches
@@ -4723,7 +4723,7 @@
 </histogram>
 
 <histogram name="AppBanners.UserResponse" enum="AppBannersUserResponse"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>dominickn@chromium.org</owner>
   <summary>
     This stat tracks the user reponse when the add-to-homescreen dialog is shown
@@ -5237,7 +5237,7 @@
 </histogram>
 
 <histogram name="Apps.AppList.DriveQuickAccessProvider.CacheEmpty"
-    enum="BooleanEmpty" expires_after="M81">
+    enum="BooleanEmpty" expires_after="2020-06-28">
   <owner>tby@chromium.org</owner>
   <owner>wrong@chromium.org</owner>
   <owner>jiameng@chromium.org</owner>
@@ -5249,7 +5249,7 @@
 </histogram>
 
 <histogram name="Apps.AppList.DriveQuickAccessProvider.DriveFSMounted"
-    enum="Boolean" expires_after="M81">
+    enum="Boolean" expires_after="2020-06-28">
   <owner>tby@chromium.org</owner>
   <owner>wrong@chromium.org</owner>
   <owner>jiameng@chromium.org</owner>
@@ -5318,7 +5318,7 @@
 </histogram>
 
 <histogram name="Apps.AppList.DriveQuickAccessProvider.ValidResults"
-    units="count" expires_after="M81">
+    units="count" expires_after="2020-06-28">
   <owner>tby@chromium.org</owner>
   <owner>wrong@chromium.org</owner>
   <owner>jiameng@chromium.org</owner>
@@ -5362,7 +5362,7 @@
 </histogram>
 
 <histogram name="Apps.AppList.SearchQueryLength.Apps" units="characters"
-    expires_after="2020-04-29">
+    expires_after="2020-06-28">
   <owner>thanhdng@chromium.org</owner>
   <owner>jiameng@chromium.org</owner>
   <summary>
@@ -5412,7 +5412,7 @@
 </histogram>
 
 <histogram name="Apps.AppList.ZeroStateResults.ReceivedScore.DriveQuickAccess"
-    units="score" expires_after="M81">
+    units="score" expires_after="2020-06-28">
   <owner>wrong@chromium.org</owner>
   <owner>tby@chromium.org</owner>
   <owner>jiameng@chromium.org</owner>
@@ -5424,7 +5424,7 @@
 </histogram>
 
 <histogram name="Apps.AppList.ZeroStateResults.ReceivedScore.OmniboxSearch"
-    units="score" expires_after="M81">
+    units="score" expires_after="2020-06-28">
   <owner>wrong@chromium.org</owner>
   <owner>tby@chromium.org</owner>
   <owner>jiameng@chromium.org</owner>
@@ -5522,7 +5522,7 @@
 </histogram>
 
 <histogram name="Apps.AppList.ZeroStateResultsList.NumImpressionTypesV2"
-    units="count" expires_after="M81">
+    units="count" expires_after="2020-06-28">
   <owner>wrong@chromium.org</owner>
   <owner>tby@chromium.org</owner>
   <owner>jiameng@chromium.org</owner>
@@ -5841,7 +5841,7 @@
 </histogram>
 
 <histogram name="Apps.AppListRecommendedResponse"
-    enum="ReinstallResponseParseResult" expires_after="M81">
+    enum="ReinstallResponseParseResult" expires_after="2020-06-28">
   <owner>napper@chromium.org</owner>
   <owner>robsc@chromium.org</owner>
   <summary>
@@ -5892,7 +5892,7 @@
 </histogram>
 
 <histogram name="Apps.AppListSearchBoxActivated"
-    enum="SearchBoxActivationSource" expires_after="2020-04-26">
+    enum="SearchBoxActivationSource" expires_after="2020-06-28">
 <!-- Name completed by histogram_suffixes name="TabletOrClamshellMode" -->
 
   <owner>newcomer@chromium.org</owner>
@@ -6128,7 +6128,7 @@
 </histogram>
 
 <histogram base="true" name="Apps.ContextMenuExecuteCommand"
-    enum="ChromeOSUICommands" expires_after="M81">
+    enum="ChromeOSUICommands" expires_after="2020-06-28">
 <!-- Name completed by histogram_suffixes
      name="ContextMenuFromApp" -->
 
@@ -6141,7 +6141,7 @@
 </histogram>
 
 <histogram base="true" name="Apps.ContextMenuShowSource" enum="MenuSourceType"
-    expires_after="M81">
+    expires_after="2020-06-28">
 <!-- Name completed by histogram_suffixes
      name="AppUIComponent" -->
 
@@ -6621,7 +6621,7 @@
 
 <histogram base="true"
     name="Apps.StateTransition.Drag.PresentationTime.MaxLatency" units="ms"
-    expires_after="M81">
+    expires_after="2020-06-28">
 <!-- Name completed by histogram_suffixes
      name="TabletOrClamshellMode" -->
 
@@ -6665,7 +6665,7 @@
 </histogram>
 
 <histogram name="Arc.AppInstalledReason" enum="InstallationCounterReasonEnum"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>robsc@chromium.org</owner>
   <owner>napper@chromium.org</owner>
   <summary>
@@ -6716,7 +6716,7 @@
 </histogram>
 
 <histogram name="Arc.AppUninstallReason" enum="UninstallCounterReasonEnum"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>robsc@chromium.org</owner>
   <owner>napper@chromium.org</owner>
   <summary>
@@ -6794,7 +6794,7 @@
 </histogram>
 
 <histogram name="Arc.ContainerLifetimeEvent" enum="ArcContainerLifetimeEvent"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>elijahtaylor@google.com</owner>
   <owner>yusukes@google.com</owner>
   <summary>
@@ -7134,13 +7134,15 @@
   </summary>
 </histogram>
 
-<histogram name="Arc.LowMemoryKiller.FreedSize" units="KB" expires_after="M81">
+<histogram name="Arc.LowMemoryKiller.FreedSize" units="KB"
+    expires_after="2020-06-28">
   <owner>elijahtaylor@google.com</owner>
   <owner>shihuis@google.com</owner>
   <summary>The memory size freed by each low memory kill event.</summary>
 </histogram>
 
-<histogram name="Arc.LowMemoryKiller.TimeDelta" units="ms" expires_after="M81">
+<histogram name="Arc.LowMemoryKiller.TimeDelta" units="ms"
+    expires_after="2020-06-28">
   <owner>elijahtaylor@google.com</owner>
   <owner>shihuis@google.com</owner>
   <summary>The elapsed time to last low memory kill event.</summary>
@@ -7211,7 +7213,7 @@
 </histogram>
 
 <histogram name="Arc.OptInSilentAuthCode.Reauthorization"
-    enum="ArcOptInSilentAuthCode" expires_after="2020-04-26">
+    enum="ArcOptInSilentAuthCode" expires_after="2020-06-28">
   <owner>khmel@google.com</owner>
   <summary>
     Arc Silent Auth Code status. This status is set during the ARC
@@ -7297,7 +7299,7 @@
 </histogram>
 
 <histogram name="Arc.PlayStoreSearch.ReturnedInstantApps" units="apps"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>hejq@chromium.org</owner>
   <summary>
     The number of returned instant apps of a Play Store app discovery query.
@@ -7305,7 +7307,7 @@
 </histogram>
 
 <histogram name="Arc.PlayStoreSearch.ReturnedUninstalledApps" units="apps"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>hejq@chromium.org</owner>
   <summary>
     The number of returned uninstalled apps of a Play Store app discovery query.
@@ -7455,7 +7457,7 @@
 </histogram>
 
 <histogram name="Arc.Secondary.Signin.Result" enum="ArcProvisioningResult"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>khmel@google.com</owner>
   <summary>
     The result (success or the type of failure) of ARC Secondary Account signin/
@@ -7754,7 +7756,7 @@
 </histogram>
 
 <histogram name="Ash.Accelerators.Rotation.Usage"
-    enum="ScreenRotationAcceleratorAction" expires_after="M81">
+    enum="ScreenRotationAcceleratorAction" expires_after="2020-06-28">
   <owner>baileyberro@chromium.org</owner>
   <summary>
     Captures the result of a user using the rotation accelerator -
@@ -7897,7 +7899,7 @@
 </histogram>
 
 <histogram name="Ash.Desks.RemoveDesk" enum="DesksCreationRemovalSource"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>afakhry@chromium.org</owner>
   <summary>
     Emitted when a virtual desk is removed to specify the source of this remove
@@ -8613,7 +8615,7 @@
 </histogram>
 
 <histogram name="Ash.Shelf.Palette.Usage.AutoOpened" enum="PaletteTrayOptions"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>jdufault@chromium.org</owner>
   <summary>
     Recorded every time that the palette option has been selected from the
@@ -8645,7 +8647,7 @@
 </histogram>
 
 <histogram name="Ash.Shelf.TimeBetweenWindowMinimizedAndActivatedActions"
-    units="ms" expires_after="M81">
+    units="ms" expires_after="2020-06-28">
   <owner>bruthig@google.com</owner>
   <owner>tdanderson@google.com</owner>
   <summary>
@@ -8934,7 +8936,8 @@
   </summary>
 </histogram>
 
-<histogram name="Ash.TouchView.LidAngle" units="degrees" expires_after="M81">
+<histogram name="Ash.TouchView.LidAngle" units="degrees"
+    expires_after="2020-06-28">
   <owner>oshima@chromium.org</owner>
   <summary>
     Chrome OS only. The computed angle between the lid and the keyboard panel.
@@ -9414,7 +9417,7 @@
 </histogram>
 
 <histogram name="Ash.WindowSelector.TimeInOverview" units="ms"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>flackr@chromium.org</owner>
   <owner>kuscher@google.com</owner>
   <summary>
@@ -9953,7 +9956,7 @@
 </histogram>
 
 <histogram name="Assistant.ServiceStartTime" units="ms"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>updowndota@chromium.org</owner>
   <summary>Amount of time spent in starting Assistant service.</summary>
 </histogram>
@@ -10473,7 +10476,7 @@
   </summary>
 </histogram>
 
-<histogram name="AsyncDNS.ServerCount" units="units" expires_after="M81">
+<histogram name="AsyncDNS.ServerCount" units="units" expires_after="2020-06-28">
   <owner>pauljensen@chromium.org</owner>
   <owner>mef@chromium.org</owner>
   <summary>
@@ -10853,7 +10856,7 @@
 </histogram>
 
 <histogram name="AsyncDNS.WatchStatus" enum="AsyncDNSWatchStatus"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>pauljensen@chromium.org</owner>
   <owner>mef@chromium.org</owner>
   <summary>
@@ -14295,7 +14298,7 @@
 </histogram>
 
 <histogram name="AutoScreenBrightness.NewCurveSaved.Duration" units="ms"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>jiameng@chromium.org</owner>
   <summary>
     The time elapsed between training start and a new curve saved to disk. Only
@@ -14334,7 +14337,7 @@
 </histogram>
 
 <histogram name="AutoScreenBrightness.PersonalCurveValid" enum="BooleanValid"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>tby@chromium.org</owner>
   <summary>
     Whether the user's personal brightness curve is valid. Chrome OS only.
@@ -14342,7 +14345,7 @@
 </histogram>
 
 <histogram name="AutoScreenBrightness.Same.UserModelBrightnessAdjustments"
-    units="count" expires_after="2020-04-26">
+    units="count" expires_after="2020-06-28">
   <owner>jiameng@chromium.org</owner>
   <summary>
     When user changes brightness manually, if the previous change was caused by
@@ -19461,7 +19464,7 @@
   </summary>
 </histogram>
 
-<histogram name="BlueZ.ChipLost2" units="seconds" expires_after="2020-04-26">
+<histogram name="BlueZ.ChipLost2" units="seconds" expires_after="2020-06-28">
   <owner>sonnysasaka@chromium.org</owner>
   <summary>
     This is specific to Chrome OS. Records a duration of a Bluetooth adapter
@@ -19543,7 +19546,7 @@
 </histogram>
 
 <histogram name="BlueZ.TimeLengthOfDiscovering" units="seconds"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>mcchou@chromium.org</owner>
   <summary>
     This is specific to Chrome OS. Records the duration for the local device to
@@ -20028,7 +20031,7 @@
 </histogram>
 
 <histogram base="true" name="Browser.Tabs.TabSwitchResult"
-    enum="TabSwitchResult" expires_after="M81">
+    enum="TabSwitchResult" expires_after="2020-06-28">
 <!-- Name completed by histogram_suffixes name="TabSwitchingType" -->
 
   <owner>fdoray@chromium.org</owner>
@@ -20048,7 +20051,7 @@
 </histogram>
 
 <histogram base="true" name="Browser.Tabs.TotalSwitchDuration" units="ms"
-    expires_after="M81">
+    expires_after="2020-06-28">
 <!-- Name completed by histogram_suffixes name="TabSwitchingType" -->
 
   <owner>ejoe@google.com</owner>
@@ -20157,7 +20160,7 @@
 </histogram>
 
 <histogram name="BrowserRenderProcessHost.ChildKills.OOM" enum="RendererType"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>oshima@chromium.org</owner>
   <summary>
     Out of BrowserRenderProcessHost.ChildKills, numer of kills due to SIGKILL,
@@ -24861,7 +24864,7 @@
 </histogram>
 
 <histogram name="ConnectivityDiagnostics.ChromeOsSignalStrength" units="%"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>ebeach@google.com</owner>
   <summary>
     Connectivity Diagnostics App: WiFi signal strength recorded during
@@ -25022,7 +25025,7 @@
 </histogram>
 
 <histogram name="ContentSettings.DefaultAutoplaySetting" enum="ContentSetting"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>mlamouri@chromium.org</owner>
   <summary>The default autoplay setting at profile open.</summary>
 </histogram>
@@ -25118,7 +25121,7 @@
 </histogram>
 
 <histogram name="ContentSettings.DefaultPluginsSetting" enum="ContentSetting"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>toyoshim@chromium.org</owner>
   <summary>The default plugins setting at profile open.</summary>
 </histogram>
@@ -25364,7 +25367,7 @@
 </histogram>
 
 <histogram name="ContentSettings.PermissionRequested" enum="PermissionType"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>dominickn@chromium.org</owner>
   <owner>engedy@chromium.org</owner>
   <owner>hkamila@chromium.org</owner>
@@ -25451,7 +25454,7 @@
 </histogram>
 
 <histogram name="ContentSettings.Popups.BlockerActions"
-    enum="PopupBlockerAction" expires_after="M81">
+    enum="PopupBlockerAction" expires_after="2020-06-28">
   <owner>csharrison@chromium.org</owner>
   <summary>
     Counts of various events related to the popup blocker. Including blocked
@@ -25462,7 +25465,7 @@
 </histogram>
 
 <histogram name="ContentSettings.Popups.ClickThroughPosition"
-    enum="ListItemPosition" expires_after="M81">
+    enum="ListItemPosition" expires_after="2020-06-28">
   <owner>csharrison@chromium.org</owner>
   <summary>
     The blocked popup list contains a list of links that Chrome has blocked via
@@ -25529,7 +25532,7 @@
 </histogram>
 
 <histogram name="ContentSettings.Popups.StrongBlockerActions"
-    enum="StrongPopupBlockerAction" expires_after="M81">
+    enum="StrongPopupBlockerAction" expires_after="2020-06-28">
   <owner>csharrison@chromium.org</owner>
   <summary>
     Counts of various events related to the strong popup blocker (aka abusive
@@ -27236,7 +27239,7 @@
 </histogram>
 
 <histogram name="Cookie.Startup.NumberOfCookiesDeleted" units="units"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>erikchen@chromium.org</owner>
   <summary>
     The number of session cookies deleted on startup. This metric is emitted
@@ -27340,7 +27343,7 @@
   </summary>
 </histogram>
 
-<histogram name="Cookie.TimeInitializeDB" units="ms" expires_after="M81">
+<histogram name="Cookie.TimeInitializeDB" units="ms" expires_after="2020-06-28">
   <owner>nyquist@chromium.org</owner>
   <summary>The amount of time (ms) to initialize the cookies database.</summary>
 </histogram>
@@ -27374,7 +27377,7 @@
   </summary>
 </histogram>
 
-<histogram name="Cookie.TimeLoad" units="ms" expires_after="M81">
+<histogram name="Cookie.TimeLoad" units="ms" expires_after="2020-06-28">
   <owner>pwnall@chromium.org</owner>
   <summary>
     This histogram records the sum of the durations of all initial tasks loading
@@ -37830,7 +37833,7 @@
 </histogram>
 
 <histogram name="Download.ContentType.Audio" enum="DownloadAudioType"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>xingliu@chromium.org</owner>
   <summary>Types of audio files that are downloaded.</summary>
 </histogram>
@@ -37844,7 +37847,7 @@
 </histogram>
 
 <histogram name="Download.ContentType.Text" enum="DownloadTextType"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>xingliu@chromium.org</owner>
   <summary>Types of text files that are downloaded.</summary>
 </histogram>
@@ -38091,7 +38094,7 @@
 </histogram>
 
 <histogram name="Download.DownloadManager.CreationDelay" units="ms"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>qinmin@chromium.org</owner>
   <owner>dtrainor@chromium.org</owner>
   <summary>
@@ -38101,7 +38104,7 @@
 </histogram>
 
 <histogram name="Download.DownloadManager.MemoryUsage" units="KB"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>qinmin@chromium.org</owner>
   <owner>dtrainor@chromium.org</owner>
   <summary>
@@ -38278,7 +38281,7 @@
 </histogram>
 
 <histogram name="Download.InsecureBlocking.Totals"
-    enum="InsecureDownloadSecurityStatus" expires_after="M81">
+    enum="InsecureDownloadSecurityStatus" expires_after="2020-06-28">
   <owner>jdeblasio@chromium.org</owner>
   <owner>estark@chromium.org</owner>
   <owner>cthomp@chromium.org</owner>
@@ -38356,7 +38359,7 @@
 </histogram>
 
 <histogram base="true" name="Download.InterruptedReason" enum="InterruptReason"
-    expires_after="M81">
+    expires_after="2020-06-28">
 <!-- Name completed by histogram_suffixes name="DownloadSource" -->
 
   <owner>xingliu@chromium.org</owner>
@@ -38413,7 +38416,7 @@
 </histogram>
 
 <histogram name="Download.IOSDownloadARModelState.USDZ"
-    enum="DownloadARModelState" expires_after="M81">
+    enum="DownloadARModelState" expires_after="2020-06-28">
   <owner>mahmadi@chromium.org</owner>
   <summary>
     Logged at different stages of downloading a USDZ MIME type AR model on iOS.
@@ -38751,7 +38754,7 @@
 </histogram>
 
 <histogram name="Download.ParallelDownload.CreationFailureReason"
-    enum="InterruptReason" expires_after="M81">
+    enum="InterruptReason" expires_after="2020-06-28">
   <owner>qinmin@chromium.org</owner>
   <owner>xingliu@chromium.org</owner>
   <summary>
@@ -38803,7 +38806,7 @@
 </histogram>
 
 <histogram name="Download.Parallelizable.DownloadTime" units="ms"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>xingliu@chromium.org</owner>
   <summary>The download time for a parallelizable download.</summary>
 </histogram>
@@ -38815,7 +38818,7 @@
 </histogram>
 
 <histogram base="true" name="Download.ParallelizableDownloadBandwidth"
-    units="bytes/second" expires_after="M81">
+    units="bytes/second" expires_after="2020-06-28">
   <owner>qinmin@chromium.org</owner>
   <summary>
     For parallelizable download, average disk bandwidth seen for different
@@ -39192,7 +39195,7 @@
 </histogram>
 
 <histogram name="Download.Service.Recovery" enum="Download.Service.EntryStates"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>xingliu@chromium.org</owner>
   <summary>
     The state that the entry transitions to when recovery operation happens.
@@ -39379,7 +39382,7 @@
 </histogram>
 
 <histogram name="Download.SourcesChrome" enum="ChromeDownloadSource"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>xingliu@chromium.org</owner>
   <summary>
     The initiation source (if initiated within the above-content layer of
@@ -40158,7 +40161,7 @@
 </histogram>
 
 <histogram name="EasyUnlock.AuthProximity.RollingRssi" units="dBm"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>hansberry@chromium.org</owner>
   <summary>
     Measures the exponentially weighted rolling average of the received signal
@@ -40861,7 +40864,7 @@
 </histogram>
 
 <histogram name="Enterprise.DevicePolicyInvalidations"
-    enum="EnterprisePolicyInvalidations" expires_after="M81">
+    enum="EnterprisePolicyInvalidations" expires_after="2020-06-28">
   <owner>bartfab@chromium.org</owner>
   <summary>
     Events for counting device policy invalidations received with and without
@@ -40899,7 +40902,7 @@
 </histogram>
 
 <histogram name="Enterprise.DMServerRequestSuccess"
-    enum="EnterpriseDMServerRequestSuccess" expires_after="2020-04-26">
+    enum="EnterpriseDMServerRequestSuccess" expires_after="2020-06-28">
   <owner>poromov@chromium.org</owner>
   <summary>
     Number of retries the client did to execute a DeviceManagementServer
@@ -40971,7 +40974,7 @@
 </histogram>
 
 <histogram name="Enterprise.Enrollment" enum="EnterpriseEnrollmentType"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>mnissler@chromium.org</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
@@ -41843,7 +41846,7 @@
 </histogram>
 
 <histogram name="Enterprise.UserSession.Logins"
-    enum="EnterpriseUserSessionLogins" expires_after="2020-04-26">
+    enum="EnterpriseUserSessionLogins" expires_after="2020-06-28">
   <owner>xiyuan@chromium.org</owner>
   <owner>sduraisamy@chromium.org</owner>
   <summary>Tracks the sign-in events on an enrolled device.</summary>
@@ -41971,7 +41974,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.IsEnterpriseUser" enum="BooleanEnabled"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>rogerta@chromium.org</owner>
   <summary>
     Whether the machine is considered an enterprise user. An enterprise user is
@@ -42129,7 +42132,7 @@
 </histogram>
 
 <histogram name="Event.AsyncTargeting.AsyncClientDepth" units="clients"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>yigu@chromium.org</owner>
   <owner>event-targeting@chromium.org</owner>
   <summary>
@@ -42139,7 +42142,7 @@
 </histogram>
 
 <histogram name="Event.AsyncTargeting.ResponseTime" units="ms"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>yigu@chromium.org</owner>
   <summary>
     Tracks how long it takes for a client to respond to an asynchronous request
@@ -42150,7 +42153,7 @@
 </histogram>
 
 <histogram name="Event.AsyncTargeting.TimeInQueue" units="ms"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>yigu@chromium.org</owner>
   <summary>
     Tracks how long an event has to wait in the queue while a previous event is
@@ -42758,7 +42761,7 @@
 </histogram>
 
 <histogram name="Event.Latency.BlockingTime.TouchMoveDefaultPrevented"
-    units="ms" expires_after="M81">
+    units="ms" expires_after="2020-06-28">
   <owner>tdresser@chromium.org</owner>
   <summary>
     Time between the renderer main thread receiving a touchmove event and acking
@@ -43456,7 +43459,8 @@
   </summary>
 </histogram>
 
-<histogram name="Event.Latency.OS" units="microseconds" expires_after="M81">
+<histogram name="Event.Latency.OS" units="microseconds"
+    expires_after="2020-06-28">
   <owner>tdresser@chromium.org</owner>
   Team: input-dev@chromium.org.
   <summary>
@@ -43722,7 +43726,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollBegin.Scrollbar.HandledToRendererSwap2"
-    units="microseconds" expires_after="M81">
+    units="microseconds" expires_after="2020-06-28">
   <owner>input-dev@chromium.org</owner>
   <owner>dlibby@microsoft.com</owner>
   <summary>
@@ -44388,7 +44392,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollUpdate.Scrollbar.HandledToRendererSwap2"
-    units="microseconds" expires_after="M81">
+    units="microseconds" expires_after="2020-06-28">
   <owner>input-dev@chromium.org</owner>
   <owner>dlibby@microsoft.com</owner>
   <summary>
@@ -45372,7 +45376,7 @@
 </histogram>
 
 <histogram name="Event.Touch.GestureTarget" enum="BrowserGestureActionType"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>kuscher@google.com</owner>
   <owner>rbyers@chromium.org</owner>
   <summary>
@@ -45813,7 +45817,7 @@
 </histogram>
 
 <histogram name="Event.VizHitTest.AsyncHitTestReasons"
-    enum="AsyncHitTestReasons" expires_after="M81">
+    enum="AsyncHitTestReasons" expires_after="2020-06-28">
   <owner>yigu@chromium.org</owner>
   <summary>
     Tracks the reasons why sychronous hit testing could not be done for each hit
@@ -45834,7 +45838,7 @@
 </histogram>
 
 <histogram name="Event.VizHitTest.HitTestRegions" units="regions"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>yigu@chromium.org</owner>
   <owner>event-targeting@chromium.org</owner>
   <summary>
@@ -45856,7 +45860,7 @@
 </histogram>
 
 <histogram name="Event.VizHitTest.TargetTimeUs" units="microseconds"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>yigu@chromium.org</owner>
   <owner>event-targeting@chromium.org</owner>
   <summary>
@@ -45879,7 +45883,7 @@
 </histogram>
 
 <histogram name="Event.VizHitTest.TransformTimeUs" units="microseconds"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>yigu@chromium.org</owner>
   <owner>event-targeting@chromium.org</owner>
   <summary>
@@ -55411,7 +55415,8 @@
   </summary>
 </histogram>
 
-<histogram name="GPU.ContextLost" enum="ContextLostReason" expires_after="M81">
+<histogram name="GPU.ContextLost" enum="ContextLostReason"
+    expires_after="2020-06-28">
   <owner>sievers@chromium.org</owner>
   <summary>
     The reason a GPU command buffer context of a given type was lost.
@@ -55957,12 +55962,12 @@
 </histogram>
 
 <histogram name="GPU.EGLDisplayType" enum="EGLDisplayType"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>jbauman@chromium.org</owner>
   <summary>The display type used to ask for an EGLDisplay.</summary>
 </histogram>
 
-<histogram name="GPU.Error" enum="GLError" expires_after="M81">
+<histogram name="GPU.Error" enum="GLError" expires_after="2020-06-28">
   <owner>zmo@chromium.org</owner>
   <owner>vmiura@chromium.org</owner>
   <summary>The error states generated by OpenGL calls.</summary>
@@ -56055,7 +56060,7 @@
 </histogram>
 
 <histogram name="GPU.GPUChannelHostWaitTime.MicroSeconds" units="microseconds"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>kylechar@chromium.org</owner>
   <summary>
     Records how long the browser UI thread spent blocked for a sync IPC sent
@@ -56111,7 +56116,7 @@
 </histogram>
 
 <histogram name="GPU.GPUProcessInitialized" enum="BooleanSuccess"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>vmiura@chromium.org</owner>
   <summary>
     Whether the GPU process successfully initialized or failed and then exitted
@@ -56131,7 +56136,7 @@
 </histogram>
 
 <histogram name="GPU.GPUProcessLaunchTime" units="ms"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>vmiura@chromium.org</owner>
   <summary>
     Startup time of the GPU process as measured by the GPU process host.
@@ -56458,7 +56463,7 @@
 </histogram>
 
 <histogram name="GPU.ProcessLifetimeEvents.HardwareAccelerated"
-    enum="GPUProcessLifetimeEvent" expires_after="M81">
+    enum="GPUProcessLifetimeEvent" expires_after="2020-06-28">
   <owner>vmiura@chromium.org</owner>
   <summary>
     Recorded once for every GPU process launch and crash when GPU process is
@@ -62191,7 +62196,7 @@
 </histogram>
 
 <histogram name="IOS.NTP.Impression" enum="IOSNTPImpression"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>gambard@chromium.org</owner>
   <summary>
     The type of NTP impressions on iOS, split by type of suggestions shown
@@ -62976,7 +62981,7 @@
 </histogram>
 
 <histogram name="KeyboardAccessory.AccessorySheetSuggestionCount" units="count"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>fhorschig@chromium.org</owner>
   <summary>
     Android only. Records how many suggestions a user faced when opening a
@@ -62985,7 +62990,7 @@
 </histogram>
 
 <histogram name="KeyboardAccessory.AccessorySheetSuggestionsSelected"
-    enum="AccessorySuggestionType" expires_after="M81">
+    enum="AccessorySuggestionType" expires_after="2020-06-28">
   <owner>fhorschig@chromium.org</owner>
   <summary>
     Android only. Records which type of suggestion was selected from an open
@@ -62994,7 +62999,7 @@
 </histogram>
 
 <histogram name="KeyboardAccessory.AccessorySheetTriggered"
-    enum="AccessorySheetTrigger" expires_after="M81">
+    enum="AccessorySheetTrigger" expires_after="2020-06-28">
   <owner>fhorschig@chromium.org</owner>
   <summary>
     Android only. Records how often the bottom sheet was opened or closed by a
@@ -63040,7 +63045,8 @@
   <summary>Tracks kiosk launch errors.</summary>
 </histogram>
 
-<histogram name="Kiosk.LaunchType" enum="KioskLaunchType" expires_after="M81">
+<histogram name="Kiosk.LaunchType" enum="KioskLaunchType"
+    expires_after="2020-06-28">
   <owner>xiyuan@chromium.org</owner>
   <owner>aghuie@chromium.org</owner>
   <summary>
@@ -63125,7 +63131,7 @@
 </histogram>
 
 <histogram name="LanguageSettings.PageImpression"
-    enum="LanguageSettingsPageType" expires_after="2020-04-26">
+    enum="LanguageSettingsPageType" expires_after="2020-06-28">
   <owner>googleo@chromium.org</owner>
   <summary>The type of panes which language settings loads.</summary>
 </histogram>
@@ -63441,7 +63447,7 @@
   </summary>
 </histogram>
 
-<histogram name="LevelDB.Open" enum="LevelDBStatus" expires_after="M81">
+<histogram name="LevelDB.Open" enum="LevelDBStatus" expires_after="2020-06-28">
   <owner>cmumford@chromium.org</owner>
   <summary>The result of an open attempt of a leveldb.</summary>
 </histogram>
@@ -63466,7 +63472,8 @@
   </summary>
 </histogram>
 
-<histogram name="LevelDB.SharedCache.KBUsed" units="KB" expires_after="M81">
+<histogram name="LevelDB.SharedCache.KBUsed" units="KB"
+    expires_after="2020-06-28">
   <owner>cmumford@chromium.org</owner>
   <summary>
     The estimated size (in kilobytes) of the leveldb shared cache. Recorded once
@@ -63705,7 +63712,7 @@
   </summary>
 </histogram>
 
-<histogram name="Linux.Distro" enum="LinuxDistro" expires_after="M81">
+<histogram name="Linux.Distro" enum="LinuxDistro" expires_after="2020-06-28">
   <owner>thomasanderson@chromium.org</owner>
   <summary>The Linux distro used. Logged on each start up.</summary>
 </histogram>
@@ -63729,7 +63736,7 @@
 </histogram>
 
 <histogram name="Linux.WindowManager" enum="LinuxWindowManagerName"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>pkotwicz@chromium.org</owner>
   <summary>
     The window manager used. (Linux only) Logged on each start up.
@@ -64306,7 +64313,7 @@
 </histogram>
 
 <histogram name="Login.TokenCheckResponseTime" units="ms"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>glevin@chromium.org</owner>
   <summary>
     Time between sending a request to, and receiving a reply from, GAIA token
@@ -76524,7 +76531,7 @@
 <histogram
     name="MultiDevice.DeviceSyncService.FindEligibleDevices.Result.FailureReason"
     enum="MultiDevice_DeviceSyncService_DeviceSyncRequestFailureReason"
-    expires_after="2020-03-01">
+    expires_after="2020-06-28">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -97515,7 +97522,8 @@
   </summary>
 </histogram>
 
-<histogram name="NQE.Kbps.RawObservation" units="Kbps" expires_after="M81">
+<histogram name="NQE.Kbps.RawObservation" units="Kbps"
+    expires_after="2020-06-28">
   <obsolete>
     Obsoleted in M81.
   </obsolete>
@@ -97757,7 +97765,8 @@
   </summary>
 </histogram>
 
-<histogram name="NQE.RTT.OnECTComputation" units="ms" expires_after="M81">
+<histogram name="NQE.RTT.OnECTComputation" units="ms"
+    expires_after="2020-06-28">
   <owner>bengr@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -102811,7 +102820,7 @@
 </histogram>
 
 <histogram name="Overscroll.Started3" enum="OverscrollNavigationType"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>nzolghadr@chromium.org</owner>
   <summary>
     Overscroll gestures initiated by the user. Note that not all overscroll
@@ -119461,7 +119470,7 @@
 </histogram>
 
 <histogram name="Previews.ServerLitePage.PreconnectedToPreviewServer"
-    enum="PreviewOrOriginServerConnection" expires_after="M81">
+    enum="PreviewOrOriginServerConnection" expires_after="2020-06-28">
   <owner>robertogden@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -119472,7 +119481,7 @@
 </histogram>
 
 <histogram name="Previews.ServerLitePage.PredictorToggled"
-    enum="BooleanToggled" expires_after="M81">
+    enum="BooleanToggled" expires_after="2020-06-28">
   <owner>robertogden@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -119483,7 +119492,7 @@
 </histogram>
 
 <histogram name="Previews.ServerLitePage.PreresolvedToPreviewServer"
-    enum="PreviewOrOriginServerConnection" expires_after="M81">
+    enum="PreviewOrOriginServerConnection" expires_after="2020-06-28">
   <owner>robertogden@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -119612,7 +119621,7 @@
 </histogram>
 
 <histogram name="Previews.Triggered.EffectiveConnectionType2"
-    enum="NQEEffectiveConnectionType" expires_after="M81">
+    enum="NQEEffectiveConnectionType" expires_after="2020-06-28">
   <owner>dougarnett@chromium.org</owner>
   <summary>
     Records the effective connection type of a navigation that triggers a
@@ -120219,7 +120228,7 @@
 </histogram>
 
 <histogram name="PrintPreview.PrintSettings" enum="PrintSettings"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>thestig@chromium.org</owner>
   <summary>
     Track the popularity of print settings. (Settings when printing to PDF are
@@ -121436,7 +121445,8 @@
   <summary>Whether a ProtoDB Get call was successful or not.</summary>
 </histogram>
 
-<histogram name="ProtoDB.InitStatus" enum="LevelDBStatus" expires_after="M81">
+<histogram name="ProtoDB.InitStatus" enum="LevelDBStatus"
+    expires_after="2020-06-28">
   <owner>nyquist@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <summary>The LevelDB Status from a ProtoDatabase Init call.</summary>
@@ -130630,7 +130640,7 @@
 </histogram>
 
 <histogram name="SB2.ResourceTypes2" enum="ContentResourceType2"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -132338,7 +132348,7 @@
 </histogram>
 
 <histogram name="SBIRS.UploadResult" enum="ReportProcessingResult"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>caitkp@google.com</owner>
   <summary>
     The result of an attempted report upload by the safe browsing incident
@@ -146896,7 +146906,7 @@
 </histogram>
 
 <histogram name="Stability.Android.StrongBindingOomRemainingStrongBindingCount"
-    units="units" expires_after="2020-04-26">
+    units="units" expires_after="2020-06-28">
   <owner>boliu@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <summary>
@@ -146948,7 +146958,7 @@
 </histogram>
 
 <histogram name="Stability.BadMessageTerminated.NaCl"
-    enum="BadMessageReasonNaCl" expires_after="M81">
+    enum="BadMessageReasonNaCl" expires_after="2020-06-28">
   <owner>creis@chromium.org</owner>
   <owner>jamescook@chromium.org</owner>
   <summary>
@@ -147559,7 +147569,7 @@
 </histogram>
 
 <histogram name="Startup.Android.StartupTabPreloader.TabLoaded" units="Boolean"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>alexclarke@chromium.org</owner>
   <owner>skyostil@chromium.org</owner>
   <summary>
@@ -147571,7 +147581,7 @@
 </histogram>
 
 <histogram name="Startup.Android.StartupTabPreloader.TabTaken" units="Boolean"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>alexclarke@chromium.org</owner>
   <owner>skyostil@chromium.org</owner>
   <summary>
@@ -147680,7 +147690,7 @@
 </histogram>
 
 <histogram name="Startup.BrowserLaunchURLCount" units="urls"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>rkaplow@chromium.org</owner>
   <summary>
     Measured when Chrome is invoked, this counts the number of URLs passed via
@@ -147741,7 +147751,7 @@
 </histogram>
 
 <histogram name="Startup.BrowserMessageLoopStart.To.NonEmptyPaint2" units="ms"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>asvitkine@chromium.org</owner>
   <summary>
     Time between Startup.BrowserMessageLoopStartTime and
@@ -147792,7 +147802,7 @@
 </histogram>
 
 <histogram name="Startup.BrowserMessageLoopStartTime" units="ms"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>fdoray@chromium.org</owner>
   <summary>
     Time from browser startup to the start of the main thread's message loop.
@@ -147858,7 +147868,7 @@
 </histogram>
 
 <histogram name="Startup.BrowserMessageLoopStartTimeFromMainEntry3" units="ms"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>fdoray@chromium.org</owner>
   <owner>gab@chromium.org</owner>
   <summary>
@@ -147866,7 +147876,8 @@
   </summary>
 </histogram>
 
-<histogram name="Startup.BrowserOpenTabs" units="units" expires_after="M81">
+<histogram name="Startup.BrowserOpenTabs" units="units"
+    expires_after="2020-06-28">
   <owner>fdoray@chromium.org</owner>
   <summary>
     Time taken to open the initial tab or to restore tabs from previous session.
@@ -148062,7 +148073,7 @@
 </histogram>
 
 <histogram name="Startup.FirstWebContents.FinishReason"
-    enum="StartupProfilingFinishReason" expires_after="M81">
+    enum="StartupProfilingFinishReason" expires_after="2020-06-28">
   <owner>gab@chromium.org</owner>
   <summary>
     [Desktop] The reason for which startup profiling was deemed complete. Logged
@@ -148113,7 +148124,7 @@
 </histogram>
 
 <histogram name="Startup.FirstWebContents.MainNavigationStart" units="ms"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>gab@chromium.org</owner>
   <summary>
     [Desktop] Measure the elapsed time from process launch to the beginning of
@@ -149084,13 +149095,13 @@
 </histogram>
 
 <histogram base="true" name="Storage.BytesRead" units="bytes"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>michaeln@chromium.org</owner>
   <summary>The number of bytes read. Recorded on each read.</summary>
 </histogram>
 
 <histogram base="true" name="Storage.BytesWritten" units="bytes"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>michaeln@chromium.org</owner>
   <summary>The number of bytes written. Recorded on each write.</summary>
 </histogram>
@@ -150735,7 +150746,7 @@
 </histogram>
 
 <histogram name="Sync.ConfigureDataTypeManagerOption"
-    enum="SyncFeatureOrTransport" expires_after="2020-04-26">
+    enum="SyncFeatureOrTransport" expires_after="2020-06-28">
   <owner>treib@chromium.org</owner>
   <summary>
     Whether the full Sync feature or only the Sync transport layer is being
@@ -150951,7 +150962,8 @@
 
 <histogram
     name="Sync.Crypto.CustomPassphraseKeyDerivationMethodOnNewPassphrase"
-    enum="SyncCustomPassphraseKeyDerivationMethodState" expires_after="M81">
+    enum="SyncCustomPassphraseKeyDerivationMethodState"
+    expires_after="2020-06-28">
   <owner>vitaliii@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -150964,7 +150976,8 @@
 
 <histogram
     name="Sync.Crypto.CustomPassphraseKeyDerivationMethodOnSuccessfulDecryption"
-    enum="SyncCustomPassphraseKeyDerivationMethodState" expires_after="M81">
+    enum="SyncCustomPassphraseKeyDerivationMethodState"
+    expires_after="2020-06-28">
   <owner>vitaliii@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -150977,7 +150990,8 @@
 </histogram>
 
 <histogram name="Sync.Crypto.CustomPassphraseKeyDerivationMethodStateOnStartup"
-    enum="SyncCustomPassphraseKeyDerivationMethodState" expires_after="M81">
+    enum="SyncCustomPassphraseKeyDerivationMethodState"
+    expires_after="2020-06-28">
   <owner>vitaliii@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -151015,7 +151029,7 @@
 </histogram>
 
 <histogram name="Sync.CustomEncryption" enum="SyncCustomEncryptionEvent"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>zea@chromium.org</owner>
   <summary>
     Histogram that keeps track of how users encrypt their sync data. All users
@@ -153081,7 +153095,7 @@
 </histogram>
 
 <histogram name="Sync.StopSource" enum="SyncStopSource"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>treib@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -157798,7 +157812,7 @@
 </histogram>
 
 <histogram name="Translate.CLD3.LanguagePercentage" units="%"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>frechette@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -157815,7 +157829,7 @@
 </histogram>
 
 <histogram name="Translate.CompactInfobar.Language.AlwaysTranslate"
-    enum="CLD3LanguageCode" expires_after="M81">
+    enum="CLD3LanguageCode" expires_after="2020-06-28">
   <owner>anthonyvd@chromium.org</owner>
   <summary>
     Records the hashcode of the source language when always translate this
@@ -157824,7 +157838,7 @@
 </histogram>
 
 <histogram name="Translate.CompactInfobar.Language.MoreLanguages"
-    enum="CLD3LanguageCode" expires_after="M81">
+    enum="CLD3LanguageCode" expires_after="2020-06-28">
   <owner>anthonyvd@chromium.org</owner>
   <summary>
     Records the hashcode of the language clicked on the more languages menu.
@@ -157841,7 +157855,7 @@
 </histogram>
 
 <histogram name="Translate.CompactInfobar.Language.PageNotIn"
-    enum="CLD3LanguageCode" expires_after="M81">
+    enum="CLD3LanguageCode" expires_after="2020-06-28">
   <owner>anthonyvd@chromium.org</owner>
   <summary>
     Records the hashcode of the language clicked on the menu to indicate the
@@ -157858,7 +157872,7 @@
 </histogram>
 
 <histogram name="Translate.CompactInfobar.TranslationsPerPage"
-    units="translations" expires_after="M81">
+    units="translations" expires_after="2020-06-28">
   <owner>anthonyvd@chromium.org</owner>
   <summary>
     Records the number of times a page is translated, every time the page is
@@ -157878,7 +157892,7 @@
 </histogram>
 
 <histogram name="Translate.ContentLanguage" enum="TranslateLanguage"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>kenjibaheux@google.com</owner>
   <summary>
     A page may provide a Content-Language HTTP header or a META tag. For each
@@ -157958,7 +157972,7 @@
 </histogram>
 
 <histogram name="Translate.HtmlLang" enum="TranslateLanguage"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>kenjibaheux@google.com</owner>
   <summary>
     A page may provide a lang attribute in html tag. For each page load,
@@ -158077,7 +158091,7 @@
 </histogram>
 
 <histogram name="Translate.ModifyOriginalLang" units="units"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>kenjibaheux@google.com</owner>
   <summary>
     The number of times the original language in the translate infobar has been
@@ -158095,7 +158109,7 @@
 </histogram>
 
 <histogram name="Translate.NeverTranslateLang" units="units"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>kenjibaheux@google.com</owner>
   <summary>
     The number of times the never translate option was selected in the translate
@@ -158104,7 +158118,7 @@
 </histogram>
 
 <histogram name="Translate.NeverTranslateSite" units="units"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>kenjibaheux@google.com</owner>
   <summary>
     The number of times the never translate site was selected in the translate
@@ -158130,7 +158144,7 @@
 </histogram>
 
 <histogram name="Translate.Ranker.Model.Status" enum="RankerModelStatus"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>rogerm@google.com</owner>
   <summary>
     Tracks the outcome of attempts to download a Translate Ranker Model.
@@ -158258,7 +158272,7 @@
 </histogram>
 
 <histogram name="Translate.ShowErrorUI" enum="TranslateError"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>kenjibaheux@google.com</owner>
   <summary>
     Chrome Translate shows an error UI (infobar or bubble) when an error happens
@@ -158290,7 +158304,7 @@
 </histogram>
 
 <histogram name="Translate.TargetLanguage" enum="CLD3LanguageCode"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>yyushkina@google.com</owner>
   <summary>
     The number of requests sent to the Translate server, grouped by target
@@ -159863,7 +159877,7 @@
 </histogram>
 
 <histogram name="UMA.TruncatedEvents.UserAction" units="events"
-    expires_after="2020-06-21">
+    expires_after="2020-06-28">
   <owner>rkaplow@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
@@ -161201,7 +161215,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.CodeCacheSizeRatio" units="%" expires_after="M81">
+<histogram name="V8.CodeCacheSizeRatio" units="%" expires_after="2020-06-28">
   <owner>yangguo@chromium.org</owner>
   <summary>Cache size to source size ratio when caching compiled code.</summary>
 </histogram>
@@ -161599,7 +161613,7 @@
 </histogram>
 
 <histogram name="V8.DebugFeatureUsage" enum="V8DebugFeature"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>yangguo@chromium.org</owner>
   <summary>
     Debugger feature used at least once per isolate, recorded on first use.
@@ -164590,7 +164604,7 @@
 </histogram>
 
 <histogram name="WebApk.Install.GooglePlayErrorCode"
-    enum="WebApkInstallGooglePlayErrorCode" expires_after="M81">
+    enum="WebApkInstallGooglePlayErrorCode" expires_after="2020-06-28">
   <owner>hanxi@chromium.org</owner>
   <owner>pkotwicz@chromium.org</owner>
   <owner>yfriedman@chromium.org</owner>
@@ -165824,7 +165838,7 @@
 </histogram>
 
 <histogram name="WebController.BackForwardListOutOfSync" enum="BooleanHit"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>gambard@chromium.org</owner>
   <summary>
     [iOS] Counts the number of time the backForwardList of the WebView gets out
@@ -165834,7 +165848,7 @@
 </histogram>
 
 <histogram name="WebController.BackForwardListOutOfSyncInProvisionalNavigation"
-    enum="IOSOutOfSyncURLAction" expires_after="M81">
+    enum="IOSOutOfSyncURLAction" expires_after="2020-06-28">
   <owner>ajuma@chromium.org</owner>
   <summary>
     [iOS] Records the action take when the backForwardList of the WebView is out
@@ -167330,7 +167344,7 @@
 </histogram>
 
 <histogram name="WebCore.WebSocket.MessageSize.Receive" units="bytes"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>yhirano@chromium.org</owner>
   <owner>ricea@chromium.org</owner>
   <summary>
@@ -167340,7 +167354,7 @@
 </histogram>
 
 <histogram name="WebCore.WebSocket.MessageSize.Send" units="bytes"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>yhirano@chromium.org</owner>
   <owner>ricea@chromium.org</owner>
   <summary>
@@ -168786,7 +168800,7 @@
 </histogram>
 
 <histogram name="WebRTC.AudioOutputSampleRate" enum="AudioSampleRate"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>henrika@chromium.org</owner>
   <summary>Audio output sample rate for WebRTC (in Hz).</summary>
 </histogram>
@@ -169023,7 +169037,7 @@
 </histogram>
 
 <histogram name="WebRTC.Call.TimeReceivingAudioRtpPacketsInSeconds" units="s"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>saza@chromium.org</owner>
   <summary>
     The amount of time between the arrival of the first and last audio RTP
@@ -169163,7 +169177,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.CallSetupState.CallSetupState"
-    enum="PeerConnectionCallSetupState" expires_after="M81">
+    enum="PeerConnectionCallSetupState" expires_after="2020-06-28">
   <owner>hbos@chromium.org</owner>
   <summary>
     Setting up a call involves a number of steps; this enum describes the status
@@ -169173,7 +169187,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.CallSetupState.OffererState"
-    enum="PeerConnectionOffererState" expires_after="M81">
+    enum="PeerConnectionOffererState" expires_after="2020-06-28">
   <owner>hbos@chromium.org</owner>
   <summary>
     Setting up a call involves a number of steps; this enum describes which
@@ -169346,7 +169360,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.OfferExtmapAllowMixed"
-    enum="PeerConnectionOfferExtmapAllowMixed" expires_after="M81">
+    enum="PeerConnectionOfferExtmapAllowMixed" expires_after="2020-06-28">
   <owner>kron@chromium.org</owner>
   <summary>
     What setting for the SDP attribute extmap-allow-mixed has been asked for by
@@ -169437,7 +169451,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.SdpFormatReceived"
-    enum="PeerConnectionSdpFormatReceived" expires_after="M81">
+    enum="PeerConnectionSdpFormatReceived" expires_after="2020-06-28">
   <owner>steveanton@chromium.org</owner>
   <summary>
     What SDP format is received in the remote offer. The value &quot;no
@@ -169495,7 +169509,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.Simulcast.ApplyRemoteDescription"
-    enum="SimulcastApiVersion" expires_after="M81">
+    enum="SimulcastApiVersion" expires_after="2020-06-28">
   <owner>amithi@chromium.org</owner>
   <summary>
     Was simulcast applied to the remote description and with which API surface.
@@ -169512,7 +169526,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.Simulcast.NumberOfSendEncodings"
-    units="units" expires_after="M81">
+    units="units" expires_after="2020-06-28">
   <owner>amithi@chromium.org</owner>
   <summary>
     Counts the number of send encodings given to PeerConnection::AddTransceiver.
@@ -169600,7 +169614,7 @@
 </histogram>
 
 <histogram name="WebRTC.ReceivedVideoTrackDuration" units="ms"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>perkj@chromium.org</owner>
   <summary>
     Durations of video tracks received over a PeerConnection. The stopwatch
@@ -169619,13 +169633,14 @@
   </summary>
 </histogram>
 
-<histogram name="WebRTC.ScreenCaptureTime" units="ms" expires_after="M81">
+<histogram name="WebRTC.ScreenCaptureTime" units="ms"
+    expires_after="2020-06-28">
   <owner>jiayl@chromium.org</owner>
   <summary>Time for capturing one frame in screen capturing.</summary>
 </histogram>
 
 <histogram name="WebRTC.SentAudioTrackDuration" units="ms"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>perkj@chromium.org</owner>
   <summary>
     Durations of audio tracks sent over a PeerConnection. The stopwatch starts
@@ -169947,7 +169962,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.DroppedFrames.Capturer" units="frames"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>ilnik@chromium.org</owner>
   <summary>
     Total number of frames dropped by a capturer for a sent video stream.
@@ -170037,7 +170052,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.EndToEndDelayMaxInMs" units="ms"
-    expires_after="2020-04-26">
+    expires_after="2020-06-28">
   <owner>ilnik@chromium.org</owner>
   <summary>
     The maximum end-to-end delay per frame for a received video stream. Recorded
@@ -170499,7 +170514,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.Screenshare.BitrateSentInKbps" units="kbps"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>sprang@chromium.org</owner>
   <summary>
     The number of sent bits per second for a sent screenshare stream. Recorded
@@ -172452,7 +172467,8 @@
   </summary>
 </histogram>
 
-<histogram name="WrenchMenu.TimeToAction" units="units" expires_after="M81">
+<histogram name="WrenchMenu.TimeToAction" units="units"
+    expires_after="2020-06-28">
   <owner>ainslie@chromium.org</owner>
   <owner>edwardjung@chromium.org</owner>
   <summary>
@@ -172504,7 +172520,7 @@
 </histogram>
 
 <histogram name="XHR.Sync.PageDismissal" enum="XHRPageDismissalState"
-    expires_after="M81">
+    expires_after="2020-06-28">
   <owner>panicker@chromium.org</owner>
   <summary>
     Records occurence of sync XHR during page dismissal state (unload,
diff --git a/tools/perf/page_sets/rendering/throughput_test_cases.py b/tools/perf/page_sets/rendering/throughput_test_cases.py
index f61b772..ddd2486e 100644
--- a/tools/perf/page_sets/rendering/throughput_test_cases.py
+++ b/tools/perf/page_sets/rendering/throughput_test_cases.py
@@ -62,3 +62,20 @@
   URL = ('file://../../../../chrome/test/data/perf/throughput_test_cases/'
          'main-animations-throughput.html#60')
 
+class MainFifteenWithJankImplZero(ThroughputMetricStory):
+  BASE_NAME = 'main_15fps_with_jank_impl_0fps'
+  SUPPORTED_PLATFORMS = platforms.ALL_PLATFORMS
+  URL = ('file://../../../../chrome/test/data/perf/throughput_test_cases/'
+         'main-animations-throughput.html?jank#15')
+
+class MainSixtyWithJankImplZero(ThroughputMetricStory):
+  BASE_NAME = 'main_60fps_with_jank_impl_0fps'
+  SUPPORTED_PLATFORMS = platforms.ALL_PLATFORMS
+  URL = ('file://../../../../chrome/test/data/perf/throughput_test_cases/'
+         'main-animations-throughput.html?jank#60')
+
+class MainZeroWithJankImplZero(ThroughputMetricStory):
+  BASE_NAME = 'main_0fps_with_jank_impl_0fps'
+  SUPPORTED_PLATFORMS = platforms.ALL_PLATFORMS
+  URL = ('file://../../../../chrome/test/data/perf/throughput_test_cases/'
+         'main-animations-throughput.html?jank#0')
diff --git a/ui/display/manager/display_manager.cc b/ui/display/manager/display_manager.cc
index 245810e..23f779a2 100644
--- a/ui/display/manager/display_manager.cc
+++ b/ui/display/manager/display_manager.cc
@@ -351,6 +351,7 @@
            size_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
     info_list.push_back(ManagedDisplayInfo::CreateFromSpec(part));
     info_list.back().set_native(true);
+    info_list.back().set_from_native_platform(true);
   }
   MaybeInitInternalDisplay(&info_list[0]);
   OnNativeDisplaysChanged(info_list);
diff --git a/ui/views/examples/bubble_example.cc b/ui/views/examples/bubble_example.cc
index 644d1059..1fb8d37 100644
--- a/ui/views/examples/bubble_example.cc
+++ b/ui/views/examples/bubble_example.cc
@@ -10,6 +10,7 @@
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/examples/examples_window.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/widget/widget.h"
 
@@ -121,8 +122,9 @@
   BubbleDialogDelegateView::CreateBubble(bubble);
 
   bubble->GetWidget()->Show();
-  PrintStatus("Click with optional modifiers: [Ctrl] for set_arrow(NONE), "
-     "[Alt] for set_arrow(FLOAT), or [Shift] to reverse the arrow iteration.");
+  LogStatus(
+      "Click with optional modifiers: [Ctrl] for set_arrow(NONE), "
+      "[Alt] for set_arrow(FLOAT), or [Shift] to reverse the arrow iteration.");
 }
 
 }  // namespace examples
diff --git a/ui/views/examples/button_example.cc b/ui/views/examples/button_example.cc
index 50197395..6ea2cac 100644
--- a/ui/views/examples/button_example.cc
+++ b/ui/views/examples/button_example.cc
@@ -12,6 +12,7 @@
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/button/md_text_button.h"
+#include "ui/views/examples/examples_window.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/resources/grit/views_resources.h"
 #include "ui/views/view.h"
diff --git a/ui/views/examples/checkbox_example.cc b/ui/views/examples/checkbox_example.cc
index 422dd37..b8cf9b8 100644
--- a/ui/views/examples/checkbox_example.cc
+++ b/ui/views/examples/checkbox_example.cc
@@ -8,6 +8,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/views/controls/button/checkbox.h"
 #include "ui/views/controls/button/radio_button.h"
+#include "ui/views/examples/examples_window.h"
 #include "ui/views/layout/fill_layout.h"
 
 namespace views {
diff --git a/ui/views/examples/combobox_example.cc b/ui/views/examples/combobox_example.cc
index 32f0867..a39accd 100644
--- a/ui/views/examples/combobox_example.cc
+++ b/ui/views/examples/combobox_example.cc
@@ -10,6 +10,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/views/controls/combobox/combobox.h"
+#include "ui/views/examples/examples_window.h"
 #include "ui/views/layout/box_layout.h"
 
 namespace views {
diff --git a/ui/views/examples/dialog_example.cc b/ui/views/examples/dialog_example.cc
index bf29f81..a0c50d8e 100644
--- a/ui/views/examples/dialog_example.cc
+++ b/ui/views/examples/dialog_example.cc
@@ -14,6 +14,7 @@
 #include "ui/views/controls/combobox/combobox.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/examples/examples_window.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/grid_layout.h"
 #include "ui/views/layout/layout_provider.h"
@@ -268,7 +269,7 @@
   if (sender == bubble_) {
     if (bubble_->GetChecked() && GetModalType() != ui::MODAL_TYPE_CHILD) {
       mode_->SetSelectedIndex(ui::MODAL_TYPE_CHILD);
-      PrintStatus("You nearly always want Child Modal for bubbles.");
+      LogStatus("You nearly always want Child Modal for bubbles.");
     }
     persistent_bubble_->SetEnabled(bubble_->GetChecked());
     OnPerformAction(mode_);  // Validate the modal type.
@@ -294,7 +295,7 @@
     return;
 
   if (sender == extra_button_label_)
-    PrintStatus("DialogDelegate can never refresh the extra view.");
+    LogStatus("DialogDelegate can never refresh the extra view.");
 
   if (sender == title_) {
     last_dialog_->GetWidget()->UpdateWindowTitle();
@@ -314,9 +315,9 @@
 #endif
   show_->SetEnabled(enable);
   if (!enable && GetModalType() == ui::MODAL_TYPE_CHILD)
-    PrintStatus("MODAL_TYPE_CHILD can't be used with non-bubbles.");
+    LogStatus("MODAL_TYPE_CHILD can't be used with non-bubbles.");
   if (!enable && GetModalType() == ui::MODAL_TYPE_SYSTEM)
-    PrintStatus("MODAL_TYPE_SYSTEM isn't supported on Mac.");
+    LogStatus("MODAL_TYPE_SYSTEM isn't supported on Mac.");
 }
 
 }  // namespace examples
diff --git a/ui/views/examples/example_base.cc b/ui/views/examples/example_base.cc
index 878ac700..3602e15 100644
--- a/ui/views/examples/example_base.cc
+++ b/ui/views/examples/example_base.cc
@@ -4,33 +4,16 @@
 
 #include "ui/views/examples/example_base.h"
 
-#include <stdarg.h>
-
-#include "base/macros.h"
-#include "base/strings/stringprintf.h"
 #include "ui/views/view.h"
 
 namespace views {
 namespace examples {
 
-// Logs the specified string to the status area of the examples window.
-// This function can only be called if there is a visible examples window.
-void LogStatus(const std::string& status);
-
 ExampleBase::~ExampleBase() = default;
 
 ExampleBase::ExampleBase(const char* title) : example_title_(title) {
   container_ = new View();
 }
 
-// Prints a message in the status area, at the bottom of the window.
-void ExampleBase::PrintStatus(const char* format, ...) {
-  va_list ap;
-  va_start(ap, format);
-  std::string msg;
-  base::StringAppendV(&msg, format, ap);
-  LogStatus(msg);
-}
-
 }  // namespace examples
 }  // namespace views
diff --git a/ui/views/examples/example_base.h b/ui/views/examples/example_base.h
index e582096..231011b6 100644
--- a/ui/views/examples/example_base.h
+++ b/ui/views/examples/example_base.h
@@ -28,9 +28,6 @@
  protected:
   explicit ExampleBase(const char* title);
 
-  // Prints a message in the status area, at the bottom of the window.
-  void PrintStatus(const char* format, ...);
-
  private:
   // Name of the example - used as title in the combobox list.
   std::string example_title_;
diff --git a/ui/views/examples/examples_window.cc b/ui/views/examples/examples_window.cc
index 2bbbfa7..4956b07f 100644
--- a/ui/views/examples/examples_window.cc
+++ b/ui/views/examples/examples_window.cc
@@ -179,7 +179,7 @@
 
   ~ExamplesWindowContents() override = default;
 
-  // Prints a message in the status area, at the bottom of the window.
+  // Sets the status area (at the bottom of the window) to |status|.
   void SetStatus(const std::string& status) {
     status_label_->SetText(base::UTF8ToUTF16(status));
   }
diff --git a/ui/views/examples/examples_window.h b/ui/views/examples/examples_window.h
index d543c01..d47de87 100644
--- a/ui/views/examples/examples_window.h
+++ b/ui/views/examples/examples_window.h
@@ -8,13 +8,15 @@
 #include <memory>
 #include <vector>
 
+#include "base/strings/stringprintf.h"
 #include "ui/gfx/native_widget_types.h"
-#include "ui/views/examples/example_base.h"
 #include "ui/views/examples/views_examples_export.h"
 
 namespace views {
 namespace examples {
 
+class VIEWS_EXAMPLES_EXPORT ExampleBase;
+
 // Shows a window with the views examples in it. |extra_examples| contains any
 // additional examples to add. |window_context| is used to determine where the
 // window should be created (see |Widget::InitParams::context| for details).
@@ -24,6 +26,15 @@
     std::vector<std::unique_ptr<ExampleBase>> extra_examples =
         std::vector<std::unique_ptr<ExampleBase>>());
 
+// Prints |string| in the status area, at the bottom of the window.
+VIEWS_EXAMPLES_EXPORT void LogStatus(const std::string& string);
+
+// Same as LogStatus(), but with a format string.
+template <typename... Args>
+void PrintStatus(const char* format, Args... args) {
+  LogStatus(base::StringPrintf(format, args...));
+}
+
 }  // namespace examples
 }  // namespace views
 
diff --git a/ui/views/examples/link_example.cc b/ui/views/examples/link_example.cc
index 03a8c6b..49d8db3 100644
--- a/ui/views/examples/link_example.cc
+++ b/ui/views/examples/link_example.cc
@@ -6,27 +6,27 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "ui/views/controls/link.h"
+#include "ui/views/examples/examples_window.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/view.h"
 
 namespace views {
 namespace examples {
 
-LinkExample::LinkExample() : ExampleBase("Link") {
-}
+LinkExample::LinkExample() : ExampleBase("Link") {}
 
 LinkExample::~LinkExample() = default;
 
 void LinkExample::CreateExampleView(View* container) {
-  link_ = new Link(base::ASCIIToUTF16("Click me!"));
-  link_->set_listener(this);
+  auto link = std::make_unique<Link>(base::ASCIIToUTF16("Click me!"));
+  link->set_listener(this);
 
   container->SetLayoutManager(std::make_unique<FillLayout>());
-  container->AddChildView(link_);
+  container->AddChildView(std::move(link));
 }
 
 void LinkExample::LinkClicked(Link* source, int event_flags) {
-  PrintStatus("Link clicked");
+  LogStatus("Link clicked");
 }
 
 }  // namespace examples
diff --git a/ui/views/examples/link_example.h b/ui/views/examples/link_example.h
index 5f6f57f..f26042b 100644
--- a/ui/views/examples/link_example.h
+++ b/ui/views/examples/link_example.h
@@ -16,6 +16,8 @@
                                           public LinkListener {
  public:
   LinkExample();
+  LinkExample(const LinkExample&) = delete;
+  LinkExample& operator=(const LinkExample&) = delete;
   ~LinkExample() override;
 
   // ExampleBase:
@@ -24,10 +26,6 @@
  private:
   // LinkListener:
   void LinkClicked(Link* source, int event_flags) override;
-
-  Link* link_;
-
-  DISALLOW_COPY_AND_ASSIGN(LinkExample);
 };
 
 }  // namespace examples
diff --git a/ui/views/examples/message_box_example.cc b/ui/views/examples/message_box_example.cc
index 67ff6dc..f359195 100644
--- a/ui/views/examples/message_box_example.cc
+++ b/ui/views/examples/message_box_example.cc
@@ -10,6 +10,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/message_box_view.h"
+#include "ui/views/examples/examples_window.h"
 #include "ui/views/layout/grid_layout.h"
 #include "ui/views/view.h"
 
@@ -57,8 +58,9 @@
   if (sender == status_) {
     message_box_view_->SetCheckBoxLabel(
         ASCIIToUTF16(message_box_view_->IsCheckBoxSelected() ? "on" : "off"));
-    PrintStatus(message_box_view_->IsCheckBoxSelected() ?
-       "Check Box Selected" : "Check Box Not Selected");
+    LogStatus(message_box_view_->IsCheckBoxSelected()
+                  ? "Check Box Selected"
+                  : "Check Box Not Selected");
   } else if (sender == toggle_) {
     message_box_view_->SetCheckBoxSelected(
         !message_box_view_->IsCheckBoxSelected());
diff --git a/ui/views/examples/radio_button_example.cc b/ui/views/examples/radio_button_example.cc
index 0bbcc618..0119dd0 100644
--- a/ui/views/examples/radio_button_example.cc
+++ b/ui/views/examples/radio_button_example.cc
@@ -11,6 +11,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/button/radio_button.h"
+#include "ui/views/examples/examples_window.h"
 #include "ui/views/layout/grid_layout.h"
 #include "ui/views/view.h"
 
diff --git a/ui/views/examples/tabbed_pane_example.cc b/ui/views/examples/tabbed_pane_example.cc
index b534d663..4746d7f 100644
--- a/ui/views/examples/tabbed_pane_example.cc
+++ b/ui/views/examples/tabbed_pane_example.cc
@@ -7,6 +7,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/tabbed_pane/tabbed_pane.h"
+#include "ui/views/examples/examples_window.h"
 #include "ui/views/layout/grid_layout.h"
 
 using base::ASCIIToUTF16;
@@ -67,18 +68,17 @@
     if (tabbed_pane_->GetTabCount() > 1)
       tabbed_pane_->SelectTabAt(1);
   }
-  PrintStatus();
+  PrintCurrentStatus();
 }
 
 void TabbedPaneExample::TabSelectedAt(int index) {
   // Just print the status when selection changes.
-  PrintStatus();
+  PrintCurrentStatus();
 }
 
-void TabbedPaneExample::PrintStatus() {
-  ExampleBase::PrintStatus("Tab Count:%" PRIuS ", Selected Tab:%" PRIuS,
-                           tabbed_pane_->GetTabCount(),
-                           tabbed_pane_->GetSelectedTabIndex());
+void TabbedPaneExample::PrintCurrentStatus() {
+  PrintStatus("Tab Count:%" PRIuS ", Selected Tab:%" PRIuS,
+              tabbed_pane_->GetTabCount(), tabbed_pane_->GetSelectedTabIndex());
 }
 
 void TabbedPaneExample::AddButton(const std::string& label) {
diff --git a/ui/views/examples/tabbed_pane_example.h b/ui/views/examples/tabbed_pane_example.h
index 79a1f42f..5806ca9 100644
--- a/ui/views/examples/tabbed_pane_example.h
+++ b/ui/views/examples/tabbed_pane_example.h
@@ -36,7 +36,7 @@
   void TabSelectedAt(int index) override;
 
   // Print the status of the tab in the status area.
-  void PrintStatus();
+  void PrintCurrentStatus();
 
   void AddButton(const std::string& label);
 
diff --git a/ui/views/examples/table_example.cc b/ui/views/examples/table_example.cc
index ce739c8..7c90c17 100644
--- a/ui/views/examples/table_example.cc
+++ b/ui/views/examples/table_example.cc
@@ -13,6 +13,7 @@
 #include "ui/gfx/image/image_skia.h"
 #include "ui/views/controls/button/checkbox.h"
 #include "ui/views/controls/scroll_view.h"
+#include "ui/views/examples/examples_window.h"
 #include "ui/views/layout/grid_layout.h"
 
 using base::ASCIIToUTF16;
diff --git a/ui/views/examples/textfield_example.cc b/ui/views/examples/textfield_example.cc
index ad966e4..f5789019 100644
--- a/ui/views/examples/textfield_example.cc
+++ b/ui/views/examples/textfield_example.cc
@@ -14,6 +14,7 @@
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/examples/examples_window.h"
 #include "ui/views/layout/grid_layout.h"
 #include "ui/views/view.h"
 
diff --git a/ui/views/examples/toggle_button_example.cc b/ui/views/examples/toggle_button_example.cc
index fd47653..ab3f222 100644
--- a/ui/views/examples/toggle_button_example.cc
+++ b/ui/views/examples/toggle_button_example.cc
@@ -8,6 +8,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/views/controls/button/toggle_button.h"
+#include "ui/views/examples/examples_window.h"
 #include "ui/views/layout/box_layout.h"
 
 namespace views {