diff --git a/DEPS b/DEPS
index 3fd23d7..9f20dda 100644
--- a/DEPS
+++ b/DEPS
@@ -133,11 +133,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': 'c8a84d2b7ff98b44c42a53d1ba750a2c02f9063a',
+  'skia_revision': '348880c3f67fb4422d1f66588ab87866c6ea848f',
   # 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': '0829f5e5e466d8078da8365602f3ea5df735cfcd',
+  'v8_revision': '0ef678d942fe182261c0120adea1091d6dde1986',
   # 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.
@@ -145,15 +145,15 @@
   # 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': '01cd5697760e6df88936b0d99dee740130956d49',
+  'angle_revision': 'bff32703c75b0326ca3c7e49e49c7ecdacdef41e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '3d7b7ea134d1245748a26898c40f7d078fea16d7',
+  'swiftshader_revision': 'cfbe5379bf8e12168908c305ab7228bb195d19bb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '0db20e0a220b9ce2ba7d6bee980ec1bcd7a667e7',
+  'pdfium_revision': 'a664b953817c9d439a71486f2c1d9cda67edb8ef',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -264,11 +264,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'shaderc_revision': '1475418902b2724370f4abf003c06f8e1f947add',
+  'shaderc_revision': '08549a9c626a047e4bd1c618713a0efb75774005',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '2e56970932ec01ef2b030fe6b27f305c7733a47f',
+  'dawn_revision': '0195dbf908071e7b7afaed7c9fb922910e85e5c2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -802,7 +802,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '5ea0721b4537610d30bc17d95ff2298b5e932e12',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '69250bb90295f22db34c8c853e218a77a0cac787',
       'condition': 'checkout_linux',
   },
 
@@ -817,7 +817,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '70f65b7ac09d1702a5819da880d5eb61589c5f0f',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '7e26488a958d4ea1392889434292bce34e459040',
       'condition': 'checkout_linux',
   },
 
@@ -827,7 +827,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '3d86f06bc794b457a0e1329dfd4a0b5bd0dc2a0e',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '2d3b9260f3085f0ce161dbec51f131979b828474',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1389,7 +1389,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@013ffd95d645dc185e569896d40e6af9a13778be',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@66347fe2ab6c4e95cc2b7abba325f16374c41019',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/apk/BUILD.gn b/android_webview/apk/BUILD.gn
index a846f82..1ceff4b 100644
--- a/android_webview/apk/BUILD.gn
+++ b/android_webview/apk/BUILD.gn
@@ -8,6 +8,9 @@
 # in two different targets.
 android_library("webview_license_provider_java") {
   java_files = [ "//android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java" ]
+  deps = [
+    "//base:base_java",
+  ]
 }
 
 android_library("webview_license_activity_java") {
diff --git a/android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java b/android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java
index ef2dd2f..94824a6 100644
--- a/android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java
+++ b/android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java
@@ -14,6 +14,8 @@
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
+import org.chromium.base.FileUtils;
+
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -49,11 +51,7 @@
             ParcelFileDescriptor output, Uri uri, String mimeType, Bundle opts, String filename) {
         try (InputStream in = getContext().getAssets().open(filename);
                 OutputStream out = new FileOutputStream(output.getFileDescriptor());) {
-            byte[] buf = new byte[8192];
-            int size = -1;
-            while ((size = in.read(buf)) != -1) {
-                out.write(buf, 0, size);
-            }
+            FileUtils.copyStream(in, out);
         } catch (IOException e) {
             Log.e(TAG, "Failed to read the license file", e);
         }
diff --git a/android_webview/browser/net/aw_url_request_context_getter_unittest.cc b/android_webview/browser/net/aw_url_request_context_getter_unittest.cc
index 1bde77d5..31969a5 100644
--- a/android_webview/browser/net/aw_url_request_context_getter_unittest.cc
+++ b/android_webview/browser/net/aw_url_request_context_getter_unittest.cc
@@ -123,8 +123,7 @@
   std::unique_ptr<net::CertVerifier::Request> request;
   int error = context->cert_verifier()->Verify(
       net::CertVerifier::RequestParams(cert, "www.ahrn.com", flags,
-                                       /*ocsp_response=*/std::string(),
-                                       /*sct_list=*/std::string()),
+                                       std::string()),
       &result, callback.callback(), &request, net::NetLogWithSource());
   EXPECT_THAT(error, net::test::IsError(net::ERR_IO_PENDING));
   EXPECT_TRUE(request);
@@ -161,9 +160,7 @@
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
   int error = context->cert_verifier()->Verify(
-      net::CertVerifier::RequestParams(cert, "127.0.0.1", flags,
-                                       /*ocsp_response=*/std::string(),
-                                       /*sct_list=*/std::string()),
+      net::CertVerifier::RequestParams(cert, "127.0.0.1", flags, std::string()),
       &result, callback.callback(), &request, net::NetLogWithSource());
   EXPECT_THAT(error, net::test::IsError(net::ERR_IO_PENDING));
   EXPECT_TRUE(request);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidProtocolHandlerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidProtocolHandlerTest.java
index 2af044e..0182d8d4 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidProtocolHandlerTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidProtocolHandlerTest.java
@@ -12,9 +12,9 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.android_webview.AndroidProtocolHandler;
+import org.chromium.base.FileUtils;
 import org.chromium.base.test.util.Feature;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -67,10 +67,10 @@
         InputStream svgzStream = null;
         try {
             svgStream = assertOpen("file:///android_asset/star.svg");
-            byte[] expectedData = readFully(svgStream);
+            byte[] expectedData = FileUtils.readStream(svgStream);
 
             svgzStream = assertOpen("file:///android_asset/star.svgz");
-            byte[] actualData = readFully(svgzStream);
+            byte[] actualData = FileUtils.readStream(svgzStream);
 
             Assert.assertArrayEquals(
                     "Decompressed star.svgz doesn't match star.svg", expectedData, actualData);
@@ -85,15 +85,4 @@
         Assert.assertNotNull("Failed top open \"" + url + "\"", stream);
         return stream;
     }
-
-    private byte[] readFully(InputStream stream) throws IOException {
-        ByteArrayOutputStream data = new ByteArrayOutputStream();
-        byte[] buf = new byte[4096];
-        for (;;) {
-            int len = stream.read(buf);
-            if (len < 1) break;
-            data.write(buf, 0, len);
-        }
-        return data.toByteArray();
-    }
 }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 0d564ef..7fa6dc99 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1322,7 +1322,6 @@
     "//ash/components/shortcut_viewer",
     "//ash/keyboard/arc",
     "//ash/keyboard/ui",
-    "//ash/keyboard/ui:mojom",
     "//ash/login/resources:resources_grit",
     "//ash/system/message_center/arc",
     "//base",
@@ -1919,7 +1918,6 @@
     "//ash/components/shortcut_viewer:unit_tests",
     "//ash/keyboard/arc",
     "//ash/keyboard/ui",
-    "//ash/keyboard/ui:mojom",
     "//ash/keyboard/ui:test_support",
     "//ash/public/cpp",
     "//ash/public/cpp:manifest",
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 01c18ca..7d071e6 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -17,12 +17,12 @@
 #include "ash/app_list/views/search_box_view.h"
 #include "ash/app_list/views/test/apps_grid_view_test_api.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/app_list/app_list_switches.h"
 #include "ash/public/cpp/ash_switches.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shelf_types.h"
diff --git a/ash/app_list/presenter/OWNERS b/ash/app_list/presenter/OWNERS
deleted file mode 100644
index 08850f4..0000000
--- a/ash/app_list/presenter/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index dd9ee47..ba28075 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -458,9 +458,9 @@
             gfx::PointF(0, y_offset),
             gfx::PointF())),
         view_(view) {
-    DCHECK(view_->is_in_drag() &&
-           view_->app_list_state() == ash::mojom::AppListViewState::kPeeking &&
-           !view_->is_tablet_mode());
+    DCHECK(view_->is_in_drag());
+    DCHECK_EQ(view_->app_list_state(), ash::mojom::AppListViewState::kPeeking);
+    DCHECK(!view_->is_tablet_mode());
   }
 
   ~PeekingResetAnimation() override = default;
diff --git a/ash/detachable_base/detachable_base_handler.cc b/ash/detachable_base/detachable_base_handler.cc
index c4b8e77..84f449e 100644
--- a/ash/detachable_base/detachable_base_handler.cc
+++ b/ash/detachable_base/detachable_base_handler.cc
@@ -46,14 +46,11 @@
   registry->RegisterDictionaryPref(prefs::kDetachableBaseDevices);
 }
 
-DetachableBaseHandler::DetachableBaseHandler(Shell* shell)
-    : shell_(shell),
+DetachableBaseHandler::DetachableBaseHandler(PrefService* local_state)
+    : local_state_(local_state),
       hammerd_observer_(this),
       power_manager_observer_(this),
       weak_ptr_factory_(this) {
-  if (shell_)
-    shell_->AddShellObserver(this);
-
   if (chromeos::HammerdClient::Get())  // May be null in tests
     hammerd_observer_.Add(chromeos::HammerdClient::Get());
   chromeos::PowerManagerClient* power_manager_client =
@@ -63,12 +60,12 @@
   power_manager_client->GetSwitchStates(
       base::BindOnce(&DetachableBaseHandler::OnGotPowerManagerSwitchStates,
                      weak_ptr_factory_.GetWeakPtr()));
+
+  if (GetPairingStatus() != DetachableBasePairingStatus::kNone)
+    NotifyPairingStatusChanged();
 }
 
-DetachableBaseHandler::~DetachableBaseHandler() {
-  if (shell_)
-    shell_->RemoveShellObserver(this);
-}
+DetachableBaseHandler::~DetachableBaseHandler() = default;
 
 void DetachableBaseHandler::AddObserver(DetachableBaseObserver* observer) {
   observers_.AddObserver(observer);
@@ -138,14 +135,6 @@
   return true;
 }
 
-void DetachableBaseHandler::OnLocalStatePrefServiceInitialized(
-    PrefService* pref_service) {
-  local_state_ = pref_service;
-
-  if (GetPairingStatus() != DetachableBasePairingStatus::kNone)
-    NotifyPairingStatusChanged();
-}
-
 void DetachableBaseHandler::BaseFirmwareUpdateNeeded() {
   NotifyBaseRequiresFirmwareUpdate(true /*requires_update*/);
 }
diff --git a/ash/detachable_base/detachable_base_handler.h b/ash/detachable_base/detachable_base_handler.h
index b7a9884..ae1f0e5 100644
--- a/ash/detachable_base/detachable_base_handler.h
+++ b/ash/detachable_base/detachable_base_handler.h
@@ -12,7 +12,6 @@
 #include "ash/ash_export.h"
 #include "ash/detachable_base/detachable_base_pairing_status.h"
 #include "ash/public/interfaces/user_info.mojom.h"
-#include "ash/shell_observer.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
@@ -27,7 +26,6 @@
 namespace ash {
 
 class DetachableBaseObserver;
-class Shell;
 
 // Keeps track of the state of Chrome OS device's detachable base. It tracks
 // whether:
@@ -46,13 +44,11 @@
 // DetachableBaseHandler clients are expected to determine for which users the
 // detachable base state should be set or retrieved.
 class ASH_EXPORT DetachableBaseHandler
-    : public ShellObserver,
-      public chromeos::HammerdClient::Observer,
+    : public chromeos::HammerdClient::Observer,
       public chromeos::PowerManagerClient::Observer {
  public:
-  // |shell| - the ash shell that owns the DetachableBaseHandler. May be null in
-  // tests.
-  explicit DetachableBaseHandler(ash::Shell* shell);
+  // |local_state| - PrefService of Local state. May be null in tests.
+  explicit DetachableBaseHandler(PrefService* local_state);
   ~DetachableBaseHandler() override;
 
   // Registers the local state prefs for detachable base devices.
@@ -85,9 +81,6 @@
   // paired) - setting the last used base can be retried at that point.
   bool SetPairedBaseAsLastUsedByUser(const mojom::UserInfo& user);
 
-  // ShellObserver:
-  void OnLocalStatePrefServiceInitialized(PrefService* pref_service) override;
-
   // chromeos::HammerdClient::Observer:
   void BaseFirmwareUpdateNeeded() override;
   void BaseFirmwareUpdateStarted() override;
@@ -129,9 +122,6 @@
 
   PrefService* local_state_ = nullptr;
 
-  // The shell that owns |this| - used to listen for local state initialization.
-  ash::Shell* shell_;
-
   // Tablet mode state currently reported by power manager - tablet mode getting
   // turned on is used as a signal that the base is detached.
   base::Optional<chromeos::PowerManagerClient::TabletMode> tablet_mode_;
diff --git a/ash/detachable_base/detachable_base_handler_unittest.cc b/ash/detachable_base/detachable_base_handler_unittest.cc
index 3a53ee4..02602e8 100644
--- a/ash/detachable_base/detachable_base_handler_unittest.cc
+++ b/ash/detachable_base/detachable_base_handler_unittest.cc
@@ -95,8 +95,7 @@
     default_user_ = CreateUser("user_1@foo.bar", "111111", UserType::kNormal);
 
     DetachableBaseHandler::RegisterPrefs(local_state_.registry());
-    handler_ = std::make_unique<DetachableBaseHandler>(nullptr);
-    handler_->OnLocalStatePrefServiceInitialized(&local_state_);
+    handler_ = std::make_unique<DetachableBaseHandler>(&local_state_);
     handler_->AddObserver(&detachable_base_observer_);
   }
 
@@ -122,21 +121,10 @@
 
   void RestartHandler() {
     handler_->RemoveObserver(&detachable_base_observer_);
-    handler_ = std::make_unique<DetachableBaseHandler>(nullptr);
-    handler_->OnLocalStatePrefServiceInitialized(&local_state_);
+    handler_ = std::make_unique<DetachableBaseHandler>(&local_state_);
     handler_->AddObserver(&detachable_base_observer_);
   }
 
-  void ResetHandlerWithNoLocalState() {
-    handler_->RemoveObserver(&detachable_base_observer_);
-    handler_ = std::make_unique<DetachableBaseHandler>(nullptr);
-    handler_->AddObserver(&detachable_base_observer_);
-  }
-
-  void SimulateLocalStateInitialized() {
-    handler_->OnLocalStatePrefServiceInitialized(&local_state_);
-  }
-
   chromeos::FakeHammerdClient* hammerd_client_ = nullptr;
 
   TestBaseObserver detachable_base_observer_;
@@ -552,42 +540,6 @@
   detachable_base_observer_.reset_pairing_status_changed_count();
 }
 
-TEST_F(DetachableBaseHandlerTest, NoLocalState) {
-  base::RunLoop().RunUntilIdle();
-
-  hammerd_client_->FirePairChallengeSucceededSignal({0x01, 0x02, 0x03, 0x04});
-  handler_->SetPairedBaseAsLastUsedByUser(*default_user_);
-  detachable_base_observer_.reset_pairing_status_changed_count();
-
-  ResetHandlerWithNoLocalState();
-  base::RunLoop().RunUntilIdle();
-
-  ChangePairedBase({0x04, 0x05, 0x06});
-
-  EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
-            handler_->GetPairingStatus());
-  EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  detachable_base_observer_.reset_pairing_status_changed_count();
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
-
-  SimulateLocalStateInitialized();
-
-  EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  detachable_base_observer_.reset_pairing_status_changed_count();
-  EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
-            handler_->GetPairingStatus());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
-
-  ChangePairedBase({0x01, 0x02, 0x03, 0x04});
-
-  EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
-            handler_->GetPairingStatus());
-  EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  detachable_base_observer_.reset_pairing_status_changed_count();
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
-  detachable_base_observer_.reset_pairing_status_changed_count();
-}
-
 TEST_F(DetachableBaseHandlerTest, EphemeralUser) {
   base::RunLoop().RunUntilIdle();
 
diff --git a/ash/keyboard/ash_keyboard_controller.cc b/ash/keyboard/ash_keyboard_controller.cc
index ac09cc6..1768166 100644
--- a/ash/keyboard/ash_keyboard_controller.cc
+++ b/ash/keyboard/ash_keyboard_controller.cc
@@ -6,8 +6,8 @@
 
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_ui_factory.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/virtual_keyboard_controller.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
diff --git a/ash/keyboard/ui/BUILD.gn b/ash/keyboard/ui/BUILD.gn
index 0a85456..88bf659 100644
--- a/ash/keyboard/ui/BUILD.gn
+++ b/ash/keyboard/ui/BUILD.gn
@@ -43,8 +43,6 @@
     "keyboard_util.h",
     "notification_manager.cc",
     "notification_manager.h",
-    "public/keyboard_switches.cc",
-    "public/keyboard_switches.h",
     "queued_container_type.cc",
     "queued_container_type.h",
     "queued_display_change.cc",
@@ -58,8 +56,8 @@
   defines = [ "KEYBOARD_IMPLEMENTATION" ]
 
   deps = [
-    ":mojom",
     ":resources",
+    "//ash/public/cpp",
     "//base",
     "//services/metrics/public/cpp:ukm_builders",
     "//ui/aura",
@@ -145,13 +143,6 @@
   path = rebase_path("//third_party/google_input_tools")
 }
 
-mojom("mojom") {
-  sources = [
-    "public/keyboard_config.mojom",
-    "public/keyboard_controller_types.mojom",
-  ]
-}
-
 test("keyboard_unittests") {
   sources = [
     "container_floating_behavior_unittest.cc",
@@ -165,9 +156,9 @@
   ]
 
   deps = [
-    ":mojom",
     ":test_support",
     ":ui",
+    "//ash/public/cpp",
     "//base",
     "//base/test:test_support",
     "//components/ukm:test_support",
diff --git a/ash/keyboard/ui/container_behavior.h b/ash/keyboard/ui/container_behavior.h
index 4efcea7..2c8d097 100644
--- a/ash/keyboard/ui/container_behavior.h
+++ b/ash/keyboard/ui/container_behavior.h
@@ -6,7 +6,7 @@
 #define ASH_KEYBOARD_UI_CONTAINER_BEHAVIOR_H_
 
 #include "ash/keyboard/ui/keyboard_export.h"
-#include "ash/keyboard/ui/public/keyboard_controller_types.mojom.h"
+#include "ash/public/interfaces/keyboard_controller_types.mojom.h"
 #include "ui/display/display.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/vector2d.h"
diff --git a/ash/keyboard/ui/keyboard_controller.cc b/ash/keyboard/ui/keyboard_controller.cc
index f87effe..395d4dc 100644
--- a/ash/keyboard/ui/keyboard_controller.cc
+++ b/ash/keyboard/ui/keyboard_controller.cc
@@ -16,10 +16,10 @@
 #include "ash/keyboard/ui/keyboard_ui_factory.h"
 #include "ash/keyboard/ui/keyboard_util.h"
 #include "ash/keyboard/ui/notification_manager.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/queued_container_type.h"
 #include "ash/keyboard/ui/queued_display_change.h"
 #include "ash/keyboard/ui/shaped_window_targeter.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/macros.h"
@@ -142,16 +142,17 @@
 }
 
 // An enumeration of different keyboard control events that should be logged.
-enum KeyboardControlEvent {
-  KEYBOARD_CONTROL_SHOW = 0,
-  KEYBOARD_CONTROL_HIDE_AUTO,
-  KEYBOARD_CONTROL_HIDE_USER,
-  KEYBOARD_CONTROL_MAX,
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class KeyboardControlEvent {
+  kShow = 0,
+  kHideAuto = 1,
+  kHideUser = 2,
+  kMaxValue = kHideUser
 };
 
 void LogKeyboardControlEvent(KeyboardControlEvent event) {
-  UMA_HISTOGRAM_ENUMERATION("VirtualKeyboard.KeyboardControlEvent", event,
-                            KEYBOARD_CONTROL_MAX);
+  UMA_HISTOGRAM_ENUMERATION("VirtualKeyboard.KeyboardControlEvent", event);
 }
 
 class InputMethodKeyboardController : public ui::InputMethodKeyboardController {
@@ -603,11 +604,11 @@
         case HIDE_REASON_SYSTEM_EXPLICIT:
         case HIDE_REASON_SYSTEM_IMPLICIT:
         case HIDE_REASON_SYSTEM_TEMPORARY:
-          LogKeyboardControlEvent(KEYBOARD_CONTROL_HIDE_AUTO);
+          LogKeyboardControlEvent(KeyboardControlEvent::kHideAuto);
           break;
         case HIDE_REASON_USER_EXPLICIT:
         case HIDE_REASON_USER_IMPLICIT:
-          LogKeyboardControlEvent(KEYBOARD_CONTROL_HIDE_USER);
+          LogKeyboardControlEvent(KeyboardControlEvent::kHideUser);
           break;
       }
 
@@ -948,7 +949,7 @@
   // are at begin states for animation.
   container_behavior_->InitializeShowAnimationStartingState(keyboard_window);
 
-  LogKeyboardControlEvent(KEYBOARD_CONTROL_SHOW);
+  LogKeyboardControlEvent(KeyboardControlEvent::kShow);
   RecordUkmKeyboardShown();
 
   ui::LayerAnimator* container_animator =
@@ -973,8 +974,7 @@
   ChangeState(KeyboardControllerState::SHOWN);
 
   UMA_HISTOGRAM_ENUMERATION("InputMethod.VirtualKeyboard.ContainerBehavior",
-                            GetActiveContainerType(),
-                            mojom::ContainerType::kMaxValue);
+                            GetActiveContainerType());
 }
 
 bool KeyboardController::WillHideKeyboard() const {
diff --git a/ash/keyboard/ui/keyboard_controller.h b/ash/keyboard/ui/keyboard_controller.h
index c61de37..efabdb6 100644
--- a/ash/keyboard/ui/keyboard_controller.h
+++ b/ash/keyboard/ui/keyboard_controller.h
@@ -16,10 +16,10 @@
 #include "ash/keyboard/ui/keyboard_layout_delegate.h"
 #include "ash/keyboard/ui/keyboard_ukm_recorder.h"
 #include "ash/keyboard/ui/notification_manager.h"
-#include "ash/keyboard/ui/public/keyboard_config.mojom.h"
-#include "ash/keyboard/ui/public/keyboard_controller_types.mojom.h"
 #include "ash/keyboard/ui/queued_container_type.h"
 #include "ash/keyboard/ui/queued_display_change.h"
+#include "ash/public/interfaces/keyboard_config.mojom.h"
+#include "ash/public/interfaces/keyboard_controller_types.mojom.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/scoped_observer.h"
diff --git a/ash/keyboard/ui/keyboard_util.cc b/ash/keyboard/ui/keyboard_util.cc
index 84de22e..1068975 100644
--- a/ash/keyboard/ui/keyboard_util.cc
+++ b/ash/keyboard/ui/keyboard_util.cc
@@ -7,7 +7,7 @@
 #include <string>
 
 #include "ash/keyboard/ui/keyboard_controller.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "base/command_line.h"
 #include "base/metrics/histogram_macros.h"
 
diff --git a/ash/keyboard/ui/public/keyboard_switches.h b/ash/keyboard/ui/public/keyboard_switches.h
deleted file mode 100644
index b5e22c2..0000000
--- a/ash/keyboard/ui/public/keyboard_switches.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_KEYBOARD_UI_PUBLIC_KEYBOARD_SWITCHES_H_
-#define ASH_KEYBOARD_UI_PUBLIC_KEYBOARD_SWITCHES_H_
-
-#include "ash/keyboard/ui/keyboard_export.h"
-
-namespace keyboard {
-namespace switches {
-
-// Enables the virtual keyboard.
-KEYBOARD_EXPORT extern const char kEnableVirtualKeyboard[];
-
-}  // namespace switches
-}  // namespace keyboard
-
-#endif  //  ASH_KEYBOARD_UI_PUBLIC_KEYBOARD_SWITCHES_H_
diff --git a/ash/keyboard/ui/queued_container_type.h b/ash/keyboard/ui/queued_container_type.h
index 56dd458..53b29d5 100644
--- a/ash/keyboard/ui/queued_container_type.h
+++ b/ash/keyboard/ui/queued_container_type.h
@@ -5,7 +5,7 @@
 #ifndef ASH_KEYBOARD_UI_QUEUED_CONTAINER_TYPE_H_
 #define ASH_KEYBOARD_UI_QUEUED_CONTAINER_TYPE_H_
 
-#include "ash/keyboard/ui/public/keyboard_controller_types.mojom.h"
+#include "ash/public/interfaces/keyboard_controller_types.mojom.h"
 #include "base/bind.h"
 #include "base/optional.h"
 #include "ui/gfx/geometry/rect.h"
diff --git a/ash/keyboard/virtual_keyboard_controller.cc b/ash/keyboard/virtual_keyboard_controller.cc
index faf7a58..2f4afc9d 100644
--- a/ash/keyboard/virtual_keyboard_controller.cc
+++ b/ash/keyboard/virtual_keyboard_controller.cc
@@ -11,7 +11,7 @@
 #include "ash/keyboard/ash_keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_util.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller_impl.h"
diff --git a/ash/keyboard/virtual_keyboard_controller_unittest.cc b/ash/keyboard/virtual_keyboard_controller_unittest.cc
index 8d74e4b..3ea25905 100644
--- a/ash/keyboard/virtual_keyboard_controller_unittest.cc
+++ b/ash/keyboard/virtual_keyboard_controller_unittest.cc
@@ -11,8 +11,8 @@
 #include "ash/ime/ime_controller.h"
 #include "ash/ime/test_ime_controller_client.h"
 #include "ash/keyboard/ash_keyboard_controller.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/shell.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/virtual_keyboard/virtual_keyboard_observer.h"
diff --git a/ash/keyboard/virtual_keyboard_unittest.cc b/ash/keyboard/virtual_keyboard_unittest.cc
index b25efc5d..8e331a6 100644
--- a/ash/keyboard/virtual_keyboard_unittest.cc
+++ b/ash/keyboard/virtual_keyboard_unittest.cc
@@ -4,8 +4,8 @@
 
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_util.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/bind_helpers.h"
diff --git a/ash/login/ui/login_keyboard_test_base.cc b/ash/login/ui/login_keyboard_test_base.cc
index 098aaea..f4f87cc 100644
--- a/ash/login/ui/login_keyboard_test_base.cc
+++ b/ash/login/ui/login_keyboard_test_base.cc
@@ -7,11 +7,11 @@
 #include "ash/keyboard/ash_keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_ui.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
 #include "ash/login/mock_login_screen_client.h"
 #include "ash/login/ui/lock_screen.h"
 #include "ash/login/ui/login_test_utils.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index bb3ba157..828be8cb 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -17,8 +17,6 @@
     "app_list/app_list_metrics.h",
     "app_list/app_list_switches.cc",
     "app_list/app_list_switches.h",
-    "app_list/app_list_types.cc",
-    "app_list/app_list_types.h",
     "app_list/internal_app_id_constants.h",
     "app_list/term_break_iterator.cc",
     "app_list/term_break_iterator.h",
@@ -35,7 +33,6 @@
     "ash_features.h",
     "ash_pref_names.cc",
     "ash_pref_names.h",
-    "ash_public_export.h",
     "ash_switches.cc",
     "ash_switches.h",
     "ash_typography.cc",
@@ -78,6 +75,8 @@
     "immersive/immersive_fullscreen_controller_delegate.h",
     "immersive/immersive_revealed_lock.cc",
     "immersive/immersive_revealed_lock.h",
+    "keyboard/keyboard_switches.cc",
+    "keyboard/keyboard_switches.h",
     "keyboard_shortcut_viewer.h",
     "kiosk_app_menu.cc",
     "kiosk_app_menu.h",
@@ -168,6 +167,7 @@
   ]
 
   public_deps = [
+    ":base",
     "//ash/public/interfaces:interfaces_internal",
     "//base",
     "//components/session_manager:base",
@@ -180,6 +180,23 @@
   output_name = "ash_public_cpp"
 }
 
+# This is listed separately because app_list.mojom type mapping depends on it
+# but //ash/public/interfaces could not depend on //ash/public/cpp.
+# TODO(crbug.com/958134): Move this back when app_list.mojom is gone.
+source_set("base") {
+  sources = [
+    "app_list/app_list_types.cc",
+    "app_list/app_list_types.h",
+    "ash_public_export.h",
+  ]
+
+  defines = [ "ASH_PUBLIC_IMPLEMENTATION" ]
+
+  public_deps = [
+    "//ui/gfx",
+  ]
+}
+
 source_set("manifest") {
   sources = [
     "manifest.cc",
@@ -269,7 +286,6 @@
 
   deps = [
     ":cpp",
-    "//ash/keyboard/ui:mojom",
     "//base",
     "//services/service_manager/public/cpp",
     "//ui/aura",
diff --git a/ash/public/cpp/app_list/app_list_features.cc b/ash/public/cpp/app_list/app_list_features.cc
index d21c63f..b65370b 100644
--- a/ash/public/cpp/app_list/app_list_features.cc
+++ b/ash/public/cpp/app_list/app_list_features.cc
@@ -33,6 +33,8 @@
     "EnableZeroStateAppsRanker", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kEnableQueryBasedMixedTypesRanker{
     "EnableQueryBasedMixedTypesRanker", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kEnableZeroStateMixedTypesRanker{
+    "EnableZeroStateMixedTypesRanker", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kEnableAppReinstallZeroState{
     "EnableAppReinstallZeroState", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kEnableEmbeddedAssistantUI{
@@ -90,6 +92,10 @@
   return base::FeatureList::IsEnabled(kEnableQueryBasedMixedTypesRanker);
 }
 
+bool IsZeroStateMixedTypesRankerEnabled() {
+  return base::FeatureList::IsEnabled(kEnableZeroStateMixedTypesRanker);
+}
+
 bool IsAppReinstallZeroStateEnabled() {
   return base::FeatureList::IsEnabled(kEnableAppReinstallZeroState);
 }
diff --git a/ash/public/cpp/app_list/app_list_features.h b/ash/public/cpp/app_list/app_list_features.h
index 59682837..994ddff 100644
--- a/ash/public/cpp/app_list/app_list_features.h
+++ b/ash/public/cpp/app_list/app_list_features.h
@@ -53,6 +53,9 @@
 // Enable an model that ranks query based non-apps result.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableQueryBasedMixedTypesRanker;
 
+// Enable a model that ranks zero-state files and recent queries.
+ASH_PUBLIC_EXPORT extern const base::Feature kEnableZeroStateMixedTypesRanker;
+
 // Enables the feature to include a single reinstallation candidate in
 // zero-state.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableAppReinstallZeroState;
@@ -77,6 +80,7 @@
 bool ASH_PUBLIC_EXPORT IsQueryBasedAppsRankerEnabled();
 bool ASH_PUBLIC_EXPORT IsZeroStateAppsRankerEnabled();
 bool ASH_PUBLIC_EXPORT IsQueryBasedMixedTypesRankerEnabled();
+bool ASH_PUBLIC_EXPORT IsZeroStateMixedTypesRankerEnabled();
 bool ASH_PUBLIC_EXPORT IsAppReinstallZeroStateEnabled();
 bool ASH_PUBLIC_EXPORT IsEmbeddedAssistantUIEnabled();
 bool ASH_PUBLIC_EXPORT IsAppGridGhostEnabled();
diff --git a/ash/keyboard/ui/public/keyboard_switches.cc b/ash/public/cpp/keyboard/keyboard_switches.cc
similarity index 85%
rename from ash/keyboard/ui/public/keyboard_switches.cc
rename to ash/public/cpp/keyboard/keyboard_switches.cc
index df21ecd..dd3e98f 100644
--- a/ash/keyboard/ui/public/keyboard_switches.cc
+++ b/ash/public/cpp/keyboard/keyboard_switches.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/keyboard/ui/public/keyboard_switches.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 
 namespace keyboard {
 namespace switches {
diff --git a/ash/public/cpp/keyboard/keyboard_switches.h b/ash/public/cpp/keyboard/keyboard_switches.h
new file mode 100644
index 0000000..e3a01d8
--- /dev/null
+++ b/ash/public/cpp/keyboard/keyboard_switches.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_PUBLIC_CPP_KEYBOARD_KEYBOARD_SWITCHES_H_
+#define ASH_PUBLIC_CPP_KEYBOARD_KEYBOARD_SWITCHES_H_
+
+#include "ash/public/cpp/ash_public_export.h"
+
+namespace keyboard {
+namespace switches {
+
+// Enables the virtual keyboard.
+ASH_PUBLIC_EXPORT extern const char kEnableVirtualKeyboard[];
+
+}  // namespace switches
+}  // namespace keyboard
+
+#endif  //  ASH_PUBLIC_CPP_KEYBOARD_KEYBOARD_SWITCHES_H_
diff --git a/ash/public/cpp/test/shell_test_api.h b/ash/public/cpp/test/shell_test_api.h
index e23379f..9367df69 100644
--- a/ash/public/cpp/test/shell_test_api.h
+++ b/ash/public/cpp/test/shell_test_api.h
@@ -12,8 +12,6 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 
-class PrefService;
-
 namespace ash {
 class DragDropController;
 class MessageCenterController;
@@ -43,9 +41,6 @@
   DragDropController* drag_drop_controller();
   PowerPrefs* power_prefs();
 
-  // Calls the private method.
-  void OnLocalStatePrefServiceInitialized(PrefService* pref_service);
-
   // Resets |shell_->power_button_controller_| to hold a new object to simulate
   // Chrome starting.
   void ResetPowerButtonControllerForTest();
diff --git a/ash/public/cpp/test/test_keyboard_controller_observer.h b/ash/public/cpp/test/test_keyboard_controller_observer.h
index 935ba98..9f9988a 100644
--- a/ash/public/cpp/test/test_keyboard_controller_observer.h
+++ b/ash/public/cpp/test/test_keyboard_controller_observer.h
@@ -5,7 +5,7 @@
 #ifndef ASH_PUBLIC_CPP_TEST_TEST_KEYBOARD_CONTROLLER_OBSERVER_H_
 #define ASH_PUBLIC_CPP_TEST_TEST_KEYBOARD_CONTROLLER_OBSERVER_H_
 
-#include "ash/keyboard/ui/public/keyboard_config.mojom.h"
+#include "ash/public/interfaces/keyboard_config.mojom.h"
 #include "ash/public/interfaces/keyboard_controller.mojom.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index fe8d646f..0ec2d2d 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -37,7 +37,9 @@
     "highlighter_controller.mojom",
     "ime_controller.mojom",
     "ime_info.mojom",
+    "keyboard_config.mojom",
     "keyboard_controller.mojom",
+    "keyboard_controller_types.mojom",
     "kiosk_next_shell.mojom",
     "locale.mojom",
     "login_screen.mojom",
@@ -64,7 +66,6 @@
   ]
 
   public_deps = [
-    "//ash/keyboard/ui:mojom",
     "//chromeos/components/proximity_auth/public/interfaces",
     "//chromeos/services/assistant/public/mojom",
     "//components/account_id/interfaces",
diff --git a/ash/public/interfaces/app_list.typemap b/ash/public/interfaces/app_list.typemap
index 169a3c49..672d5ce 100644
--- a/ash/public/interfaces/app_list.typemap
+++ b/ash/public/interfaces/app_list.typemap
@@ -8,6 +8,9 @@
 sources = [
   "//ash/public/cpp/app_list/app_list_struct_traits.cc",
 ]
+deps = [
+  "//ash/public/cpp:base",
+]
 type_mappings = [
   "ash.mojom.AppListState=ash::AppListState",
   "ash.mojom.AppListModelStatus=ash::AppListModelStatus",
diff --git a/ash/keyboard/ui/public/keyboard_config.mojom b/ash/public/interfaces/keyboard_config.mojom
similarity index 100%
rename from ash/keyboard/ui/public/keyboard_config.mojom
rename to ash/public/interfaces/keyboard_config.mojom
diff --git a/ash/public/interfaces/keyboard_controller.mojom b/ash/public/interfaces/keyboard_controller.mojom
index 8dffff4..a27418c 100644
--- a/ash/public/interfaces/keyboard_controller.mojom
+++ b/ash/public/interfaces/keyboard_controller.mojom
@@ -4,8 +4,8 @@
 
 module ash.mojom;
 
-import "ash/keyboard/ui/public/keyboard_config.mojom";
-import "ash/keyboard/ui/public/keyboard_controller_types.mojom";
+import "ash/public/interfaces/keyboard_config.mojom";
+import "ash/public/interfaces/keyboard_controller_types.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 
 enum HideReason {
diff --git a/ash/keyboard/ui/public/keyboard_controller_types.mojom b/ash/public/interfaces/keyboard_controller_types.mojom
similarity index 100%
rename from ash/keyboard/ui/public/keyboard_controller_types.mojom
rename to ash/public/interfaces/keyboard_controller_types.mojom
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc
index 025085a0..62a2d7e 100644
--- a/ash/root_window_controller_unittest.cc
+++ b/ash/root_window_controller_unittest.cc
@@ -9,8 +9,8 @@
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_ui.h"
 #include "ash/keyboard/ui/keyboard_util.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/session/test_session_controller_client.h"
diff --git a/ash/shelf/shelf_widget_unittest.cc b/ash/shelf/shelf_widget_unittest.cc
index e37aa3c31..b57109f 100644
--- a/ash/shelf/shelf_widget_unittest.cc
+++ b/ash/shelf/shelf_widget_unittest.cc
@@ -6,10 +6,10 @@
 
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_util.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/login_shelf_view.h"
 #include "ash/shelf/shelf.h"
diff --git a/ash/shell.cc b/ash/shell.cc
index d6326ef..ed7be4d0 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -891,6 +891,8 @@
   // Required by DetachableBaseHandler.
   chromeos::InitializeDBusClient<chromeos::HammerdClient>(dbus_bus.get());
 
+  local_state_ = local_state;
+
   // This creates the MessageCenter object which is used by some other objects
   // initialized here, so it needs to come early.
   message_center_controller_ = std::make_unique<MessageCenterController>();
@@ -898,8 +900,10 @@
   // These controllers call Shell::Get() in their constructors, so they cannot
   // be in the member initialization list.
   touch_devices_controller_ = std::make_unique<TouchDevicesController>();
-  bluetooth_power_controller_ = std::make_unique<BluetoothPowerController>();
-  detachable_base_handler_ = std::make_unique<DetachableBaseHandler>(this);
+  bluetooth_power_controller_ =
+      std::make_unique<BluetoothPowerController>(local_state_);
+  detachable_base_handler_ =
+      std::make_unique<DetachableBaseHandler>(local_state_);
   detachable_base_notification_controller_ =
       std::make_unique<DetachableBaseNotificationController>(
           detachable_base_handler_.get());
@@ -930,7 +934,7 @@
   // Shelf, and WallPaper could be created by the factory.
   views::FocusManagerFactory::Install(new AshFocusManagerFactory);
 
-  wallpaper_controller_ = std::make_unique<WallpaperController>();
+  wallpaper_controller_ = std::make_unique<WallpaperController>(local_state_);
 
   window_positioner_ = std::make_unique<WindowPositioner>();
 
@@ -940,7 +944,7 @@
 
   InitializeDisplayManager();
 
-  display_prefs_ = std::make_unique<DisplayPrefs>(local_state);
+  display_prefs_ = std::make_unique<DisplayPrefs>(local_state_);
 
   // This will initialize aura::Env which requires |display_manager_| to
   // be initialized first.
@@ -1033,9 +1037,9 @@
   sticky_keys_controller_.reset(new StickyKeysController);
   screen_pinning_controller_ = std::make_unique<ScreenPinningController>();
 
-  power_prefs_ =
-      std::make_unique<PowerPrefs>(chromeos::PowerPolicyController::Get(),
-                                   chromeos::PowerManagerClient::Get());
+  power_prefs_ = std::make_unique<PowerPrefs>(
+      chromeos::PowerPolicyController::Get(),
+      chromeos::PowerManagerClient::Get(), local_state_);
 
   backlights_forced_off_setter_ = std::make_unique<BacklightsForcedOffSetter>();
 
@@ -1182,10 +1186,6 @@
   // By this point ash shell should have initialized its D-Bus signal
   // listeners, so inform the session manager that Ash is initialized.
   session_controller_->EmitAshInitialized();
-
-  // Associates with local state.
-  if (local_state)
-    OnLocalStatePrefServiceInitialized(local_state);
 }
 
 void Shell::InitializeDisplayManager() {
@@ -1342,14 +1342,4 @@
 #endif
 }
 
-void Shell::OnLocalStatePrefServiceInitialized(PrefService* pref_service) {
-  DCHECK(!local_state_);
-  DCHECK(pref_service);
-
-  local_state_ = pref_service;
-
-  for (auto& observer : shell_observers_)
-    observer.OnLocalStatePrefServiceInitialized(local_state_);
-}
-
 }  // namespace ash
diff --git a/ash/shell.h b/ash/shell.h
index 656c933..e926b5f1 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -548,6 +548,8 @@
     return toplevel_window_event_handler_.get();
   }
 
+  PrefService* local_state() { return local_state_; }
+
   // Force the shelf to query for it's current visibility state.
   // TODO(jamescook): Move to Shelf.
   void UpdateShelfVisibility();
@@ -661,8 +663,6 @@
   void OnLoginStatusChanged(LoginStatus login_status) override;
   void OnLockStateChanged(bool locked) override;
 
-  void OnLocalStatePrefServiceInitialized(PrefService* pref_service);
-
   static Shell* instance_;
 
   // |owned_aura_env_| is non-null if Shell created aura::Env. Shell creates
diff --git a/ash/shell_observer.h b/ash/shell_observer.h
index bea0507..ace31ebc6 100644
--- a/ash/shell_observer.h
+++ b/ash/shell_observer.h
@@ -12,8 +12,6 @@
 class Window;
 }
 
-class PrefService;
-
 namespace ash {
 
 class ASH_EXPORT ShellObserver {
@@ -69,10 +67,6 @@
   // most of Shell's state has been deleted.
   virtual void OnShellDestroyed() {}
 
-  // Called when local state prefs are available. This occurs an arbitrary
-  // amount of time after Shell initialization. Only called once.
-  virtual void OnLocalStatePrefServiceInitialized(PrefService* pref_service) {}
-
  protected:
   virtual ~ShellObserver() {}
 };
diff --git a/ash/shell_test_api.cc b/ash/shell_test_api.cc
index a4e85a2..3572f3a 100644
--- a/ash/shell_test_api.cc
+++ b/ash/shell_test_api.cc
@@ -166,11 +166,6 @@
   return shell_->power_prefs_.get();
 }
 
-void ShellTestApi::OnLocalStatePrefServiceInitialized(
-    PrefService* pref_service) {
-  shell_->OnLocalStatePrefServiceInitialized(pref_service);
-}
-
 void ShellTestApi::ResetPowerButtonControllerForTest() {
   shell_->backlights_forced_off_setter_->ResetForTest();
   shell_->power_button_controller_ = std::make_unique<PowerButtonController>(
diff --git a/ash/shell_unittest.cc b/ash/shell_unittest.cc
index c55f035..e5cec16b 100644
--- a/ash/shell_unittest.cc
+++ b/ash/shell_unittest.cc
@@ -14,9 +14,9 @@
 #include "ash/drag_drop/drag_drop_controller_test_api.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_util.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_prefs.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/test/shell_test_api.h"
 #include "ash/root_window_controller.h"
@@ -182,22 +182,6 @@
   DISALLOW_COPY_AND_ASSIGN(SimpleMenuDelegate);
 };
 
-class TestShellObserver : public ShellObserver {
- public:
-  TestShellObserver() = default;
-  ~TestShellObserver() override = default;
-
-  // ShellObserver:
-  void OnLocalStatePrefServiceInitialized(PrefService* pref_service) override {
-    last_local_state_ = pref_service;
-  }
-
-  PrefService* last_local_state_ = nullptr;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestShellObserver);
-};
-
 }  // namespace
 
 class ShellTest : public AshTestBase {
@@ -674,21 +658,6 @@
   std::unique_ptr<TestingPrefServiceSimple> local_state_;
 };
 
-TEST_F(ShellLocalStateTest, LocalState) {
-  TestShellObserver observer;
-  Shell::Get()->AddShellObserver(&observer);
-
-  // Prefs service wrapper code creates a PrefService.
-  local_state_ = std::make_unique<TestingPrefServiceSimple>();
-  RegisterLocalStatePrefs(local_state_->registry(), true);
-  TestingPrefServiceSimple* local_state_ptr = local_state_.get();
-  ShellTestApi().OnLocalStatePrefServiceInitialized(local_state_ptr);
-  EXPECT_EQ(local_state_ptr, observer.last_local_state_);
-  EXPECT_EQ(local_state_ptr, ash_test_helper()->GetLocalStatePrefService());
-
-  Shell::Get()->RemoveShellObserver(&observer);
-}
-
 using ShellLoginTest = NoSessionAshTestBase;
 
 TEST_F(ShellLoginTest, DragAndDropDisabledBeforeLogin) {
diff --git a/ash/system/bluetooth/bluetooth_power_controller.cc b/ash/system/bluetooth/bluetooth_power_controller.cc
index 33c11f0..dcadaba 100644
--- a/ash/system/bluetooth/bluetooth_power_controller.cc
+++ b/ash/system/bluetooth/bluetooth_power_controller.cc
@@ -23,18 +23,29 @@
 // initialize power, so taking 1000 ms has enough time buffer for worst cases.
 const int kBluetoothInitializationDelay = 1000;
 
-BluetoothPowerController::BluetoothPowerController() : weak_ptr_factory_(this) {
+BluetoothPowerController::BluetoothPowerController(PrefService* local_state)
+    : local_state_(local_state), weak_ptr_factory_(this) {
   device::BluetoothAdapterFactory::GetAdapter(
       base::BindOnce(&BluetoothPowerController::InitializeOnAdapterReady,
                      weak_ptr_factory_.GetWeakPtr()));
-  Shell::Get()->AddShellObserver(this);
   Shell::Get()->session_controller()->AddObserver(this);
+
+  // AppLaunchTest.TestQuickLaunch fails under target=linux due to
+  // |local_state_| being nullptr.
+  if (local_state_) {
+    StartWatchingLocalStatePrefsChanges();
+
+    if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted()) {
+      // Apply the local state pref only if no user has logged in (still in
+      // login screen).
+      ApplyBluetoothLocalStatePref();
+    }
+  }
 }
 
 BluetoothPowerController::~BluetoothPowerController() {
   if (bluetooth_adapter_)
     bluetooth_adapter_->RemoveObserver(this);
-  Shell::Get()->RemoveShellObserver(this);
   Shell::Get()->session_controller()->RemoveObserver(this);
 }
 
@@ -42,9 +53,8 @@
   if (active_user_pref_service_) {
     active_user_pref_service_->SetBoolean(prefs::kUserBluetoothAdapterEnabled,
                                           enabled);
-  } else if (local_state_pref_service_) {
-    local_state_pref_service_->SetBoolean(prefs::kSystemBluetoothAdapterEnabled,
-                                          enabled);
+  } else if (local_state_) {
+    local_state_->SetBoolean(prefs::kSystemBluetoothAdapterEnabled, enabled);
   } else {
     DLOG(ERROR)
         << "active user and local state pref service cannot both be null";
@@ -78,10 +88,10 @@
 }
 
 void BluetoothPowerController::StartWatchingLocalStatePrefsChanges() {
-  DCHECK(local_state_pref_service_);
+  DCHECK(local_state_);
 
   local_state_pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
-  local_state_pref_change_registrar_->Init(local_state_pref_service_);
+  local_state_pref_change_registrar_->Init(local_state_);
   local_state_pref_change_registrar_->Add(
       prefs::kSystemBluetoothAdapterEnabled,
       base::Bind(
@@ -101,10 +111,10 @@
 }
 
 void BluetoothPowerController::OnBluetoothPowerLocalStatePrefChanged() {
-  DCHECK(local_state_pref_service_);
+  DCHECK(local_state_);
   BLUETOOTH_LOG(EVENT) << "Local state bluetooth power pref changed";
-  SetBluetoothPower(local_state_pref_service_->GetBoolean(
-      prefs::kSystemBluetoothAdapterEnabled));
+  SetBluetoothPower(
+      local_state_->GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
 }
 
 void BluetoothPowerController::SetPrimaryUserBluetoothPowerSetting(
@@ -149,24 +159,6 @@
   }
 }
 
-void BluetoothPowerController::OnLocalStatePrefServiceInitialized(
-    PrefService* pref_service) {
-  // AppLaunchTest.TestQuickLaunch fails under target=linux due to
-  // pref_service being nullptr.
-  if (!pref_service)
-    return;
-
-  local_state_pref_service_ = pref_service;
-
-  StartWatchingLocalStatePrefsChanges();
-
-  if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted()) {
-    // Apply the local state pref only if no user has logged in (still in login
-    // screen).
-    ApplyBluetoothLocalStatePref();
-  }
-}
-
 void BluetoothPowerController::AdapterPresentChanged(
     device::BluetoothAdapter* adapter,
     bool present) {
@@ -221,15 +213,14 @@
 }
 
 void BluetoothPowerController::ApplyBluetoothLocalStatePref() {
-  PrefService* prefs = local_state_pref_service_;
-
-  if (prefs->FindPreference(prefs::kSystemBluetoothAdapterEnabled)
+  if (local_state_->FindPreference(prefs::kSystemBluetoothAdapterEnabled)
           ->IsDefaultValue()) {
     // If the device has not had the local state bluetooth pref, set the pref
     // according to whatever the current bluetooth power is.
-    SavePrefValue(prefs, prefs::kSystemBluetoothAdapterEnabled);
+    SavePrefValue(local_state_, prefs::kSystemBluetoothAdapterEnabled);
   } else {
-    bool enabled = prefs->GetBoolean(prefs::kSystemBluetoothAdapterEnabled);
+    bool enabled =
+        local_state_->GetBoolean(prefs::kSystemBluetoothAdapterEnabled);
     BLUETOOTH_LOG(EVENT) << "Applying local state pref bluetooth power: "
                          << enabled;
     SetBluetoothPower(enabled);
diff --git a/ash/system/bluetooth/bluetooth_power_controller.h b/ash/system/bluetooth/bluetooth_power_controller.h
index a651990..4640ea7 100644
--- a/ash/system/bluetooth/bluetooth_power_controller.h
+++ b/ash/system/bluetooth/bluetooth_power_controller.h
@@ -7,7 +7,6 @@
 
 #include "ash/ash_export.h"
 #include "ash/session/session_observer.h"
-#include "ash/shell_observer.h"
 #include "base/containers/queue.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -29,10 +28,9 @@
 // setting instead.
 class ASH_EXPORT BluetoothPowerController
     : public SessionObserver,
-      public ShellObserver,
       public device::BluetoothAdapter::Observer {
  public:
-  BluetoothPowerController();
+  explicit BluetoothPowerController(PrefService* local_state);
   ~BluetoothPowerController() override;
 
   // Changes the bluetooth power setting to |enabled|.
@@ -55,9 +53,6 @@
   // SessionObserver:
   void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
 
-  // ShellObserver:
-  void OnLocalStatePrefServiceInitialized(PrefService* pref_service) override;
-
   // BluetoothAdapter::Observer:
   void AdapterPresentChanged(device::BluetoothAdapter* adapter,
                              bool present) override;
@@ -128,7 +123,7 @@
   bool is_primary_user_bluetooth_applied_ = false;
 
   PrefService* active_user_pref_service_ = nullptr;
-  PrefService* local_state_pref_service_ = nullptr;
+  PrefService* local_state_ = nullptr;
 
   // Contains pending tasks which depend on the availability of bluetooth
   // adapter.
diff --git a/ash/system/bluetooth/bluetooth_power_controller_unittest.cc b/ash/system/bluetooth/bluetooth_power_controller_unittest.cc
index 437d0df..cb80cac 100644
--- a/ash/system/bluetooth/bluetooth_power_controller_unittest.cc
+++ b/ash/system/bluetooth/bluetooth_power_controller_unittest.cc
@@ -58,10 +58,9 @@
 
     BluetoothPowerController::RegisterProfilePrefs(
         active_user_prefs_.registry());
-    BluetoothPowerController::RegisterLocalStatePrefs(
-        local_state_prefs_.registry());
+    BluetoothPowerController::RegisterLocalStatePrefs(local_state_.registry());
 
-    GetController()->local_state_pref_service_ = &local_state_prefs_;
+    GetController()->local_state_ = &local_state_;
 
     SetupBluetoothAdapter();
   }
@@ -105,7 +104,7 @@
   }
 
   TestingPrefServiceSimple active_user_prefs_;
-  TestingPrefServiceSimple local_state_prefs_;
+  TestingPrefServiceSimple local_state_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(BluetoothPowerControllerTest);
@@ -115,14 +114,11 @@
 TEST_F(BluetoothPowerControllerTest, ToggleBluetoothEnabled) {
   // Toggling bluetooth on/off when there is no user session should affect
   // local state prefs.
-  EXPECT_FALSE(
-      local_state_prefs_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+  EXPECT_FALSE(local_state_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
   GetController()->SetBluetoothEnabled(true);
-  EXPECT_TRUE(
-      local_state_prefs_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+  EXPECT_TRUE(local_state_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
   GetController()->SetBluetoothEnabled(false);
-  EXPECT_FALSE(
-      local_state_prefs_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+  EXPECT_FALSE(local_state_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
 
   // Toggling bluetooth on/off when there is user session should affect
   // user prefs.
@@ -144,15 +140,14 @@
 
   // Makes sure we start with bluetooth power off.
   EXPECT_FALSE(GetBluetoothAdapter()->IsPowered());
-  EXPECT_FALSE(
-      local_state_prefs_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+  EXPECT_FALSE(local_state_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
 
   // Power should be turned on when pref changes to enabled.
-  local_state_prefs_.SetBoolean(prefs::kSystemBluetoothAdapterEnabled, true);
+  local_state_.SetBoolean(prefs::kSystemBluetoothAdapterEnabled, true);
   EXPECT_TRUE(GetBluetoothAdapter()->IsPowered());
 
   // Power should be turned off when pref changes to disabled.
-  local_state_prefs_.SetBoolean(prefs::kSystemBluetoothAdapterEnabled, false);
+  local_state_.SetBoolean(prefs::kSystemBluetoothAdapterEnabled, false);
   EXPECT_FALSE(GetBluetoothAdapter()->IsPowered());
 }
 
@@ -208,7 +203,7 @@
 TEST_F(BluetoothPowerControllerTest, ApplyBluetoothLocalStatePrefDefault) {
   // Makes sure pref hasn't been set before.
   EXPECT_TRUE(
-      local_state_prefs_.FindPreference(prefs::kSystemBluetoothAdapterEnabled)
+      local_state_.FindPreference(prefs::kSystemBluetoothAdapterEnabled)
           ->IsDefaultValue());
   // Start with bluetooth power on.
   GetBluetoothAdapter()->SetPowered(true, base::DoNothing(), base::DoNothing());
@@ -218,19 +213,18 @@
 
   // Pref should now contain the current bluetooth adapter state (on).
   EXPECT_FALSE(
-      local_state_prefs_.FindPreference(prefs::kSystemBluetoothAdapterEnabled)
+      local_state_.FindPreference(prefs::kSystemBluetoothAdapterEnabled)
           ->IsDefaultValue());
-  EXPECT_TRUE(
-      local_state_prefs_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+  EXPECT_TRUE(local_state_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
 }
 
 // Tests how BluetoothPowerController applies the local state pref when
 // the pref has been set before.
 TEST_F(BluetoothPowerControllerTest, ApplyBluetoothLocalStatePrefOn) {
   // Set the pref to true.
-  local_state_prefs_.SetBoolean(prefs::kSystemBluetoothAdapterEnabled, true);
+  local_state_.SetBoolean(prefs::kSystemBluetoothAdapterEnabled, true);
   EXPECT_FALSE(
-      local_state_prefs_.FindPreference(prefs::kSystemBluetoothAdapterEnabled)
+      local_state_.FindPreference(prefs::kSystemBluetoothAdapterEnabled)
           ->IsDefaultValue());
   // Start with bluetooth power off.
   GetBluetoothAdapter()->SetPowered(false, base::DoNothing(),
@@ -240,8 +234,7 @@
   ApplyBluetoothLocalStatePref();
 
   // Bluetooth power setting should be applied (on), and pref value unchanged.
-  EXPECT_TRUE(
-      local_state_prefs_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+  EXPECT_TRUE(local_state_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
   EXPECT_TRUE(GetBluetoothAdapter()->IsPowered());
 }
 
diff --git a/ash/system/message_center/ash_popup_alignment_delegate_unittest.cc b/ash/system/message_center/ash_popup_alignment_delegate_unittest.cc
index b4092f22..c149179 100644
--- a/ash/system/message_center/ash_popup_alignment_delegate_unittest.cc
+++ b/ash/system/message_center/ash_popup_alignment_delegate_unittest.cc
@@ -10,7 +10,7 @@
 
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_util.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index 287cbc1..4331e31 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -72,10 +72,19 @@
 // Returns true if the |palette_tray| is on an internal display or on every
 // display if requested from the command line.
 bool ShouldShowOnDisplay(PaletteTray* palette_tray) {
+  if (stylus_utils::IsPaletteEnabledOnEveryDisplay())
+    return true;
+
+  // |widget| is null when this function is called from PaletteTray constructor
+  // before it is added to a widget.
+  views::Widget* const widget = palette_tray->GetWidget();
+  if (!widget)
+    return false;
+
   const display::Display& display =
       display::Screen::GetScreen()->GetDisplayNearestWindow(
-          palette_tray->GetWidget()->GetNativeWindow());
-  return display.IsInternal() || stylus_utils::IsPaletteEnabledOnEveryDisplay();
+          widget->GetNativeWindow());
+  return display.IsInternal();
 }
 
 class TitleView : public views::View, public views::ButtonListener {
@@ -182,6 +191,8 @@
   tray_container()->AddChildView(icon_);
 
   Shell::Get()->AddShellObserver(this);
+
+  InitializeWithLocalState();
 }
 
 PaletteTray::~PaletteTray() {
@@ -222,8 +233,8 @@
 }
 
 void PaletteTray::OnStylusEvent(const ui::TouchEvent& event) {
-  if (!HasSeenStylus() && local_state_pref_service_)
-    local_state_pref_service_->SetBoolean(prefs::kHasSeenStylus, true);
+  if (!HasSeenStylus() && local_state_)
+    local_state_->SetBoolean(prefs::kHasSeenStylus, true);
 
   // Attempt to show the welcome bubble.
   if (!welcome_bubble_->HasBeenShown() && active_user_pref_service_) {
@@ -277,27 +288,6 @@
   }
 }
 
-void PaletteTray::OnLocalStatePrefServiceInitialized(
-    PrefService* pref_service) {
-  local_state_pref_service_ = pref_service;
-
-  // If a device has an internal stylus or the flag to force stylus is set, mark
-  // the has seen stylus flag as true since we know the user has a stylus.
-  if (stylus_utils::HasInternalStylus() ||
-      stylus_utils::HasForcedStylusInput()) {
-    local_state_pref_service_->SetBoolean(prefs::kHasSeenStylus, true);
-  }
-
-  pref_change_registrar_local_ = std::make_unique<PrefChangeRegistrar>();
-  pref_change_registrar_local_->Init(local_state_pref_service_);
-  pref_change_registrar_local_->Add(
-      prefs::kHasSeenStylus,
-      base::BindRepeating(&PaletteTray::OnHasSeenStylusPrefChanged,
-                          base::Unretained(this)));
-
-  OnHasSeenStylusPrefChanged();
-}
-
 void PaletteTray::ClickedOutsideBubble() {
   if (num_actions_in_bubble_ == 0) {
     RecordPaletteOptionsUsage(PaletteTrayOptions::PALETTE_CLOSED_NO_ACTION,
@@ -528,6 +518,30 @@
   return bubble_ ? bubble_->bubble_view() : nullptr;
 }
 
+void PaletteTray::InitializeWithLocalState() {
+  DCHECK(!local_state_);
+  local_state_ = Shell::Get()->local_state();
+  // |local_state_| could be null in tests.
+  if (!local_state_)
+    return;
+
+  // If a device has an internal stylus or the flag to force stylus is set, mark
+  // the has seen stylus flag as true since we know the user has a stylus.
+  if (stylus_utils::HasInternalStylus() ||
+      stylus_utils::HasForcedStylusInput()) {
+    local_state_->SetBoolean(prefs::kHasSeenStylus, true);
+  }
+
+  pref_change_registrar_local_ = std::make_unique<PrefChangeRegistrar>();
+  pref_change_registrar_local_->Init(local_state_);
+  pref_change_registrar_local_->Add(
+      prefs::kHasSeenStylus,
+      base::BindRepeating(&PaletteTray::OnHasSeenStylusPrefChanged,
+                          base::Unretained(this)));
+
+  OnHasSeenStylusPrefChanged();
+}
+
 void PaletteTray::UpdateTrayIcon() {
   icon_->SetImage(CreateVectorIcon(
       palette_tool_manager_->GetActiveTrayIcon(
@@ -548,7 +562,7 @@
 }
 
 void PaletteTray::OnHasSeenStylusPrefChanged() {
-  DCHECK(local_state_pref_service_);
+  DCHECK(local_state_);
 
   UpdateIconVisibility();
 }
@@ -569,8 +583,7 @@
 }
 
 bool PaletteTray::HasSeenStylus() {
-  return local_state_pref_service_ &&
-         local_state_pref_service_->GetBoolean(prefs::kHasSeenStylus);
+  return local_state_ && local_state_->GetBoolean(prefs::kHasSeenStylus);
 }
 
 void PaletteTray::UpdateIconVisibility() {
diff --git a/ash/system/palette/palette_tray.h b/ash/system/palette/palette_tray.h
index f067c64..f2e1588 100644
--- a/ash/system/palette/palette_tray.h
+++ b/ash/system/palette/palette_tray.h
@@ -69,7 +69,6 @@
 
   // ShellObserver:
   void OnLockStateChanged(bool locked) override;
-  void OnLocalStatePrefServiceInitialized(PrefService* pref_service) override;
 
   // TrayBackgroundView:
   void ClickedOutsideBubble() override;
@@ -106,6 +105,9 @@
   void OnActiveToolChanged() override;
   aura::Window* GetWindow() override;
 
+  // Initializes with Shell's local state and starts to observe it.
+  void InitializeWithLocalState();
+
   // Updates the tray icon from the palette tool manager.
   void UpdateTrayIcon();
 
@@ -132,7 +134,7 @@
   // A Shell pre-target handler that notifies PaletteTray of stylus events.
   std::unique_ptr<ui::EventHandler> stylus_event_handler_;
 
-  PrefService* local_state_pref_service_ = nullptr;  // Not owned.
+  PrefService* local_state_ = nullptr;               // Not owned.
   PrefService* active_user_pref_service_ = nullptr;  // Not owned.
   std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_local_;
   std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_user_;
diff --git a/ash/system/power/power_prefs.cc b/ash/system/power/power_prefs.cc
index ad3b5a4..e62f14c 100644
--- a/ash/system/power/power_prefs.cc
+++ b/ash/system/power/power_prefs.cc
@@ -136,22 +136,25 @@
 }  // namespace
 
 PowerPrefs::PowerPrefs(chromeos::PowerPolicyController* power_policy_controller,
-                       chromeos::PowerManagerClient* power_manager_client)
+                       chromeos::PowerManagerClient* power_manager_client,
+                       PrefService* local_state)
     : power_policy_controller_(power_policy_controller),
       power_manager_client_observer_(this),
-      tick_clock_(base::DefaultTickClock::GetInstance()) {
+      tick_clock_(base::DefaultTickClock::GetInstance()),
+      local_state_(local_state) {
   DCHECK(power_manager_client);
   DCHECK(power_policy_controller_);
   DCHECK(tick_clock_);
 
-  Shell::Get()->AddShellObserver(this);
-
   power_manager_client_observer_.Add(power_manager_client);
   Shell::Get()->session_controller()->AddObserver(this);
+
+  // |local_state_| could be null in tests.
+  if (local_state_)
+    ObserveLocalStatePrefs(local_state_);
 }
 
 PowerPrefs::~PowerPrefs() {
-  Shell::Get()->RemoveShellObserver(this);
   Shell::Get()->session_controller()->RemoveObserver(this);
 }
 
@@ -243,16 +246,6 @@
   ObservePrefs(prefs);
 }
 
-void PowerPrefs::OnLocalStatePrefServiceInitialized(PrefService* prefs) {
-  local_state_ = prefs;
-
-  // Pass |nullptr| in tests, because lifetime of local state prefs is shorter
-  // than lifetime of PowerPrefs.
-  if (local_state_) {
-    ObserveLocalStatePrefs(prefs);
-  }
-}
-
 void PowerPrefs::UpdatePowerPolicyFromPrefs() {
   PrefService* prefs = GetPrefService();
   if (!prefs || !local_state_)
diff --git a/ash/system/power/power_prefs.h b/ash/system/power/power_prefs.h
index 8e52e29..7671a402 100644
--- a/ash/system/power/power_prefs.h
+++ b/ash/system/power/power_prefs.h
@@ -9,7 +9,6 @@
 
 #include "ash/ash_export.h"
 #include "ash/session/session_observer.h"
-#include "ash/shell_observer.h"
 #include "base/macros.h"
 #include "base/scoped_observer.h"
 #include "base/time/tick_clock.h"
@@ -29,14 +28,16 @@
 
 namespace ash {
 
+class PowerPrefsTest;
+
 // Sends an updated power policy to the |power_policy_controller| whenever one
 // of the power-related prefs changes.
 class ASH_EXPORT PowerPrefs : public chromeos::PowerManagerClient::Observer,
-                              public SessionObserver,
-                              public ShellObserver {
+                              public SessionObserver {
  public:
   PowerPrefs(chromeos::PowerPolicyController* power_policy_controller,
-             chromeos::PowerManagerClient* power_manager_client);
+             chromeos::PowerManagerClient* power_manager_client,
+             PrefService* local_state);
   ~PowerPrefs() override;
 
   // Registers power prefs with default values applicable to the local state
@@ -54,6 +55,8 @@
   void set_tick_clock_for_test(base::TickClock* clock) { tick_clock_ = clock; }
 
  private:
+  friend class PowerPrefsTest;
+
   // chromeos::PowerManagerClient::Observer:
   void ScreenIdleStateChanged(
       const power_manager::ScreenIdleState& proto) override;
@@ -63,9 +66,6 @@
   void OnSigninScreenPrefServiceInitialized(PrefService* prefs) override;
   void OnActiveUserPrefServiceChanged(PrefService* prefs) override;
 
-  // ShellObserver:
-  void OnLocalStatePrefServiceInitialized(PrefService* pref_service) override;
-
   void UpdatePowerPolicyFromPrefs();
 
   // Observes either the signin screen prefs or active user prefs and loads
diff --git a/ash/system/power/power_prefs_unittest.cc b/ash/system/power/power_prefs_unittest.cc
index 7f4a8cb3..87c7adbc 100644
--- a/ash/system/power/power_prefs_unittest.cc
+++ b/ash/system/power/power_prefs_unittest.cc
@@ -200,8 +200,7 @@
   }
 
   void TearDown() override {
-    static_cast<ShellObserver*>(power_prefs_)
-        ->OnLocalStatePrefServiceInitialized(nullptr);
+    power_prefs_->local_state_ = nullptr;
 
     NoSessionAshTestBase::TearDown();
   }
@@ -220,8 +219,8 @@
 
     PowerPrefs::RegisterLocalStatePrefs(pref_registry_.get());
 
-    static_cast<ShellObserver*>(power_prefs_)
-        ->OnLocalStatePrefServiceInitialized(local_state_.get());
+    power_prefs_->local_state_ = local_state_.get();
+    power_prefs_->ObserveLocalStatePrefs(power_prefs_->local_state_);
   }
 
   std::string GetCurrentPowerPolicy() const {
diff --git a/ash/system/status_area_widget_unittest.cc b/ash/system/status_area_widget_unittest.cc
index 10d2cd5..a20d8b3 100644
--- a/ash/system/status_area_widget_unittest.cc
+++ b/ash/system/status_area_widget_unittest.cc
@@ -7,9 +7,9 @@
 #include "ash/focus_cycler.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_util.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
 #include "ash/public/cpp/ash_switches.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/cpp/system_tray_focus_observer.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/session/test_session_controller_client.h"
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index 9b9230e..523ef01 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -230,16 +230,14 @@
 constexpr int kUnifiedFeaturePodHoverRadius = 4;
 constexpr int kUnifiedFeaturePodVerticalPadding = 28;
 constexpr int kUnifiedFeaturePodTopPadding = 24;
-constexpr int kUnifiedFeaturePodBottomPadding = 5;
+constexpr int kUnifiedFeaturePodBottomPadding = 20;
 constexpr int kUnifiedFeaturePodHorizontalSidePadding = 12;
 constexpr int kUnifiedFeaturePodHorizontalMiddlePadding = 0;
-constexpr int kUnifiedFeaturePodCollapsedVerticalPadding = 12;
+constexpr int kUnifiedFeaturePodCollapsedVerticalPadding = 16;
 constexpr int kUnifiedFeaturePodCollapsedHorizontalPadding = 24;
 constexpr int kUnifiedFeaturePodArrowSpacing = 4;
 constexpr int kUnifiedFeaturePodItemsInRow = 3;
-constexpr int kUnifiedFeaturePodItemsRows = 3;
 constexpr int kUnifiedFeaturePodMaxItemsInCollapsed = 5;
-constexpr int kUnifiedFeaturePodsPageSpacing = 48;
 constexpr int kUnifiedNotificationSeparatorThickness = 1;
 
 // Constants used in PageIndicatorView of UnifiedSystemTray.
diff --git a/ash/system/unified/feature_pods_container_view.cc b/ash/system/unified/feature_pods_container_view.cc
index b6004d33..21127a8 100644
--- a/ash/system/unified/feature_pods_container_view.cc
+++ b/ash/system/unified/feature_pods_container_view.cc
@@ -4,26 +4,15 @@
 
 #include "ash/system/unified/feature_pods_container_view.h"
 
-#include "ash/public/cpp/ash_features.h"
-#include "ash/public/cpp/pagination/pagination_controller.h"
-#include "ash/public/cpp/pagination/pagination_model.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/feature_pod_button.h"
 
 namespace ash {
 
-FeaturePodsContainerView::FeaturePodsContainerView(
-    PaginationModel* pagination_model,
-    bool initially_expanded)
-    : pagination_model_(pagination_model),
-      expanded_amount_(initially_expanded ? 1.0 : 0.0) {
-  pagination_model_->AddObserver(this);
-}
+FeaturePodsContainerView::FeaturePodsContainerView(bool initially_expanded)
+    : expanded_amount_(initially_expanded ? 1.0 : 0.0) {}
 
-FeaturePodsContainerView::~FeaturePodsContainerView() {
-  DCHECK(pagination_model_);
-  pagination_model_->RemoveObserver(this);
-}
+FeaturePodsContainerView::~FeaturePodsContainerView() = default;
 
 void FeaturePodsContainerView::SetExpandedAmount(double expanded_amount) {
   DCHECK(0.0 <= expanded_amount && expanded_amount <= 1.0);
@@ -44,10 +33,6 @@
   // floor(visible_count / kUnifiedFeaturePodItemsInRow)
   int number_of_lines = (visible_count + kUnifiedFeaturePodItemsInRow - 1) /
                         kUnifiedFeaturePodItemsInRow;
-
-  if (features::IsSystemTrayFeaturePodsPaginationEnabled())
-    number_of_lines = std::min(number_of_lines, kUnifiedFeaturePodItemsRows);
-
   return kUnifiedFeaturePodBottomPadding +
          (kUnifiedFeaturePodVerticalPadding + kUnifiedFeaturePodSize.height()) *
              number_of_lines;
@@ -98,10 +83,27 @@
 
 void FeaturePodsContainerView::Layout() {
   UpdateCollapsedSidePadding();
-  CalculateIdealBoundsForFeaturePods();
-  for (int i = 0; i < visible_buttons_.view_size(); ++i) {
-    auto* button = visible_buttons_.view_at(i);
-    button->SetBoundsRect(visible_buttons_.ideal_bounds(i));
+
+  int visible_count = 0;
+  for (auto* child : children()) {
+    if (!child->visible())
+      continue;
+
+    gfx::Size child_size;
+    if (expanded_amount_ > 0.0) {
+      child_size = kUnifiedFeaturePodSize;
+
+      // Flexibly give more height if the child view doesn't fit into the
+      // default height, so that label texts won't be broken up in the middle.
+      child_size.set_height(std::max(
+          child_size.height(), child->GetHeightForWidth(child_size.height())));
+    } else {
+      child_size = kUnifiedFeaturePodCollapsedSize;
+    }
+
+    child->SetBoundsRect(
+        gfx::Rect(GetButtonPosition(visible_count++), child_size));
+    child->Layout();
   }
 }
 
@@ -112,26 +114,15 @@
   int visible_count = 0;
   for (auto* view : children()) {
     auto* child = static_cast<FeaturePodButton*>(view);
-    bool visible = IsButtonVisible(child, visible_count);
+    bool visible = child->visible_preferred() &&
+                   (expanded_amount_ > 0.0 ||
+                    visible_count < kUnifiedFeaturePodMaxItemsInCollapsed);
     child->SetVisibleByContainer(visible);
-    if (visible) {
-      if (visible_buttons_.GetIndexOfView(child) < 0)
-        visible_buttons_.Add(child, visible_count);
+    if (visible)
       ++visible_count;
-    } else {
-      if (visible_buttons_.GetIndexOfView(child))
-        visible_buttons_.Remove(visible_buttons_.GetIndexOfView(child));
-    }
   }
-  UpdateTotalPages();
-  changing_visibility_ = false;
-}
 
-bool FeaturePodsContainerView::IsButtonVisible(FeaturePodButton* button,
-                                               int index) {
-  return button->visible_preferred() &&
-         (expanded_amount_ > 0.0 ||
-          index < kUnifiedFeaturePodMaxItemsInCollapsed);
+  changing_visibility_ = false;
 }
 
 int FeaturePodsContainerView::GetVisibleCount() const {
@@ -191,106 +182,4 @@
   DCHECK(collapsed_side_padding_ > 0);
 }
 
-void FeaturePodsContainerView::AddFeaturePodButton(FeaturePodButton* button) {
-  int view_size = visible_buttons_.view_size();
-  if (IsButtonVisible(button, view_size)) {
-    visible_buttons_.Add(button, view_size);
-  }
-  AddChildView(button);
-
-  UpdateTotalPages();
-}
-
-const gfx::Vector2d FeaturePodsContainerView::CalculateTransitionOffset(
-    int page_of_view) const {
-  gfx::Size grid_size = CalculatePreferredSize();
-
-  // If there is a transition, calculates offset for current and target page.
-  const int current_page = pagination_model_->selected_page();
-  const PaginationModel::Transition& transition =
-      pagination_model_->transition();
-  const bool is_valid =
-      pagination_model_->is_valid_page(transition.target_page);
-
-  // Transition to previous page means negative offset.
-  const int direction = transition.target_page > current_page ? -1 : 1;
-
-  int x_offset = 0;
-  int y_offset = 0;
-
-  // Page size including padding pixels. A tile.x + page_width means the same
-  // tile slot in the next page.
-  const int page_width = grid_size.width() + kUnifiedFeaturePodsPageSpacing;
-  if (page_of_view < current_page)
-    x_offset = -page_width;
-  else if (page_of_view > current_page)
-    x_offset = page_width;
-
-  if (is_valid) {
-    if (page_of_view == current_page ||
-        page_of_view == transition.target_page) {
-      x_offset += transition.progress * page_width * direction;
-    }
-  }
-
-  return gfx::Vector2d(x_offset, y_offset);
-}
-
-void FeaturePodsContainerView::CalculateIdealBoundsForFeaturePods() {
-  for (int i = 0; i < visible_buttons_.view_size(); ++i) {
-    gfx::Rect tile_bounds;
-    gfx::Size child_size;
-    if (expanded_amount_ > 0.0) {
-      child_size = kUnifiedFeaturePodSize;
-
-      // Flexibly give more height if the child view doesn't fit into the
-      // default height, so that label texts won't be broken up in the middle.
-      child_size.set_height(std::max(
-          child_size.height(),
-          visible_buttons_.view_at(i)->GetHeightForWidth(child_size.height())));
-
-      tile_bounds =
-          gfx::Rect(GetButtonPosition(i % GetTilesPerPage()), child_size);
-      // TODO(amehfooz): refactor this logic so that the ideal_bounds are set
-      // once when the transition starts and the actual feature pod bounds are
-      // interpolated using the ideal_bounds as the transition progresses.
-      tile_bounds.Offset(CalculateTransitionOffset(i / GetTilesPerPage()));
-    } else {
-      child_size = kUnifiedFeaturePodCollapsedSize;
-      tile_bounds = gfx::Rect(GetButtonPosition(i), child_size);
-    }
-
-    visible_buttons_.set_ideal_bounds(i, tile_bounds);
-  }
-}
-
-int FeaturePodsContainerView::GetTilesPerPage() const {
-  if (features::IsSystemTrayFeaturePodsPaginationEnabled())
-    return kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodItemsRows;
-  else
-    return children().size();
-}
-
-void FeaturePodsContainerView::UpdateTotalPages() {
-  int total_pages = 0;
-
-  int total_visible = visible_buttons_.view_size();
-  int tiles_per_page = GetTilesPerPage();
-
-  if (!visible_buttons_.view_size() || !tiles_per_page) {
-    total_pages = 0;
-  } else {
-    total_pages = (total_visible / tiles_per_page) +
-                  (total_visible % tiles_per_page ? 1 : 0);
-  }
-  pagination_model_->SetTotalPages(total_pages);
-}
-
-void FeaturePodsContainerView::TransitionChanged() {
-  const PaginationModel::Transition& transition =
-      pagination_model_->transition();
-  if (pagination_model_->is_valid_page(transition.target_page))
-    Layout();
-}
-
 }  // namespace ash
diff --git a/ash/system/unified/feature_pods_container_view.h b/ash/system/unified/feature_pods_container_view.h
index a8ade81..c93737e6 100644
--- a/ash/system/unified/feature_pods_container_view.h
+++ b/ash/system/unified/feature_pods_container_view.h
@@ -6,31 +6,20 @@
 #define ASH_SYSTEM_UNIFIED_FEATURE_PODS_CONTAINER_VIEW_H_
 
 #include "ash/ash_export.h"
-#include "ash/public/cpp/pagination/pagination_model_observer.h"
 #include "ui/views/view.h"
-#include "ui/views/view_model.h"
 
 namespace ash {
 
-class FeaturePodButton;
-class PaginationModel;
-
 // Container of feature pods buttons in the middle of UnifiedSystemTrayView.
 // The container has number of buttons placed in 3x3 plane at regular distances.
 // FeaturePodButtons implements these individual buttons.
 // The container also implements collapsed state where the top 5 buttons are
 // horizontally placed and others are hidden.
-class ASH_EXPORT FeaturePodsContainerView : public views::View,
-                                            public PaginationModelObserver {
+class ASH_EXPORT FeaturePodsContainerView : public views::View {
  public:
-  FeaturePodsContainerView(PaginationModel* pagination_model,
-                           bool initially_expanded);
+  explicit FeaturePodsContainerView(bool initially_expanded);
   ~FeaturePodsContainerView() override;
 
-  // Add a FeaturePodButton as a child view and if it's visible add it to the
-  // view structure and update the pagination model.
-  void AddFeaturePodButton(FeaturePodButton* button);
-
   // Change the expanded state. 0.0 if collapsed, and 1.0 if expanded.
   // Otherwise, it shows intermediate state. If collapsed, all the buttons are
   // horizontally placed.
@@ -58,39 +47,15 @@
   void Layout() override;
 
  private:
-  friend class FeaturePodsContainerViewTest;
+  void UpdateChildVisibility();
 
   // Calculate the current position of the button from |visible_index| and
   // |expanded_amount_|.
   gfx::Point GetButtonPosition(int visible_index) const;
 
-  void UpdateChildVisibility();
-
   // Update |collapsed_state_padding_|.
   void UpdateCollapsedSidePadding();
 
-  // Calculates the ideal bounds for all feature pods.
-  void CalculateIdealBoundsForFeaturePods();
-
-  // Calculates the offset for |page_of_view| based on current page and
-  // transition target page.
-  const gfx::Vector2d CalculateTransitionOffset(int page_of_view) const;
-
-  // Returns true if button at provided index is visible.
-  bool IsButtonVisible(FeaturePodButton* button, int index);
-
-  // Returns the number of tiles per page.
-  int GetTilesPerPage() const;
-
-  // Updates page splits for feature pod buttons.
-  void UpdateTotalPages();
-
-  // PaginationModelObserver:
-  void TransitionChanged() override;
-
-  // Owned by UnifiedSystemTrayModel.
-  PaginationModel* pagination_model_;
-
   // The last |expanded_amount| passed to SetExpandedAmount().
   double expanded_amount_;
 
@@ -104,10 +69,6 @@
   // A button that had focus at the point SaveButtonFocus is called.
   views::View* focused_button_ = nullptr;
 
-  // A view model that contains all visible feature pod buttons.
-  // Used to calculate required number of pages.
-  views::ViewModelT<FeaturePodButton> visible_buttons_;
-
   DISALLOW_COPY_AND_ASSIGN(FeaturePodsContainerView);
 };
 
diff --git a/ash/system/unified/feature_pods_container_view_unittest.cc b/ash/system/unified/feature_pods_container_view_unittest.cc
index 747add5..8a3ec63 100644
--- a/ash/system/unified/feature_pods_container_view_unittest.cc
+++ b/ash/system/unified/feature_pods_container_view_unittest.cc
@@ -4,13 +4,10 @@
 
 #include "ash/system/unified/feature_pods_container_view.h"
 
-#include "ash/public/cpp/ash_features.h"
-#include "ash/public/cpp/pagination/pagination_model.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/feature_pod_button.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 #include "ash/test/ash_test_base.h"
-#include "base/test/scoped_feature_list.h"
 #include "ui/views/view_observer.h"
 
 namespace ash {
@@ -25,13 +22,10 @@
   // AshTestBase:
   void SetUp() override {
     AshTestBase::SetUp();
-    pagination_model_ = std::make_unique<PaginationModel>();
     container_ = std::make_unique<FeaturePodsContainerView>(
-        pagination_model_.get(), true /* initially_expanded */);
+        true /* initially_expanded */);
     container_->AddObserver(this);
     preferred_size_changed_count_ = 0;
-
-    scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
   }
 
   // FeaturePodControllerBase:
@@ -47,11 +41,6 @@
   }
 
  protected:
-  void EnablePagination() {
-    scoped_feature_list_->InitAndEnableFeature(
-        features::kSystemTrayFeaturePodsPagination);
-  }
-
   void AddButtons(int count) {
     for (int i = 0; i < count; ++i) {
       buttons_.push_back(new FeaturePodButton(this));
@@ -63,8 +52,6 @@
 
   FeaturePodsContainerView* container() { return container_.get(); }
 
-  PaginationModel* pagination_model() { return pagination_model_.get(); }
-
   int preferred_size_changed_count() const {
     return preferred_size_changed_count_;
   }
@@ -72,9 +59,7 @@
   std::vector<FeaturePodButton*> buttons_;
 
  private:
-  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
   std::unique_ptr<FeaturePodsContainerView> container_;
-  std::unique_ptr<PaginationModel> pagination_model_;
   int preferred_size_changed_count_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(FeaturePodsContainerViewTest);
@@ -179,59 +164,4 @@
   EXPECT_EQ(2, preferred_size_changed_count());
 }
 
-TEST_F(FeaturePodsContainerViewTest, NumberOfPagesChanged) {
-  const int kNumberOfPages = 8;
-
-  EnablePagination();
-  AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodItemsRows *
-             kNumberOfPages);
-
-  // Adding buttons to fill kNumberOfPages should cause the the same number of
-  // pages to be created.
-  EXPECT_EQ(kNumberOfPages, pagination_model()->total_pages());
-
-  // Adding an additional button causes a new page to be added.
-  AddButtons(1);
-  EXPECT_EQ(pagination_model()->total_pages(), kNumberOfPages + 1);
-}
-
-TEST_F(FeaturePodsContainerViewTest, PaginationTransition) {
-  const int kNumberOfPages = 8;
-
-  EnablePagination();
-  AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodItemsRows *
-             kNumberOfPages);
-
-  // Position of a button should slide to the left during a page
-  // transition to the next page.
-  gfx::Rect current_bounds;
-  gfx::Rect initial_bounds = buttons_[0]->bounds();
-  gfx::Rect previous_bounds = initial_bounds;
-
-  PaginationModel::Transition transition(
-      pagination_model()->selected_page() + 1, 0);
-
-  for (double i = 0.1; i <= 1.0; i += 0.1) {
-    transition.progress = i;
-    pagination_model()->SetTransition(transition);
-
-    current_bounds = buttons_[0]->bounds();
-
-    EXPECT_LT(current_bounds.x(), previous_bounds.x());
-    EXPECT_EQ(current_bounds.y(), previous_bounds.y());
-
-    previous_bounds = current_bounds;
-  }
-
-  // Button Position after page switch should move to the left by a page offset.
-  int page_offset = container()->CalculatePreferredSize().width() +
-                    kUnifiedFeaturePodsPageSpacing;
-  gfx::Rect final_bounds =
-      gfx::Rect(initial_bounds.x() - page_offset, initial_bounds.y(),
-                initial_bounds.width(), initial_bounds.height());
-  pagination_model()->SelectPage(1, false);
-  container()->Layout();
-  EXPECT_EQ(final_bounds, buttons_[0]->bounds());
-}
-
 }  // namespace ash
diff --git a/ash/system/unified/unified_system_tray_view.cc b/ash/system/unified/unified_system_tray_view.cc
index e376ec2..a5ed7ea 100644
--- a/ash/system/unified/unified_system_tray_view.cc
+++ b/ash/system/unified/unified_system_tray_view.cc
@@ -16,7 +16,6 @@
 #include "ash/system/unified/feature_pod_button.h"
 #include "ash/system/unified/feature_pods_container_view.h"
 #include "ash/system/unified/notification_hidden_view.h"
-#include "ash/system/unified/page_indicator_view.h"
 #include "ash/system/unified/top_shortcuts_view.h"
 #include "ash/system/unified/unified_system_info_view.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
@@ -221,11 +220,7 @@
       controller_(controller),
       notification_hidden_view_(new NotificationHiddenView()),
       top_shortcuts_view_(new TopShortcutsView(controller_)),
-      feature_pods_container_(
-          new FeaturePodsContainerView(controller_->model()->pagination_model(),
-                                       initially_expanded)),
-      page_indicator_view_(
-          new PageIndicatorView(controller_, initially_expanded)),
+      feature_pods_container_(new FeaturePodsContainerView(initially_expanded)),
       sliders_container_(new UnifiedSlidersContainerView(initially_expanded)),
       system_info_view_(new UnifiedSystemInfoView(controller_)),
       system_tray_container_(new SystemTrayContainer()),
@@ -260,7 +255,6 @@
 
   system_tray_container_->AddChildView(top_shortcuts_view_);
   system_tray_container_->AddChildView(feature_pods_container_);
-  system_tray_container_->AddChildView(page_indicator_view_);
   system_tray_container_->AddChildView(sliders_container_);
   system_tray_container_->AddChildView(system_info_view_);
 
@@ -294,7 +288,7 @@
 }
 
 void UnifiedSystemTrayView::AddFeaturePodButton(FeaturePodButton* button) {
-  feature_pods_container_->AddFeaturePodButton(button);
+  feature_pods_container_->AddChildView(button);
 }
 
 void UnifiedSystemTrayView::AddSliderView(views::View* slider_view) {
@@ -341,7 +335,6 @@
 
   top_shortcuts_view_->SetExpandedAmount(expanded_amount);
   feature_pods_container_->SetExpandedAmount(expanded_amount);
-  page_indicator_view_->SetExpandedAmount(expanded_amount);
   sliders_container_->SetExpandedAmount(expanded_amount);
 
   if (!IsTransformEnabled()) {
@@ -365,7 +358,6 @@
               : 0) +
          top_shortcuts_view_->GetPreferredSize().height() +
          feature_pods_container_->GetExpandedHeight() +
-         page_indicator_view_->GetPreferredSize().height() +
          sliders_container_->GetExpandedHeight() +
          system_info_view_->GetPreferredSize().height();
 }
diff --git a/ash/system/unified/unified_system_tray_view.h b/ash/system/unified/unified_system_tray_view.h
index 423a5ff..fb1e556 100644
--- a/ash/system/unified/unified_system_tray_view.h
+++ b/ash/system/unified/unified_system_tray_view.h
@@ -15,7 +15,6 @@
 class FeaturePodsContainerView;
 class TopShortcutsView;
 class NotificationHiddenView;
-class PageIndicatorView;
 class UnifiedMessageCenterView;
 class UnifiedSystemInfoView;
 class UnifiedSystemTrayController;
@@ -148,7 +147,6 @@
   NotificationHiddenView* const notification_hidden_view_;
   TopShortcutsView* const top_shortcuts_view_;
   FeaturePodsContainerView* const feature_pods_container_;
-  PageIndicatorView* const page_indicator_view_;
   UnifiedSlidersContainerView* const sliders_container_;
   UnifiedSystemInfoView* const system_info_view_;
   views::View* const system_tray_container_;
diff --git a/ash/system/virtual_keyboard/virtual_keyboard_tray_unittest.cc b/ash/system/virtual_keyboard/virtual_keyboard_tray_unittest.cc
index 458361b..d7aa47f8 100644
--- a/ash/system/virtual_keyboard/virtual_keyboard_tray_unittest.cc
+++ b/ash/system/virtual_keyboard/virtual_keyboard_tray_unittest.cc
@@ -6,8 +6,8 @@
 
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_util.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/status_area_widget_test_helper.h"
 #include "ash/test/ash_test_base.h"
diff --git a/ash/wallpaper/wallpaper_controller.cc b/ash/wallpaper/wallpaper_controller.cc
index 7a69f01d..587ea0d 100644
--- a/ash/wallpaper/wallpaper_controller.cc
+++ b/ash/wallpaper/wallpaper_controller.cc
@@ -43,6 +43,7 @@
 #include "base/values.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "services/data_decoder/public/cpp/decode_image.h"
 #include "ui/display/manager/display_manager.h"
@@ -441,7 +442,7 @@
 const char WallpaperController::kLargeWallpaperSubDir[] = "large";
 const char WallpaperController::kOriginalWallpaperSubDir[] = "original";
 
-WallpaperController::WallpaperController()
+WallpaperController::WallpaperController(PrefService* local_state)
     : locked_(false),
       wallpaper_mode_(WALLPAPER_NONE),
       color_profiles_(GetProminentColorProfiles()),
@@ -450,6 +451,7 @@
           {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
       scoped_session_observer_(this),
+      local_state_(local_state),
       weak_factory_(this) {
   DCHECK(!color_profiles_.empty());
   prominent_colors_ =
@@ -1366,12 +1368,6 @@
   InstallDesktopController(root_window);
 }
 
-void WallpaperController::OnLocalStatePrefServiceInitialized(
-    PrefService* pref_service) {
-  DCHECK(!wallpaper_controller_client_);
-  local_state_ = pref_service;
-}
-
 void WallpaperController::OnShellInitialized() {
   Shell::Get()->tablet_mode_controller()->AddObserver(this);
 }
diff --git a/ash/wallpaper/wallpaper_controller.h b/ash/wallpaper/wallpaper_controller.h
index 9b49616..1f31c1b 100644
--- a/ash/wallpaper/wallpaper_controller.h
+++ b/ash/wallpaper/wallpaper_controller.h
@@ -82,7 +82,7 @@
   static const char kLargeWallpaperSubDir[];
   static const char kOriginalWallpaperSubDir[];
 
-  WallpaperController();
+  explicit WallpaperController(PrefService* local_state);
   ~WallpaperController() override;
 
   static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
@@ -295,7 +295,6 @@
 
   // ShellObserver:
   void OnRootWindowAdded(aura::Window* root_window) override;
-  void OnLocalStatePrefServiceInitialized(PrefService* pref_service) override;
   void OnShellInitialized() override;
   void OnShellDestroying() override;
 
@@ -661,9 +660,9 @@
   // default wallpaper decoding is initiated.)
   std::vector<base::FilePath> decode_requests_for_testing_;
 
-  // PrefService provided by Shell::OnLocalStatePrefServiceInitialized.
+  // PrefService provided by Shell when constructing this.
   // Valid for the lifetime of ash::Shell which owns WallpaperController.
-  // May be null during intialization or in tests.
+  // May be null in tests.
   PrefService* local_state_ = nullptr;
 
   base::WeakPtrFactory<WallpaperController> weak_factory_;
diff --git a/ash/wm/always_on_top_controller_unittest.cc b/ash/wm/always_on_top_controller_unittest.cc
index 6f954c8..8714958 100644
--- a/ash/wm/always_on_top_controller_unittest.cc
+++ b/ash/wm/always_on_top_controller_unittest.cc
@@ -7,8 +7,8 @@
 #include "ash/keyboard/ash_keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_ui.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
diff --git a/ash/wm/collision_detection/collision_detection_utils_unittest.cc b/ash/wm/collision_detection/collision_detection_utils_unittest.cc
index f95a3fa..0f20f552 100644
--- a/ash/wm/collision_detection/collision_detection_utils_unittest.cc
+++ b/ash/wm/collision_detection/collision_detection_utils_unittest.cc
@@ -5,8 +5,8 @@
 #include "ash/wm/collision_detection/collision_detection_utils.h"
 
 #include "ash/keyboard/ui/keyboard_controller.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/root_window_controller.h"
 #include "ash/scoped_root_window_for_new_windows.h"
 #include "ash/shelf/shelf.h"
diff --git a/ash/wm/lock_action_handler_layout_manager_unittest.cc b/ash/wm/lock_action_handler_layout_manager_unittest.cc
index fc7f475..0d50f1e2 100644
--- a/ash/wm/lock_action_handler_layout_manager_unittest.cc
+++ b/ash/wm/lock_action_handler_layout_manager_unittest.cc
@@ -11,11 +11,11 @@
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_ui.h"
 #include "ash/keyboard/ui/keyboard_util.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
 #include "ash/lock_screen_action/lock_screen_action_background_controller.h"
 #include "ash/lock_screen_action/lock_screen_action_background_controller_stub.h"
 #include "ash/lock_screen_action/test_lock_screen_action_background_controller.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/interfaces/tray_action.mojom.h"
 #include "ash/root_window_controller.h"
diff --git a/ash/wm/lock_layout_manager_unittest.cc b/ash/wm/lock_layout_manager_unittest.cc
index 4b8d773..fbbf0e5 100644
--- a/ash/wm/lock_layout_manager_unittest.cc
+++ b/ash/wm/lock_layout_manager_unittest.cc
@@ -5,8 +5,8 @@
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_ui.h"
 #include "ash/keyboard/ui/keyboard_util.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
diff --git a/ash/wm/overview/overview_controller_unittest.cc b/ash/wm/overview/overview_controller_unittest.cc
index 54ea941..f636c68 100644
--- a/ash/wm/overview/overview_controller_unittest.cc
+++ b/ash/wm/overview/overview_controller_unittest.cc
@@ -7,8 +7,8 @@
 #include "ash/app_list/test/app_list_test_helper.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_util.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wallpaper/wallpaper_widget_controller.h"
diff --git a/ash/wm/pip/pip_positioner_unittest.cc b/ash/wm/pip/pip_positioner_unittest.cc
index d63328c..7df1911 100644
--- a/ash/wm/pip/pip_positioner_unittest.cc
+++ b/ash/wm/pip/pip_positioner_unittest.cc
@@ -10,8 +10,8 @@
 #include <vector>
 
 #include "ash/keyboard/ui/keyboard_controller.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/root_window_controller.h"
 #include "ash/scoped_root_window_for_new_windows.h"
 #include "ash/shelf/shelf.h"
diff --git a/ash/wm/pip/pip_unittest.cc b/ash/wm/pip/pip_unittest.cc
index de19aa4..82df7d88 100644
--- a/ash/wm/pip/pip_unittest.cc
+++ b/ash/wm/pip/pip_unittest.cc
@@ -8,8 +8,8 @@
 #include <utility>
 
 #include "ash/keyboard/ui/keyboard_controller.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_widget.h"
diff --git a/ash/wm/pip/pip_window_resizer_unittest.cc b/ash/wm/pip/pip_window_resizer_unittest.cc
index 5172552..b9ec671 100644
--- a/ash/wm/pip/pip_window_resizer_unittest.cc
+++ b/ash/wm/pip/pip_window_resizer_unittest.cc
@@ -9,9 +9,9 @@
 #include <utility>
 
 #include "ash/keyboard/ui/keyboard_controller.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
 #include "ash/metrics/pip_uma.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/scoped_root_window_for_new_windows.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
diff --git a/ash/wm/system_modal_container_layout_manager_unittest.cc b/ash/wm/system_modal_container_layout_manager_unittest.cc
index 71093e1..cf681b13 100644
--- a/ash/wm/system_modal_container_layout_manager_unittest.cc
+++ b/ash/wm/system_modal_container_layout_manager_unittest.cc
@@ -9,8 +9,8 @@
 #include "ash/keyboard/ash_keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_ui.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
diff --git a/ash/wm/window_animations_unittest.cc b/ash/wm/window_animations_unittest.cc
index acdda97..a5d369a 100644
--- a/ash/wm/window_animations_unittest.cc
+++ b/ash/wm/window_animations_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/wm/window_animations.h"
 
-#include "ash/keyboard/ui/public/keyboard_switches.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_animation_types.h"
 #include "ash/shell.h"
diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc
index 5b08381..6406454 100644
--- a/ash/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc
@@ -14,10 +14,10 @@
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_ui.h"
 #include "ash/keyboard/ui/keyboard_util.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/app_types.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/test/shell_test_api.h"
 #include "ash/public/cpp/window_properties.h"
diff --git a/base/android/java/src/org/chromium/base/FileUtils.java b/base/android/java/src/org/chromium/base/FileUtils.java
index 5ceded4..20d3dcf 100644
--- a/base/android/java/src/org/chromium/base/FileUtils.java
+++ b/base/android/java/src/org/chromium/base/FileUtils.java
@@ -9,9 +9,10 @@
 import android.graphics.BitmapFactory;
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 
-import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
@@ -29,9 +30,13 @@
 
     /**
      * Delete the given File and (if it's a directory) everything within it.
+     * @param currentFile The file or directory to delete. Does not need to exist.
+     * @return Whether currentFile does not exist afterwards.
      */
-    public static void recursivelyDeleteFile(File currentFile) {
-        ThreadUtils.assertOnBackgroundThread();
+    public static boolean recursivelyDeleteFile(File currentFile) {
+        if (!currentFile.exists()) {
+            return true;
+        }
         if (currentFile.isDirectory()) {
             File[] files = currentFile.listFiles();
             if (files != null) {
@@ -41,7 +46,11 @@
             }
         }
 
-        if (!currentFile.delete()) Log.e(TAG, "Failed to delete: " + currentFile);
+        boolean ret = currentFile.delete();
+        if (!ret) {
+            Log.e(TAG, "Failed to delete: %s", currentFile);
+        }
+        return ret;
     }
 
     /**
@@ -50,8 +59,6 @@
      * @param filePaths The file paths or content URIs to delete.
      */
     public static void batchDeleteFiles(List<String> filePaths) {
-        ThreadUtils.assertOnBackgroundThread();
-
         for (String filePath : filePaths) {
             if (ContentUriUtils.isContentUri(filePath)) {
                 ContentUriUtils.delete(filePath);
@@ -66,58 +73,41 @@
      * Extracts an asset from the app's APK to a file.
      * @param context
      * @param assetName Name of the asset to extract.
-     * @param dest File to extract the asset to.
+     * @param outFile File to extract the asset to.
      * @return true on success.
      */
-    public static boolean extractAsset(Context context, String assetName, File dest) {
-        InputStream inputStream = null;
-        OutputStream outputStream = null;
-        try {
-            inputStream = context.getAssets().open(assetName);
-            outputStream = new BufferedOutputStream(new FileOutputStream(dest));
-            byte[] buffer = new byte[8192];
-            int c;
-            while ((c = inputStream.read(buffer)) != -1) {
-                outputStream.write(buffer, 0, c);
-            }
-            inputStream.close();
-            outputStream.close();
+    public static boolean extractAsset(Context context, String assetName, File outFile) {
+        try (InputStream inputStream = context.getAssets().open(assetName)) {
+            copyStreamToFile(inputStream, outFile);
             return true;
         } catch (IOException e) {
-            if (inputStream != null) {
-                try {
-                    inputStream.close();
-                } catch (IOException ex) {
-                }
-            }
-            if (outputStream != null) {
-                try {
-                    outputStream.close();
-                } catch (IOException ex) {
-                }
-            }
+            return false;
         }
-        return false;
+    }
+
+    /**
+     * Performs a simple copy of inputStream to outputStream.
+     */
+    public static void copyStream(InputStream inputStream, OutputStream outputStream)
+            throws IOException {
+        byte[] buffer = new byte[8192];
+        int amountRead;
+        while ((amountRead = inputStream.read(buffer)) != -1) {
+            outputStream.write(buffer, 0, amountRead);
+        }
     }
 
     /**
      * Atomically copies the data from an input stream into an output file.
      * @param is Input file stream to read data from.
      * @param outFile Output file path.
-     * @param buffer Caller-provided buffer. Provided to avoid allocating the same
-     *         buffer on each call when copying several files in sequence.
      * @throws IOException in case of I/O error.
      */
-    public static void copyFileStreamAtomicWithBuffer(InputStream is, File outFile, byte[] buffer)
-            throws IOException {
+    public static void copyStreamToFile(InputStream is, File outFile) throws IOException {
         File tmpOutputFile = new File(outFile.getPath() + ".tmp");
         try (OutputStream os = new FileOutputStream(tmpOutputFile)) {
             Log.i(TAG, "Writing to %s", outFile);
-
-            int count = 0;
-            while ((count = is.read(buffer, 0, buffer.length)) != -1) {
-                os.write(buffer, 0, count);
-            }
+            copyStream(is, os);
         }
         if (!tmpOutputFile.renameTo(outFile)) {
             throw new IOException();
@@ -125,6 +115,16 @@
     }
 
     /**
+     * Reads inputStream into a byte array.
+     */
+    @NonNull
+    public static byte[] readStream(InputStream inputStream) throws IOException {
+        ByteArrayOutputStream data = new ByteArrayOutputStream();
+        FileUtils.copyStream(inputStream, data);
+        return data.toByteArray();
+    }
+
+    /**
      * Returns a URI that points at the file.
      * @param file File to get a URI for.
      * @return URI that points at that file, either as a content:// URI or a file:// URI.
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index ae710cf..72d116b7 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -747,8 +747,7 @@
                     throw new RuntimeException("Cannot find ZipEntry" + pathWithinApk);
                 InputStream inputStream = zipFile.getInputStream(zipEntry);
 
-                FileUtils.copyFileStreamAtomicWithBuffer(
-                        inputStream, libraryFile, new byte[16 * 1024]);
+                FileUtils.copyStreamToFile(inputStream, libraryFile);
                 libraryFile.setReadable(true, false);
                 libraryFile.setExecutable(true, false);
             } catch (IOException e) {
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index a0ab2e8..73a4c48 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -57,7 +57,8 @@
   } else {
     import("//build/config/android/sdk.gni")
     declare_args() {
-      # Android SDK release. Currently, only "o_mr1" is publicly supported.
+      # Android SDK release. Currently, only "o_mr1" and "p" are publicly
+      # supported.
       android_sdk_release = default_android_sdk_release
     }
   }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index f501a921..704a1db 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8913465501118992272
\ No newline at end of file
+8913436241537136912
\ No newline at end of file
diff --git a/build/landmines.py b/build/landmines.py
index d0f42980..c811bf9 100755
--- a/build/landmines.py
+++ b/build/landmines.py
@@ -37,7 +37,7 @@
 
 
 def get_build_dir(src_dir):
-  """
+  r"""
   Returns output directory absolute path dependent on build and targets.
   Examples:
     r'c:\b\build\slave\win\build\src\out'
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index 15e385e6..c226670 100755
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -5,6 +5,7 @@
 
 from __future__ import print_function
 
+import collections
 import glob
 import json
 import os
@@ -22,9 +23,11 @@
 script_dir = os.path.dirname(os.path.realpath(__file__))
 json_data_file = os.path.join(script_dir, 'win_toolchain.json')
 
-
-# Use MSVS2017 as the default toolchain.
-CURRENT_DEFAULT_TOOLCHAIN_VERSION = '2017'
+# VS versions are listed in descending order of priority (highest first).
+MSVS_VERSIONS = collections.OrderedDict([
+  ('2017', '15.0'),
+  ('2019', '16.0'),
+])
 
 
 def SetEnvironmentAndGetRuntimeDllDirs():
@@ -129,9 +132,47 @@
 
 
 def GetVisualStudioVersion():
-  """Return GYP_MSVS_VERSION of Visual Studio.
+  """Return best available version of Visual Studio.
   """
-  return os.environ.get('GYP_MSVS_VERSION', CURRENT_DEFAULT_TOOLCHAIN_VERSION)
+
+  env_version = os.environ.get('GYP_MSVS_VERSION')
+  supported_versions = MSVS_VERSIONS.keys()
+
+  # VS installed in depot_tools for Googlers
+  if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))):
+    if env_version:
+      return env_version
+    else:
+      return supported_versions[0]
+
+  # VS installed in system for external developers
+  supported_versions_str = ', '.join('{} ({})'.format(v,k)
+      for k,v in MSVS_VERSIONS.items())
+  available_versions = []
+  for version in supported_versions:
+    for path in (
+        os.environ.get('vs%s_install' % version),
+        os.path.expandvars('%ProgramFiles(x86)%' +
+                           '/Microsoft Visual Studio/%s' % version)):
+      if path and os.path.exists(path):
+        available_versions.append(version)
+        break
+
+  if not available_versions:
+    raise Exception('No supported Visual Studio can be found.'
+                    ' Supported versions are: %s.' % supported_versions_str)
+
+  if env_version:
+    if env_version not in supported_versions:
+      raise Exception('Visual Studio version %s (from GYP_MSVS_VERSION)'
+                      ' is not supported. Supported versions are: %s.'
+                      % (env_version, supported_versions_str))
+    if env_version not in available_versions:
+      raise Exception('Visual Studio version %s (from GYP_MSVS_VERSION)'
+                      ' is not available.' % env_version)
+    return env_version
+
+  return available_versions[0]
 
 
 def DetectVisualStudioPath():
@@ -141,14 +182,6 @@
   # Note that this code is used from
   # build/toolchain/win/setup_toolchain.py as well.
   version_as_year = GetVisualStudioVersion()
-  year_to_version = {
-      '2017': '15.0',
-      '2019': '16.0',
-  }
-  if version_as_year not in year_to_version:
-    raise Exception(('Visual Studio version %s (from GYP_MSVS_VERSION)'
-                     ' not supported. Supported versions are: %s') % (
-                       version_as_year, ', '.join(year_to_version.keys())))
 
   # The VC++ >=2017 install location needs to be located using COM instead of
   # the registry. For details see:
@@ -171,8 +204,8 @@
     if path and os.path.exists(path):
       return path
 
-  raise Exception(('Visual Studio Version %s (from GYP_MSVS_VERSION)'
-                   ' not found.') % (version_as_year))
+  raise Exception('Visual Studio Version %s (from GYP_MSVS_VERSION)'
+                  ' not found.' % version_as_year)
 
 
 def _CopyRuntimeImpl(target, source, verbose=True):
@@ -267,7 +300,7 @@
   version number part changes frequently so the highest version number found is
   used.
   """
-  assert GetVisualStudioVersion() in ['2017', '2019']
+
   SetEnvironmentAndGetRuntimeDllDirs()
   assert ('GYP_MSVS_OVERRIDE_PATH' in os.environ)
   vc_component_msvc_root = os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'],
diff --git a/cc/trees/proxy_main.cc b/cc/trees/proxy_main.cc
index 1a7ffbb..3629d673 100644
--- a/cc/trees/proxy_main.cc
+++ b/cc/trees/proxy_main.cc
@@ -547,9 +547,9 @@
 void ProxyMain::SetMutator(std::unique_ptr<LayerTreeMutator> mutator) {
   TRACE_EVENT0("cc", "ThreadProxy::SetMutator");
   ImplThreadTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&ProxyImpl::InitializeMutatorOnImpl,
-                                base::Unretained(proxy_impl_.get()),
-                                base::Passed(std::move(mutator))));
+      FROM_HERE,
+      base::BindOnce(&ProxyImpl::InitializeMutatorOnImpl,
+                     base::Unretained(proxy_impl_.get()), std::move(mutator)));
 }
 
 void ProxyMain::SetPaintWorkletLayerPainter(
@@ -558,8 +558,7 @@
   ImplThreadTaskRunner()->PostTask(
       FROM_HERE,
       base::BindOnce(&ProxyImpl::InitializePaintWorkletLayerPainterOnImpl,
-                     base::Unretained(proxy_impl_.get()),
-                     base::Passed(std::move(painter))));
+                     base::Unretained(proxy_impl_.get()), std::move(painter)));
 }
 
 bool ProxyMain::SupportsImplScrolling() const {
diff --git a/chrome/VERSION b/chrome/VERSION
index 8db8a39..6fa27ac 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=76
 MINOR=0
-BUILD=3795
+BUILD=3796
 PATCH=0
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 6816e8f6..092f9f33 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1466,7 +1466,6 @@
   "java/src/org/chromium/chrome/browser/tab/TabContextMenuPopulator.java",
   "java/src/org/chromium/chrome/browser/tab/TabDelegateFactory.java",
   "java/src/org/chromium/chrome/browser/tab/TabFavicon.java",
-  "java/src/org/chromium/chrome/browser/tab/TabFullscreenHandler.java",
   "java/src/org/chromium/chrome/browser/tab/TabGestureStateListener.java",
   "java/src/org/chromium/chrome/browser/tab/TabIdManager.java",
   "java/src/org/chromium/chrome/browser/tab/TabImportanceManager.java",
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
index dccd860..ec83aa2e 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
@@ -127,6 +127,21 @@
     private final PropertyModel mModel = mMediator.getModelForTesting();
     private final UserDataHost mUserDataHost = new UserDataHost();
 
+    private static class MockActivityTabProvider extends ActivityTabProvider {
+        public Tab mTab;
+
+        public void set(Tab tab) {
+            mTab = tab;
+        }
+
+        @Override
+        public Tab get() {
+            return mTab;
+        }
+    };
+
+    private final MockActivityTabProvider mActivityTabProvider = new MockActivityTabProvider();
+
     /**
      * Helper class that provides shortcuts to providing and observing AccessorySheetData and
      * Actions.
@@ -268,7 +283,7 @@
         when(mMockWindow.getActivity()).thenReturn(new WeakReference<>(mMockActivity));
         when(mMockKeyboard.calculateKeyboardHeight(any())).thenReturn(0);
         when(mMockActivity.getTabModelSelector()).thenReturn(mMockTabModelSelector);
-        when(mMockActivity.getActivityTabProvider()).thenReturn(mock(ActivityTabProvider.class));
+        when(mMockActivity.getActivityTabProvider()).thenReturn(mActivityTabProvider);
         ChromeFullscreenManager fullscreenManager = new ChromeFullscreenManager(mMockActivity, 0);
         when(mMockActivity.getFullscreenManager()).thenReturn(fullscreenManager);
         when(mMockActivity.getCompositorViewHolder()).thenReturn(mMockCompositorViewHolder);
@@ -1019,8 +1034,8 @@
         when(tab.getWebContents()).thenReturn(mLastMockWebContents);
         mCache.getStateFor(tab).getWebContentsObserverForTesting().wasShown();
         when(tab.getContentView()).thenReturn(mMockContentView);
-        when(mMockActivity.getActivityTabProvider().get()).thenReturn(tab);
         when(mMockTabModelSelector.getCurrentTab()).thenReturn(tab);
+        mActivityTabProvider.set(tab);
         mediator.getTabModelObserverForTesting().didAddTab(tab, FROM_BROWSER_ACTIONS);
         mediator.getTabObserverForTesting().onShown(tab, FROM_NEW);
         mediator.getTabModelObserverForTesting().didSelectTab(tab, FROM_NEW, lastId);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 8c71901..2f1d6b5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -128,7 +128,7 @@
 import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.chrome.browser.sync.SyncController;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabFullscreenHandler;
+import org.chromium.chrome.browser.tab.TabBrowserControlsState;
 import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModel;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
@@ -871,7 +871,7 @@
                 // When resuming the activity, force an update to the fullscreen state to ensure a
                 // subactivity did not change the fullscreen configuration of this ChromeTab's
                 // renderer in the case where it was shared.
-                TabFullscreenHandler.updateEnabledState(tab);
+                TabBrowserControlsState.updateEnabledState(tab);
             }
             VrModuleProvider.getDelegate().onActivityShown(this);
         } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
index 4bbe3e52..59b33d4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
@@ -41,7 +41,7 @@
 import org.chromium.chrome.browser.tab.SadTab;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.Tab.TabHidingType;
-import org.chromium.chrome.browser.tab.TabFullscreenHandler;
+import org.chromium.chrome.browser.tab.TabBrowserControlsState;
 import org.chromium.chrome.browser.tab.TabThemeColorHelper;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
@@ -510,7 +510,7 @@
     @Override
     public void releaseOverlayPanelContent() {
         if (getTabModelSelector() == null) return;
-        TabFullscreenHandler.updateEnabledState(getTabModelSelector().getCurrentTab());
+        TabBrowserControlsState.updateEnabledState(getTabModelSelector().getCurrentTab());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBrowserControlsVisibilityDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBrowserControlsVisibilityDelegate.java
index 93085521..8d4aaf34f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBrowserControlsVisibilityDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBrowserControlsVisibilityDelegate.java
@@ -9,7 +9,7 @@
 import org.chromium.chrome.browser.fullscreen.BrowserStateBrowserControlsVisibilityDelegate;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.tab.BrowserControlsVisibilityDelegate;
-import org.chromium.chrome.browser.tab.TabFullscreenHandler;
+import org.chromium.chrome.browser.tab.TabBrowserControlsState;
 
 import javax.inject.Inject;
 
@@ -72,6 +72,6 @@
     }
 
     private void updateActiveTabFullscreenEnabledState() {
-        TabFullscreenHandler.updateEnabledState(mTabProvider.get());
+        TabBrowserControlsState.updateEnabledState(mTabProvider.get());
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
index 67b0351..00622b1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
@@ -384,9 +384,6 @@
      * A task for creating module {@link ClassLoader}.
      */
     private class ClassLoaderTask extends AsyncTask<ClassLoader> {
-        /** Buffer size to use while copying an input stream into the disk. */
-        private static final int BUFFER_SIZE = 16 * 1024;
-
         @Override
         @Nullable
         protected ClassLoader doInBackground() {
@@ -453,7 +450,7 @@
         private void copyDexToDisk(String dexAssetName) throws IOException {
             InputStream in =
                     mDexInputStreamProvider.createInputStream(dexAssetName, mModuleContext);
-            FileUtils.copyFileStreamAtomicWithBuffer(in, getDexFile(), new byte[BUFFER_SIZE]);
+            FileUtils.copyStreamToFile(in, getDexFile());
         }
 
         private ClassLoader getModuleClassLoader(boolean loadFromDex) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
index 8018d43..5a71c76 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
@@ -476,7 +476,8 @@
     }
 
     private void showControlsTransient(Tab tab) {
-        FullscreenManager fullscreenManager = tab.getFullscreenManager();
+        assert mChromeActivity != null;
+        FullscreenManager fullscreenManager = mChromeActivity.getFullscreenManager();
         if (!(fullscreenManager instanceof ChromeFullscreenManager)) return;
         ((ChromeFullscreenManager) fullscreenManager).getBrowserVisibilityDelegate()
                 .showControlsTransient();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
index c434d4a..1ee5512 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
@@ -25,16 +25,22 @@
 import org.chromium.chrome.browser.fullscreen.FullscreenHtmlApiHandler.FullscreenHtmlApiDelegate;
 import org.chromium.chrome.browser.tab.BrowserControlsVisibilityDelegate;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.Tab.TabHidingType;
+import org.chromium.chrome.browser.tab.TabAttributeKeys;
+import org.chromium.chrome.browser.tab.TabAttributes;
 import org.chromium.chrome.browser.tab.TabBrowserControlsOffsetHelper;
 import org.chromium.chrome.browser.tab.TabBrowserControlsState;
-import org.chromium.chrome.browser.tab.TabFullscreenHandler;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
 import org.chromium.chrome.browser.tabmodel.TabSelectionType;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
 import org.chromium.chrome.browser.widget.ControlContainer;
+import org.chromium.content_public.browser.NavigationHandle;
+import org.chromium.content_public.browser.SelectionPopupController;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.common.BrowserControlsState;
 
 import java.lang.annotation.Retention;
@@ -58,6 +64,7 @@
     private final boolean mExitFullscreenOnStop;
     private final TokenHolder mHidingTokenHolder = new TokenHolder(this::scheduleVisibilityUpdate);
 
+    private TabModelSelectorTabObserver mTabFullscreenObserver;
     @Nullable private ControlContainer mControlContainer;
     private int mTopControlContainerHeight;
     private int mBottomControlContainerHeight;
@@ -173,7 +180,7 @@
                     @Override
                     public void run() {
                         if (getTab() != null) {
-                            TabFullscreenHandler.updateEnabledState(getTab());
+                            TabBrowserControlsState.updateEnabledState(getTab());
                         } else if (!mBrowserVisibilityDelegate.canAutoHideBrowserControls()) {
                             setPositionsForTabToNonFullscreen();
                         }
@@ -220,6 +227,81 @@
             }
         };
 
+        mTabFullscreenObserver = new TabModelSelectorTabObserver(modelSelector) {
+            @Override
+            public void onHidden(Tab tab, @TabHidingType int reason) {
+                // Clean up any fullscreen state that might impact other tabs.
+                exitPersistentFullscreenMode();
+            }
+
+            @Override
+            public void onDidFinishNavigation(Tab tab, NavigationHandle navigation) {
+                if (navigation.isInMainFrame() && !navigation.isSameDocument()) {
+                    if (tab == getTab()) exitPersistentFullscreenMode();
+                }
+            }
+
+            @Override
+            public void onInteractabilityChanged(boolean interactable) {
+                if (interactable) {
+                    Runnable enterFullscreen = getEnterFullscreenRunnable(getTab());
+                    if (enterFullscreen != null) enterFullscreen.run();
+                }
+            }
+
+            @Override
+            public void onEnterFullscreenMode(Tab tab, final FullscreenOptions options) {
+                // If enabling fullscreen while the tab is not interactable, fullscreen
+                // * will be delayed until the tab is interactable.
+                if (tab.isUserInteractable()) {
+                    enterPersistentFullscreenMode(options);
+                    destroySelectActionMode(tab);
+                } else {
+                    setEnterFullscreenRunnable(tab, () -> {
+                        enterPersistentFullscreenMode(options);
+                        destroySelectActionMode(tab);
+                        setEnterFullscreenRunnable(tab, null);
+                    });
+                }
+            }
+
+            @Override
+            public void onExitFullscreenMode(Tab tab) {
+                setEnterFullscreenRunnable(tab, null);
+                if (tab == getTab()) exitPersistentFullscreenMode();
+            }
+
+            @Override
+            public void onContentViewChildrenStateUpdated(Tab tab) {
+                if (tab == getTab()) updateContentViewChildrenState();
+            }
+
+            @Override
+            public void onContentViewSystemUiVisibilityChanged(Tab tab, int visibility) {
+                if (tab == getTab()) onContentViewSystemUiVisibilityChange(visibility);
+            }
+
+            private void setEnterFullscreenRunnable(Tab tab, Runnable runnable) {
+                TabAttributes attrs = TabAttributes.from(tab);
+                if (runnable == null) {
+                    attrs.clear(TabAttributeKeys.ENTER_FULLSCREEN);
+                } else {
+                    attrs.set(TabAttributeKeys.ENTER_FULLSCREEN, runnable);
+                }
+            }
+
+            private Runnable getEnterFullscreenRunnable(Tab tab) {
+                return tab != null ? TabAttributes.from(tab).get(TabAttributeKeys.ENTER_FULLSCREEN)
+                                   : null;
+            }
+
+            private void destroySelectActionMode(Tab tab) {
+                WebContents webContents = tab.getWebContents();
+                if (webContents != null) {
+                    SelectionPopupController.fromWebContents(webContents).destroySelectActionMode();
+                }
+            }
+        };
         assert controlContainer != null || mControlsPosition == ControlsPosition.NONE;
         mControlContainer = controlContainer;
 
@@ -301,7 +383,7 @@
                     // We should hide browser controls first.
                     mPendingFullscreenOptions = options;
                     mIsEnteringPersistentModeState = true;
-                    TabFullscreenHandler.updateEnabledState(tab);
+                    TabBrowserControlsState.updateEnabledState(tab);
                 }
             }
 
@@ -711,5 +793,6 @@
     public void destroy() {
         super.destroy();
         mBrowserVisibilityDelegate.destroy();
+        if (mTabFullscreenObserver != null) mTabFullscreenObserver.destroy();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java
index 714618a..443c817e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java
@@ -8,10 +8,11 @@
 import android.view.View;
 import android.view.Window;
 
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.fullscreen.FullscreenHtmlApiHandler.FullscreenHtmlApiDelegate;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabBrowserControlsOffsetHelper;
-import org.chromium.chrome.browser.tab.TabFullscreenHandler;
+import org.chromium.chrome.browser.tab.TabBrowserControlsState;
 
 /**
  * Manages the basic fullscreen functionality required by a Tab.
@@ -26,6 +27,20 @@
     @Nullable private Tab mTab;
 
     /**
+     * @return {@link FullscreenManager} instance which a given {@link Tab}
+     *         is associated with as the active tab; {@code null} if the tab
+     *         is not an active one.
+     * TODO(jinsukim): Look into removing this method.
+     */
+    public static FullscreenManager from(Tab tab) {
+        if (tab == null) return null;
+        ChromeActivity activity = tab.getActivity();
+        if (activity == null) return null;
+        FullscreenManager manager = activity.getFullscreenManager();
+        return manager.getTab() == tab ? manager : null;
+    }
+
+    /**
      * Constructs the basic ChromeTab oriented FullscreenManager.
      *
      * @param window Top-level window to turn to fullscreen.
@@ -121,19 +136,8 @@
     public void setTab(@Nullable Tab tab) {
         if (mTab == tab) return;
 
-        // Remove the fullscreen manager from the old tab before setting the new tab.
-        setFullscreenManager(null);
-
         mTab = tab;
-
-        // Initialize the new tab with the correct fullscreen manager reference.
-        setFullscreenManager(this);
-    }
-
-    private void setFullscreenManager(FullscreenManager manager) {
-        if (mTab == null) return;
-        mTab.setFullscreenManager(manager);
-        TabBrowserControlsOffsetHelper.from(mTab).resetPositions();
+        if (mTab != null) TabBrowserControlsOffsetHelper.from(mTab).resetPositions();
     }
 
     /**
@@ -147,9 +151,9 @@
      * Enters persistent fullscreen mode.  In this mode, the browser controls will be
      * permanently hidden until this mode is exited.
      */
-    public void enterPersistentFullscreenMode(FullscreenOptions options) {
+    protected void enterPersistentFullscreenMode(FullscreenOptions options) {
         mHtmlApiHandler.enterPersistentFullscreenMode(options);
-        TabFullscreenHandler.updateEnabledState(getTab());
+        TabBrowserControlsState.updateEnabledState(getTab());
     }
 
     /**
@@ -158,7 +162,7 @@
      */
     public void exitPersistentFullscreenMode() {
         mHtmlApiHandler.exitPersistentFullscreenMode();
-        TabFullscreenHandler.updateEnabledState(getTab());
+        TabBrowserControlsState.updateEnabledState(getTab());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java
index 3f73a2e..f7aacfc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java
@@ -160,4 +160,10 @@
 
     @Override
     public void onFindMatchRectsAvailable(FindMatchRectsDetails result) {}
+
+    @Override
+    public void onContentViewChildrenStateUpdated(Tab tab) {}
+
+    @Override
+    public void onContentViewSystemUiVisibilityChanged(Tab tab, int visibility) {}
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index b9ae39ffc..a71e840 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -42,7 +42,6 @@
 import org.chromium.chrome.browser.content.ContentUtils;
 import org.chromium.chrome.browser.contextmenu.ContextMenuPopulator;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
-import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenOptions;
 import org.chromium.chrome.browser.native_page.FrozenNativePage;
 import org.chromium.chrome.browser.native_page.NativePage;
@@ -251,8 +250,6 @@
      */
     private boolean mIsNativePageCommitPending;
 
-    private FullscreenManager mFullscreenManager;
-
     private TabDelegateFactory mDelegateFactory;
 
     /** Listens for views related to the tab to be attached or detached. */
@@ -790,11 +787,6 @@
 
             if (getWebContents() != null) getWebContents().onHide();
 
-            // Clean up any fullscreen state that might impact other tabs.
-            if (mFullscreenManager != null) {
-                mFullscreenManager.exitPersistentFullscreenMode();
-            }
-
             // Allow this tab's NativePage to be frozen if it stays hidden for a while.
             NativePageAssassin.getInstance().tabHidden(this);
 
@@ -1019,8 +1011,6 @@
         assert isDetached();
         updateWindowAndroid(activity.getWindowAndroid());
 
-        // Update for the controllers that need the Compositor from the new Activity.
-        mFullscreenManager = activity.getFullscreenManager();
         // Update the delegate factory, then recreate and propagate all delegates.
         mDelegateFactory = tabDelegateFactory;
         mWebContentsDelegate = mDelegateFactory.createWebContentsDelegate(this);
@@ -1792,33 +1782,21 @@
     }
 
     /**
-     * @return An instance of a {@link FullscreenManager}.
-     */
-    public FullscreenManager getFullscreenManager() {
-        return mFullscreenManager;
-    }
-
-    /**
      * Enters fullscreen mode. If enabling fullscreen while the tab is not interactable, fullscreen
      * will be delayed until the tab is interactable.
      * @param options Options to adjust fullscreen mode.
      */
     public void enterFullscreenMode(FullscreenOptions options) {
         RewindableIterator<TabObserver> observers = getTabObservers();
-        while (observers.hasNext()) {
-            observers.next().onEnterFullscreenMode(this, options);
-        }
+        while (observers.hasNext()) observers.next().onEnterFullscreenMode(this, options);
     }
 
     /**
-     * Exits fullscreen mode. If enabling fullscreen while the tab is not interactable, fullscreen
-     * will be delayed until the tab is interactable.
+     * Exits fullscreen mode.
      */
     public void exitFullscreenMode() {
         RewindableIterator<TabObserver> observers = getTabObservers();
-        while (observers.hasNext()) {
-            observers.next().onExitFullscreenMode(this);
-        }
+        while (observers.hasNext()) observers.next().onExitFullscreenMode(this);
     }
 
     /**
@@ -1833,14 +1811,6 @@
     }
 
     /**
-     * @param manager The fullscreen manager that should be notified of changes to this tab (if
-     *                set to null, no more updates will come from this tab).
-     */
-    public void setFullscreenManager(FullscreenManager manager) {
-        mFullscreenManager = manager;
-    }
-
-    /**
      * Add a new navigation entry for the current URL and page title.
      */
     void pushNativePageStateToNavigationEntry() {
@@ -1851,25 +1821,28 @@
 
     @Override
     public void onChildViewRemoved(View parent, View child) {
-        FullscreenManager fullscreenManager = getFullscreenManager();
-        if (fullscreenManager != null) {
-            fullscreenManager.updateContentViewChildrenState();
-        }
+        // TODO(jinsukkim): Consider updating |ContentView| to allow multiple
+        //         OnHierarchyChangeListener and OnSystemUiVisibilityChangeListener
+        //         to be added to not allow FullscreenManager to get the contentview
+        //         and add its own observers as needed.
+        updateContentViewChildrenState();
     }
 
     @Override
     public void onChildViewAdded(View parent, View child) {
-        FullscreenManager fullscreenManager = getFullscreenManager();
-        if (fullscreenManager != null) {
-            fullscreenManager.updateContentViewChildrenState();
-        }
+        updateContentViewChildrenState();
+    }
+
+    private void updateContentViewChildrenState() {
+        RewindableIterator<TabObserver> observers = getTabObservers();
+        while (observers.hasNext()) observers.next().onContentViewChildrenStateUpdated(this);
     }
 
     @Override
     public void onSystemUiVisibilityChange(int visibility) {
-        FullscreenManager fullscreenManager = getFullscreenManager();
-        if (fullscreenManager != null) {
-            fullscreenManager.onContentViewSystemUiVisibilityChange(visibility);
+        RewindableIterator<TabObserver> observers = getTabObservers();
+        while (observers.hasNext()) {
+            observers.next().onContentViewSystemUiVisibilityChanged(this, visibility);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabAttributeKeys.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabAttributeKeys.java
index 3b0e896..5d4966d0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabAttributeKeys.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabAttributeKeys.java
@@ -28,4 +28,7 @@
      * Parent Tab Root Task Id. See ContextRecordTaskId (context_record_task_id.h) for definition
      */
     public static final String PARENT_TAB_ROOT_TASK_ID = "ParentRootTaskId";
+
+    /** A runnable to delay the enabling of fullscreen mode if necessary. */
+    public static final String ENTER_FULLSCREEN = "EnterFullscreen";
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java
index cbe80a3..5b862ebf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java
@@ -101,9 +101,11 @@
 
     /**
      * @return Whether the browser controls are fully visible on screen.
+     * TODO(jinsukkim): Have clients listen to ChromFullscreenManager directly
+     *         and remove this method.
      */
     public boolean areBrowserControlsFullyVisible() {
-        final FullscreenManager manager = mTab.getFullscreenManager();
+        final FullscreenManager manager = FullscreenManager.from(mTab);
         return Float.compare(0f, manager.getBrowserControlHiddenRatio()) == 0;
     }
 
@@ -137,7 +139,7 @@
         mPreviousContentOffsetY = contentOffsetY;
         mAreOffsetsInitialized = true;
 
-        if (mTab.getFullscreenManager() == null) return;
+        if (FullscreenManager.from(mTab) == null) return;
         if (SadTab.isShowing(mTab) || mTab.isNativePage()) {
             showAndroidControls(false);
         } else {
@@ -152,13 +154,13 @@
      * @param animate Whether a slide-in animation should be run.
      */
     public void showAndroidControls(boolean animate) {
-        if (mTab.getFullscreenManager() == null) return;
+        FullscreenManager manager = FullscreenManager.from(mTab);
+        if (manager == null) return;
 
         if (animate) {
             runBrowserDrivenShowAnimation();
         } else {
-            updateFullscreenManagerOffsets(
-                    true, 0, 0, mTab.getFullscreenManager().getContentOffset());
+            updateFullscreenManagerOffsets(true, 0, 0, manager.getContentOffset());
         }
     }
 
@@ -167,7 +169,7 @@
      */
     public void resetPositions() {
         resetControlsOffsetOverridden();
-        if (mTab.getFullscreenManager() == null) return;
+        if (FullscreenManager.from(mTab) == null) return;
 
         // Make sure the dominant control offsets have been set.
         if (mAreOffsetsInitialized) {
@@ -176,7 +178,7 @@
         } else {
             showAndroidControls(false);
         }
-        TabFullscreenHandler.updateEnabledState(mTab);
+        TabBrowserControlsState.updateEnabledState(mTab);
     }
 
     /**
@@ -194,7 +196,7 @@
      */
     private void updateFullscreenManagerOffsets(boolean toNonFullscreen, int topControlsOffset,
             int bottomControlsOffset, int topContentOffset) {
-        final FullscreenManager manager = mTab.getFullscreenManager();
+        final FullscreenManager manager = FullscreenManager.from(mTab);
         if (manager == null) return;
 
         if (mIsInVr) {
@@ -238,7 +240,7 @@
 
         mIsControlsOffsetOverridden = true;
 
-        final FullscreenManager manager = mTab.getFullscreenManager();
+        final FullscreenManager manager = FullscreenManager.from(mTab);
         final float hiddenRatio = manager.getBrowserControlHiddenRatio();
         final int topControlHeight = manager.getTopControlsHeight();
         final int topControlOffset = manager.getTopControlOffset();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsState.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsState.java
index 1bffcd3..6210f6a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsState.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsState.java
@@ -5,7 +5,10 @@
 package org.chromium.chrome.browser.tab;
 
 import org.chromium.base.ObserverList.RewindableIterator;
-import org.chromium.base.UserData;
+import org.chromium.chrome.browser.fullscreen.FullscreenManager;
+import org.chromium.content_public.browser.GestureListenerManager;
+import org.chromium.content_public.browser.ImeAdapter;
+import org.chromium.content_public.browser.ImeEventObserver;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.common.BrowserControlsState;
 
@@ -13,7 +16,7 @@
  * Manages the state of tab browser controls. Instantiation is done by
  * {@link TabDelegateFactory#createBrowserControlsState()}.
  */
-public class TabBrowserControlsState implements UserData {
+public class TabBrowserControlsState extends TabWebContentsUserData implements ImeEventObserver {
     private static final Class<TabBrowserControlsState> USER_DATA_KEY =
             TabBrowserControlsState.class;
 
@@ -46,6 +49,15 @@
     }
 
     /**
+     * Push state about whether or not the browser controls can show or hide to the renderer.
+     * @param tab Tab object.
+     */
+    public static void updateEnabledState(Tab tab) {
+        if (tab == null || get(tab) == null) return;
+        get(tab).updateEnabledState();
+    }
+
+    /**
      * Updates the browser controls state for this tab.  As these values are set at the renderer
      * level, there is potential for this impacting other tabs that might share the same
      * process.
@@ -63,17 +75,79 @@
 
     /** Constructor */
     private TabBrowserControlsState(Tab tab, BrowserControlsVisibilityDelegate delegate) {
+        super(tab);
         mTab = tab;
         mVisibilityDelegate = delegate;
         mNativeTabBrowserControlsState = nativeInit();
+        mTab.addObserver(new EmptyTabObserver() {
+            @Override
+            public void onSSLStateUpdated(Tab tab) {
+                updateEnabledState();
+            }
+
+            @Override
+            public void onRendererResponsiveStateChanged(Tab tab, boolean isResponsive) {
+                if (FullscreenManager.from(mTab) == null) return;
+                if (isResponsive) {
+                    updateEnabledState();
+                } else {
+                    update(BrowserControlsState.SHOWN, false);
+                }
+            }
+
+            @Override
+            public void onPageLoadFinished(Tab tab, String url) {
+                updateEnabledState();
+            }
+
+            @Override
+            public void onDestroyed(Tab tab) {
+                tab.removeObserver(this);
+            }
+        });
     }
 
     @Override
-    public void destroy() {
+    public void destroyInternal() {
         nativeOnDestroyed(mNativeTabBrowserControlsState);
     }
 
-    private void update(int current, boolean animate) {
+    @Override
+    public void initWebContents(WebContents webContents) {
+        ImeAdapter.fromWebContents(webContents).addEventObserver(this);
+    }
+
+    @Override
+    public void cleanupWebContents(WebContents webContents) {}
+
+    private void updateEnabledState() {
+        if (mTab.isFrozen()) return;
+
+        update(BrowserControlsState.BOTH, getConstraints() != BrowserControlsState.HIDDEN);
+
+        WebContents webContents = mTab.getWebContents();
+        if (webContents != null) {
+            GestureListenerManager gestureManager =
+                    GestureListenerManager.fromWebContents(webContents);
+            FullscreenManager fullscreenManager = FullscreenManager.from(mTab);
+            if (gestureManager != null && fullscreenManager != null) {
+                gestureManager.updateMultiTouchZoomSupport(
+                        !fullscreenManager.getPersistentFullscreenMode());
+            }
+        }
+    }
+
+    /**
+     * Updates the browser controls state for this tab.  As these values are set at the renderer
+     * level, there is potential for this impacting other tabs that might share the same
+     * process.
+     *
+     * @param current The desired current state for the controls.  Pass
+     *                {@link BrowserControlsState#BOTH} to preserve the current position.
+     * @param animate Whether the controls should animate to the specified ending condition or
+     *                should jump immediately.
+     */
+    public void update(int current, boolean animate) {
         int constraints = getConstraints();
 
         // Do nothing if current and constraints conflict to avoid error in renderer.
@@ -120,6 +194,14 @@
         return constraints;
     }
 
+    // ImeEventObserver
+
+    @Override
+    public void onNodeAttributeUpdated(boolean editable, boolean password) {
+        if (FullscreenManager.from(mTab) == null) return;
+        updateEnabledState();
+    }
+
     private native long nativeInit();
     private native void nativeOnDestroyed(long nativeTabBrowserControlsState);
     private native void nativeUpdateState(long nativeTabBrowserControlsState,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabFullscreenHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabFullscreenHandler.java
deleted file mode 100644
index b7c585e4..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabFullscreenHandler.java
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.tab;
-
-import org.chromium.chrome.browser.fullscreen.FullscreenManager;
-import org.chromium.chrome.browser.fullscreen.FullscreenOptions;
-import org.chromium.content_public.browser.GestureListenerManager;
-import org.chromium.content_public.browser.ImeAdapter;
-import org.chromium.content_public.browser.ImeEventObserver;
-import org.chromium.content_public.browser.SelectionPopupController;
-import org.chromium.content_public.browser.WebContents;
-import org.chromium.content_public.common.BrowserControlsState;
-
-/**
- * {@link TabObserver} for basic fullscreen operations for {@link Tab}.
- */
-public final class TabFullscreenHandler extends TabWebContentsUserData implements ImeEventObserver {
-    private static final Class<TabFullscreenHandler> USER_DATA_KEY = TabFullscreenHandler.class;
-
-    private final Tab mTab;
-
-    /** A runnable to delay the enabling of fullscreen mode if necessary. */
-    private Runnable mEnterFullscreenRunnable;
-
-    public static void createForTab(Tab tab) {
-        assert get(tab) == null;
-        tab.getUserDataHost().setUserData(USER_DATA_KEY, new TabFullscreenHandler(tab));
-    }
-
-    /**
-     * Push state about whether or not the browser controls can show or hide to the renderer.
-     */
-    public static void updateEnabledState(Tab tab) {
-        if (tab == null) return;
-        TabFullscreenHandler.get(tab).updateEnabledState();
-    }
-
-    private TabFullscreenHandler(Tab tab) {
-        super(tab);
-        mTab = tab;
-        mTab.addObserver(new EmptyTabObserver() {
-            @Override
-            public void onSSLStateUpdated(Tab tab) {
-                updateEnabledState();
-            }
-
-            @Override
-            public void onInteractabilityChanged(boolean interactable) {
-                if (interactable && mEnterFullscreenRunnable != null)
-                    mEnterFullscreenRunnable.run();
-            }
-
-            @Override
-            public void onEnterFullscreenMode(Tab tab, final FullscreenOptions options) {
-                if (!tab.isUserInteractable()) {
-                    mEnterFullscreenRunnable = new Runnable() {
-                        @Override
-                        public void run() {
-                            enterFullscreenInternal(tab, options);
-                            mEnterFullscreenRunnable = null;
-                        }
-                    };
-                    return;
-                }
-
-                enterFullscreenInternal(tab, options);
-            }
-
-            @Override
-            public void onExitFullscreenMode(Tab tab) {
-                if (mEnterFullscreenRunnable != null) {
-                    mEnterFullscreenRunnable = null;
-                    return;
-                }
-
-                if (tab.getFullscreenManager() != null) {
-                    tab.getFullscreenManager().exitPersistentFullscreenMode();
-                }
-            }
-
-            /**
-             * Do the actual enter of fullscreen mode.
-             * @param options Options adjust fullscreen mode.
-             */
-            private void enterFullscreenInternal(Tab tab, FullscreenOptions options) {
-                if (tab.getFullscreenManager() != null) {
-                    tab.getFullscreenManager().enterPersistentFullscreenMode(options);
-                }
-
-                WebContents webContents = tab.getWebContents();
-                if (webContents != null) {
-                    SelectionPopupController.fromWebContents(webContents).destroySelectActionMode();
-                }
-            }
-
-            @Override
-            public void onRendererResponsiveStateChanged(Tab tab, boolean isResponsive) {
-                if (tab.getFullscreenManager() == null) return;
-                if (isResponsive) {
-                    updateEnabledState();
-                } else {
-                    TabBrowserControlsState.update(mTab, BrowserControlsState.SHOWN, false);
-                }
-            }
-
-            @Override
-            public void onPageLoadFinished(Tab tab, String url) {
-                updateEnabledState();
-            }
-
-            @Override
-            public void onDestroyed(Tab tab) {
-                tab.removeObserver(this);
-            }
-        });
-    }
-
-    static TabFullscreenHandler get(Tab tab) {
-        return tab.getUserDataHost().getUserData(USER_DATA_KEY);
-    }
-
-    @Override
-    public void initWebContents(WebContents webContents) {
-        ImeAdapter.fromWebContents(webContents).addEventObserver(this);
-    }
-
-    @Override
-    public void cleanupWebContents(WebContents webContents) {}
-
-    // ImeEventObserver
-
-    @Override
-    public void onNodeAttributeUpdated(boolean editable, boolean password) {
-        if (mTab.getFullscreenManager() == null) return;
-        updateEnabledState();
-    }
-
-    private void updateEnabledState() {
-        if (mTab.isFrozen()) return;
-
-        int current = TabBrowserControlsState.getConstraints(mTab);
-        TabBrowserControlsState.update(
-                mTab, BrowserControlsState.BOTH, current != BrowserControlsState.HIDDEN);
-
-        WebContents webContents = mTab.getWebContents();
-        if (webContents != null) {
-            GestureListenerManager gestureManager =
-                    GestureListenerManager.fromWebContents(webContents);
-            FullscreenManager fullscreenManager = mTab.getFullscreenManager();
-            if (gestureManager != null && fullscreenManager != null) {
-                gestureManager.updateMultiTouchZoomSupport(
-                        !fullscreenManager.getPersistentFullscreenMode());
-            }
-        }
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabGestureStateListener.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabGestureStateListener.java
index f6485c6..2557f29 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabGestureStateListener.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabGestureStateListener.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.tab;
 
-import org.chromium.base.Supplier;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.content_public.browser.GestureListenerManager;
 import org.chromium.content_public.browser.GestureStateListener;
@@ -19,25 +18,24 @@
     private static final Class<TabGestureStateListener> USER_DATA_KEY =
             TabGestureStateListener.class;
 
+    private final Tab mTab;
     private GestureStateListener mGestureListener;
-    private Supplier<FullscreenManager> mFullscreenManager;
 
     /**
      * Creates TabGestureStateListener and lets the WebContentsUserData of the Tab manage it.
      * @param tab Tab instance that the active WebContents instance gets loaded in.
      */
-    public static TabGestureStateListener from(Tab tab, Supplier<FullscreenManager> fullscreen) {
+    public static TabGestureStateListener from(Tab tab) {
         TabGestureStateListener listener = tab.getUserDataHost().getUserData(USER_DATA_KEY);
         if (listener == null) {
-            tab.getUserDataHost().setUserData(
-                    USER_DATA_KEY, new TabGestureStateListener(tab, fullscreen));
+            tab.getUserDataHost().setUserData(USER_DATA_KEY, new TabGestureStateListener(tab));
         }
         return listener;
     }
 
-    private TabGestureStateListener(Tab tab, Supplier<FullscreenManager> fullscreenManager) {
+    private TabGestureStateListener(Tab tab) {
         super(tab);
-        mFullscreenManager = fullscreenManager;
+        mTab = tab;
     }
 
     @Override
@@ -65,7 +63,7 @@
             }
 
             private void onScrollingStateChanged() {
-                FullscreenManager fullscreenManager = mFullscreenManager.get();
+                FullscreenManager fullscreenManager = FullscreenManager.from(mTab);
                 if (fullscreenManager == null) return;
                 fullscreenManager.onContentViewScrollingStateChanged(isScrollInProgress());
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java
index bd2429f..56a170b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java
@@ -33,7 +33,6 @@
     static void initTabHelpers(Tab tab, Tab parentTab, @TabCreationState Integer creationState) {
         if (creationState != null) TabUma.create(tab, creationState);
         TabThemeColorHelper.createForTab(tab);
-        TabFullscreenHandler.createForTab(tab);
         InterceptNavigationDelegateImpl.createForTab(tab);
         ContextualSearchTabHelper.createForTab(tab);
         if (ChromeFeatureList.isInitialized()
@@ -59,7 +58,7 @@
         InfoBarContainer.from(tab);
 
         TabWebContentsObserver.from(tab);
-        TabGestureStateListener.from(tab, tab::getFullscreenManager);
+        TabGestureStateListener.from(tab);
         SwipeRefreshHandler.from(tab);
         TabFavicon.from(tab);
         TrustedCdn.from(tab);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java
index 844dceb..0bb49c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java
@@ -354,4 +354,16 @@
      * @param result Detail information on the matched rects.
      */
     void onFindMatchRectsAvailable(FindMatchRectsDetails result);
+
+    /**
+     * Invoked when a child view is added or removed to Tab's content view.
+     */
+    void onContentViewChildrenStateUpdated(Tab tab);
+
+    /**
+     * Invoked when the status bar changes visibility.
+     * @param visibility Flags indicating the global state of the UI visibility.
+     * @see View#setSystemUiVisibility(int)
+     */
+    void onContentViewSystemUiVisibilityChanged(Tab tab, int visibility);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
index a0fb17e4..243e5d9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
@@ -11,6 +11,7 @@
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.device.DeviceClassManager;
+import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.modaldialog.TabModalPresenter;
 import org.chromium.chrome.browser.tab.Tab.TabHidingType;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
@@ -63,7 +64,7 @@
                 if (!mIsFullscreenWaitingForLoad) return;
 
                 mIsFullscreenWaitingForLoad = false;
-                TabFullscreenHandler.updateEnabledState(mTab);
+                TabBrowserControlsState.updateEnabledState(mTab);
             }
 
             private void cancelEnableFullscreenLoadDelay() {
@@ -94,13 +95,13 @@
                 mHandler.removeMessages(MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD);
                 mHandler.sendEmptyMessageDelayed(
                         MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD, getLoadDelayMs());
-                TabFullscreenHandler.updateEnabledState(mTab);
+                TabBrowserControlsState.updateEnabledState(mTab);
             }
 
             @Override
             public void onPageLoadStarted(Tab tab, String url) {
                 mIsFullscreenWaitingForLoad = !DomDistillerUrlUtils.isDistilledPage(url);
-                TabFullscreenHandler.updateEnabledState(mTab);
+                TabBrowserControlsState.updateEnabledState(mTab);
             }
 
             @Override
@@ -108,13 +109,13 @@
                 // Handle the case where a commit or prerender swap notification failed to arrive
                 // and the enable fullscreen message was never enqueued.
                 scheduleEnableFullscreenLoadDelayIfNecessary();
-                TabFullscreenHandler.updateEnabledState(mTab); ///?
+                TabBrowserControlsState.updateEnabledState(mTab);
             }
 
             @Override
             public void onPageLoadFailed(Tab tab, int errorCode) {
                 cancelEnableFullscreenLoadDelay();
-                TabFullscreenHandler.updateEnabledState(mTab);
+                TabBrowserControlsState.updateEnabledState(mTab);
             }
 
             @Override
@@ -158,7 +159,7 @@
         enableHidingBrowserControls &= !mTab.isShowingErrorPage();
         enableHidingBrowserControls &= !webContents.isShowingInterstitialPage();
         enableHidingBrowserControls &= !mTab.isRendererUnresponsive();
-        enableHidingBrowserControls &= (mTab.getFullscreenManager() != null);
+        enableHidingBrowserControls &= (FullscreenManager.from(mTab) != null);
         enableHidingBrowserControls &= DeviceClassManager.enableFullscreen();
         enableHidingBrowserControls &= !mIsFullscreenWaitingForLoad;
         enableHidingBrowserControls &= !TabModalPresenter.isDialogShowing(mTab);
@@ -168,8 +169,8 @@
 
     @Override
     public boolean canShowBrowserControls() {
-        if (mTab.getFullscreenManager() == null) return true;
-        return !mTab.getFullscreenManager().getPersistentFullscreenMode();
+        FullscreenManager manager = FullscreenManager.from(mTab);
+        return manager != null ? !manager.getPersistentFullscreenMode() : true;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java
index 29b5f8b..b93bbe9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java
@@ -34,6 +34,7 @@
 import org.chromium.chrome.browser.findinpage.FindMatchRectsDetails;
 import org.chromium.chrome.browser.findinpage.FindNotificationDetails;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
+import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenOptions;
 import org.chromium.chrome.browser.media.MediaCaptureNotificationService;
 import org.chromium.chrome.browser.policy.PolicyAuditor;
@@ -250,8 +251,8 @@
 
     @Override
     public boolean isFullscreenForTabOrPending() {
-        return mTab.getFullscreenManager() == null
-                ? false : mTab.getFullscreenManager().getPersistentFullscreenMode();
+        FullscreenManager manager = FullscreenManager.from(mTab);
+        return manager != null ? manager.getPersistentFullscreenMode() : false;
     }
 
     protected TabModel getTabModel() {
@@ -472,30 +473,22 @@
         mTab.getActivity().setOverlayMode(useOverlayMode);
     }
 
-    private ChromeFullscreenManager getFullscreenManager() {
-        // Following get* methods use this method instead of |Tab.getFullscreenManager|
-        // because the latter can return null if invoked while the tab is in detached state.
-        ChromeActivity activity = mTab.getActivity();
-        return activity != null && !activity.isActivityFinishingOrDestroyed() ?
-                activity.getFullscreenManager() : null;
-    }
-
     @Override
     public int getTopControlsHeight() {
-        ChromeFullscreenManager manager = getFullscreenManager();
+        FullscreenManager manager = FullscreenManager.from(mTab);
         return manager != null ? manager.getTopControlsHeight() : 0;
     }
 
     @Override
     public int getBottomControlsHeight() {
-        ChromeFullscreenManager manager = getFullscreenManager();
+        FullscreenManager manager = FullscreenManager.from(mTab);
         return manager != null ? manager.getBottomControlsHeight() : 0;
     }
 
     @Override
     public boolean controlsResizeView() {
-        ChromeFullscreenManager manager = getFullscreenManager();
-        return manager != null ? manager.controlsResizeView() : false;
+        FullscreenManager manager = FullscreenManager.from(mTab);
+        return manager != null ? ((ChromeFullscreenManager) manager).controlsResizeView() : false;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
index 8d3e810..ccf96a6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
@@ -17,7 +17,6 @@
 import org.chromium.chrome.browser.AppHooks;
 import org.chromium.chrome.browser.SwipeRefreshHandler;
 import org.chromium.chrome.browser.display_cutout.DisplayCutoutController;
-import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.infobar.InfoBarContainer;
 import org.chromium.chrome.browser.media.MediaCaptureNotificationService;
 import org.chromium.chrome.browser.policy.PolicyAuditor;
@@ -264,12 +263,6 @@
                 }
             }
 
-            FullscreenManager fullscreenManager = mTab.getFullscreenManager();
-            if (navigation.isInMainFrame() && !navigation.isSameDocument()
-                    && fullscreenManager != null) {
-                fullscreenManager.exitPersistentFullscreenMode();
-            }
-
             if (navigation.isInMainFrame()) {
                 // Stop swipe-to-refresh animation.
                 SwipeRefreshHandler handler = SwipeRefreshHandler.get(mTab);
@@ -302,7 +295,7 @@
                 observers.next().onDidAttachInterstitialPage(mTab);
             }
             mTab.notifyLoadProgress(mTab.getProgress());
-            TabFullscreenHandler.updateEnabledState(mTab);
+            TabBrowserControlsState.updateEnabledState(mTab);
             PolicyAuditor auditor = AppHooks.get().getPolicyAuditor();
             auditor.notifyCertificateFailure(
                     PolicyAuditor.nativeGetCertificateFailure(mTab.getWebContents()),
@@ -318,7 +311,7 @@
                 observers.next().onDidDetachInterstitialPage(mTab);
             }
             mTab.notifyLoadProgress(mTab.getProgress());
-            TabFullscreenHandler.updateEnabledState(mTab);
+            TabBrowserControlsState.updateEnabledState(mTab);
             if (!mTab.maybeShowNativePage(mTab.getUrl(), false)) {
                 mTab.showRenderedPage();
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/StorageDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/StorageDelegate.java
index 1dedba70..f2fd0a4a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/StorageDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/StorageDelegate.java
@@ -8,6 +8,7 @@
 import android.util.SparseArray;
 
 import org.chromium.base.ContextUtils;
+import org.chromium.base.FileUtils;
 import org.chromium.base.Log;
 import org.chromium.base.StreamUtil;
 import org.chromium.base.task.AsyncTask;
@@ -19,7 +20,6 @@
 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelInfo.DocumentEntry;
 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelInfo.DocumentList;
 
-import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -40,9 +40,6 @@
     /** Directory to store TabState files in. */
     private static final String STATE_DIRECTORY = "ChromeDocumentActivity";
 
-    /** The buffer size to use when reading the DocumentTabModel file, set to 4k bytes. */
-    private static final int BUF_SIZE = 0x1000;
-
     /** Cached base state directory to prevent main-thread filesystem access in getStateDirectory().
      */
     private static AsyncTask<File> sBaseStateDirectoryFetchTask;
@@ -69,15 +66,7 @@
         try {
             String filename = getFilename(encrypted);
             streamIn = ContextUtils.getApplicationContext().openFileInput(filename);
-
-            // Read the file from the file into the out stream.
-            ByteArrayOutputStream streamOut = new ByteArrayOutputStream();
-            byte[] buf = new byte[BUF_SIZE];
-            int r;
-            while ((r = streamIn.read(buf)) != -1) {
-                streamOut.write(buf, 0, r);
-            }
-            bytes = streamOut.toByteArray();
+            bytes = FileUtils.readStream(streamIn);
         } catch (FileNotFoundException e) {
             Log.e(TAG, "DocumentTabModel file not found.");
         } catch (IOException e) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtil.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtil.java
index 15b2a1c6..e8bad23 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtil.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtil.java
@@ -17,12 +17,12 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ContextUtils;
+import org.chromium.base.FileUtils;
 import org.chromium.base.StrictModeContext;
 import org.chromium.chrome.browser.util.IntentUtils;
 import org.chromium.net.MimeTypeFilter;
 import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -87,14 +87,7 @@
         try (InputStream inputStream =
                         ContextUtils.getApplicationContext().getContentResolver().openInputStream(
                                 uri)) {
-            ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
-            byte[] buffer = new byte[1024];
-
-            int len;
-            while ((len = inputStream.read(buffer)) != -1) {
-                byteBuffer.write(buffer, 0, len);
-            }
-            return byteBuffer.toByteArray();
+            return FileUtils.readStream(inputStream);
         } catch (IOException e) {
             return null;
         }
diff --git a/chrome/app_shim/DEPS b/chrome/app_shim/DEPS
index 37a1d69..54b4483 100644
--- a/chrome/app_shim/DEPS
+++ b/chrome/app_shim/DEPS
@@ -3,7 +3,7 @@
   "+chrome/browser/ui/cocoa",
   "+chrome/installer/launcher_support",
   "+components/crash/content/app",
-  "+components/remote_cocoa/common",
+  "+components/remote_cocoa",
   "+content/public/browser",
   "+mojo/core/embedder",
 ]
diff --git a/chrome/app_shim/app_shim_controller.mm b/chrome/app_shim/app_shim_controller.mm
index d117cde..4512ad1 100644
--- a/chrome/app_shim/app_shim_controller.mm
+++ b/chrome/app_shim/app_shim_controller.mm
@@ -25,6 +25,8 @@
 #include "chrome/browser/ui/cocoa/main_menu_builder.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/mac/app_mode_common.h"
+#include "components/remote_cocoa/app_shim/bridge_factory_impl.h"
+#include "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #include "components/remote_cocoa/common/bridge_factory.mojom.h"
 #include "content/public/browser/ns_view_bridge_factory_impl.h"
 #include "content/public/common/ns_view_bridge_factory.mojom.h"
@@ -34,8 +36,6 @@
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/views_bridge_mac/bridge_factory_impl.h"
-#include "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 namespace {
 // The maximum amount of time to wait for Chrome's AppShimHostManager to be
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 95fe0ae..9757365 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -9,8 +9,8 @@
 #include <set>
 #include <utility>
 
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/callback.h"
@@ -1431,12 +1431,6 @@
     {"enable-virtual-keyboard", flag_descriptions::kVirtualKeyboardName,
      flag_descriptions::kVirtualKeyboardDescription, kOsCrOS,
      SINGLE_VALUE_TYPE(keyboard::switches::kEnableVirtualKeyboard)},
-    {"enable-physical-keyboard-autocorrect",
-     flag_descriptions::kPhysicalKeyboardAutocorrectName,
-     flag_descriptions::kPhysicalKeyboardAutocorrectDescription, kOsCrOS,
-     ENABLE_DISABLE_VALUE_TYPE(
-         chromeos::switches::kEnablePhysicalKeyboardAutocorrect,
-         chromeos::switches::kDisablePhysicalKeyboardAutocorrect)},
 #endif  // OS_CHROMEOS
 #if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
     {"device-discovery-notifications",
diff --git a/chrome/browser/android/history_report/delta_file_service.cc b/chrome/browser/android/history_report/delta_file_service.cc
index ce51a73..44001b9 100644
--- a/chrome/browser/android/history_report/delta_file_service.cc
+++ b/chrome/browser/android/history_report/delta_file_service.cc
@@ -89,9 +89,9 @@
 
 DeltaFileService::~DeltaFileService() {
   // Unregister should happen on task runner.
-  task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&DeltaFileServiceDoUnregisterMDP,
-                                base::Passed(std::move(delta_file_backend_))));
+  task_runner_->PostTask(FROM_HERE,
+                         base::BindOnce(&DeltaFileServiceDoUnregisterMDP,
+                                        std::move(delta_file_backend_)));
 }
 
 void DeltaFileService::PageAdded(const GURL& url) {
diff --git a/chrome/browser/android/history_report/usage_reports_buffer_service.cc b/chrome/browser/android/history_report/usage_reports_buffer_service.cc
index 111442c6..6c36117 100644
--- a/chrome/browser/android/history_report/usage_reports_buffer_service.cc
+++ b/chrome/browser/android/history_report/usage_reports_buffer_service.cc
@@ -85,7 +85,7 @@
   // Unregister should happen on task runner.
   task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&UsageReportsBufferServiceDoUnregisterMDP,
-                                base::Passed(std::move(backend_))));
+                                std::move(backend_)));
 }
 
 void UsageReportsBufferService::Init() {
diff --git a/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc b/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
index 4530be3..031870cf 100644
--- a/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
+++ b/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/apps/intent_helper/page_transition_util.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/menu_manager.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -31,6 +32,16 @@
 
 namespace {
 
+// Returns true if |url| is a known and valid redirector that will redirect a
+// navigation elsewhere.
+bool IsGoogleRedirectorUrl(const GURL& url) {
+  // This currently only check for redirectors on the "google" domain.
+  if (!page_load_metrics::IsGoogleSearchHostname(url))
+    return false;
+
+  return url.path_piece() == "/url" && url.has_query();
+}
+
 // Compares the host name of the referrer and target URL to decide whether
 // the navigation needs to be overridden.
 bool ShouldOverrideUrlLoading(const GURL& previous_url,
@@ -54,6 +65,11 @@
     return false;
   }
 
+  // Skip URL redirectors that are intermediate pages redirecting towards a
+  // final URL.
+  if (IsGoogleRedirectorUrl(current_url))
+    return false;
+
   return true;
 }
 
@@ -162,6 +178,11 @@
 }
 
 // static
+bool AppsNavigationThrottle::IsGoogleRedirectorUrlForTesting(const GURL& url) {
+  return IsGoogleRedirectorUrl(url);
+}
+
+// static
 bool AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
     const GURL& previous_url,
     const GURL& current_url) {
@@ -413,6 +434,15 @@
 content::NavigationThrottle::ThrottleCheckResult
 AppsNavigationThrottle::HandleRequest() {
   content::NavigationHandle* handle = navigation_handle();
+  // If the navigation is from an iframe then no intent picker check is
+  // required. If the navigation happened without changing document or the
+  // navigation resulted in an error page, don't check intent for the
+  // navigation.
+  if (!handle->IsInMainFrame() || handle->IsSameDocument() ||
+      handle->IsErrorPage()) {
+    return content::NavigationThrottle::PROCEED;
+  }
+
   DCHECK(!ui_displayed_);
 
   navigate_from_link_ = false;
diff --git a/chrome/browser/apps/intent_helper/apps_navigation_throttle.h b/chrome/browser/apps/intent_helper/apps_navigation_throttle.h
index 9b3afd4..73c3dea 100644
--- a/chrome/browser/apps/intent_helper/apps_navigation_throttle.h
+++ b/chrome/browser/apps/intent_helper/apps_navigation_throttle.h
@@ -74,6 +74,8 @@
                         IntentPickerCloseReason close_reason,
                         bool should_persist);
 
+  static bool IsGoogleRedirectorUrlForTesting(const GURL& url);
+
   static bool ShouldOverrideUrlLoadingForTesting(const GURL& previous_url,
                                                  const GURL& current_url);
 
diff --git a/chrome/browser/apps/intent_helper/apps_navigation_throttle_unittest.cc b/chrome/browser/apps/intent_helper/apps_navigation_throttle_unittest.cc
index 150a969..6465caff3 100644
--- a/chrome/browser/apps/intent_helper/apps_navigation_throttle_unittest.cc
+++ b/chrome/browser/apps/intent_helper/apps_navigation_throttle_unittest.cc
@@ -9,6 +9,36 @@
 
 namespace apps {
 
+TEST(AppsNavigationThrottleTest, TestIsGoogleRedirectorUrl) {
+  // Test that redirect urls with different TLDs are still recognized.
+  EXPECT_TRUE(AppsNavigationThrottle::IsGoogleRedirectorUrlForTesting(
+      GURL("https://www.google.com.au/url?q=wathever")));
+  EXPECT_TRUE(AppsNavigationThrottle::IsGoogleRedirectorUrlForTesting(
+      GURL("https://www.google.com.mx/url?q=hotpot")));
+  EXPECT_TRUE(AppsNavigationThrottle::IsGoogleRedirectorUrlForTesting(
+      GURL("https://www.google.co/url?q=query")));
+
+  // Non-google domains shouldn't be used as valid redirect links.
+  EXPECT_FALSE(AppsNavigationThrottle::IsGoogleRedirectorUrlForTesting(
+      GURL("https://www.not-google.com/url?q=query")));
+  EXPECT_FALSE(AppsNavigationThrottle::IsGoogleRedirectorUrlForTesting(
+      GURL("https://www.gooogle.com/url?q=legit_query")));
+
+  // This method only takes "/url" as a valid path, it needs to contain a query,
+  // we don't analyze that query as it will expand later on in the same
+  // throttle.
+  EXPECT_TRUE(AppsNavigationThrottle::IsGoogleRedirectorUrlForTesting(
+      GURL("https://www.google.com/url?q=who_dis")));
+  EXPECT_TRUE(AppsNavigationThrottle::IsGoogleRedirectorUrlForTesting(
+      GURL("http://www.google.com/url?q=who_dis")));
+  EXPECT_FALSE(AppsNavigationThrottle::IsGoogleRedirectorUrlForTesting(
+      GURL("https://www.google.com/url")));
+  EXPECT_FALSE(AppsNavigationThrottle::IsGoogleRedirectorUrlForTesting(
+      GURL("https://www.google.com/link?q=query")));
+  EXPECT_FALSE(AppsNavigationThrottle::IsGoogleRedirectorUrlForTesting(
+      GURL("https://www.google.com/link")));
+}
+
 TEST(AppsNavigationThrottleTest, TestShouldOverrideUrlLoading) {
   // If either of two paramters is empty, the function should return false.
   EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
@@ -60,6 +90,17 @@
       GURL("chrome://fake_document"), GURL("https://www.a.com")));
   EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
       GURL("file://fake_document"), GURL("https://www.a.com")));
+
+  // A navigation going to a redirect url cannot be overridden, unless there's
+  // no query or the path is not valid.
+  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("http://www.google.com"), GURL("https://www.google.com/url?q=b")));
+  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("https://www.a.com"), GURL("https://www.google.com/url?q=a")));
+  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("https://www.a.com"), GURL("https://www.google.com/url")));
+  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("https://www.a.com"), GURL("https://www.google.com/link?q=a")));
 }
 
 TEST(AppsNavigationThrottleTest, TestGetPickerAction) {
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
index fd57bd8..b686009 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
@@ -7,8 +7,8 @@
 #include <algorithm>
 #include <utility>
 
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/public/cpp/ash_pref_names.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/memory/singleton.h"
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
index 86565988..b6c8216 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
@@ -9,8 +9,8 @@
 #include <utility>
 #include <vector>
 
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/public/cpp/ash_pref_names.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
diff --git a/chrome/browser/chromeos/extensions/default_keyboard_extension_browser_test.cc b/chrome/browser/chromeos/extensions/default_keyboard_extension_browser_test.cc
index 814fbc3..7d240e4 100644
--- a/chrome/browser/chromeos/extensions/default_keyboard_extension_browser_test.cc
+++ b/chrome/browser/chromeos/extensions/default_keyboard_extension_browser_test.cc
@@ -7,7 +7,7 @@
 
 #include <vector>
 
-#include "ash/keyboard/ui/public/keyboard_switches.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "base/command_line.h"
 #include "base/run_loop.h"
 #include "base/threading/thread_restrictions.h"
diff --git a/chrome/browser/chromeos/extensions/input_method_api.cc b/chrome/browser/chromeos/extensions/input_method_api.cc
index 8288f8f..a26aa86 100644
--- a/chrome/browser/chromeos/extensions/input_method_api.cc
+++ b/chrome/browser/chromeos/extensions/input_method_api.cc
@@ -12,7 +12,6 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/command_line.h"
 #include "base/lazy_instance.h"
 #include "base/values.h"
 #include "build/build_config.h"
@@ -30,7 +29,6 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/extensions/api/input_method_private.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/constants/chromeos_switches.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/sync/driver/sync_service.h"
@@ -86,10 +84,7 @@
   EXTENSION_FUNCTION_VALIDATE(false);
 #else
   std::unique_ptr<base::DictionaryValue> output(new base::DictionaryValue());
-  output->SetBoolean(
-      "isPhysicalKeyboardAutocorrectEnabled",
-      !base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kDisablePhysicalKeyboardAutocorrect));
+  output->SetBoolean("isPhysicalKeyboardAutocorrectEnabled", true);
   output->SetBoolean("isImeMenuActivated",
                      Profile::FromBrowserContext(browser_context())
                          ->GetPrefs()
diff --git a/chrome/browser/chromeos/file_manager/file_browser_handlers.cc b/chrome/browser/chromeos/file_manager/file_browser_handlers.cc
index 5735a7e..82f76188 100644
--- a/chrome/browser/chromeos/file_manager/file_browser_handlers.cc
+++ b/chrome/browser/chromeos/file_manager/file_browser_handlers.cc
@@ -346,9 +346,8 @@
         context_id,
         base::BindOnce(
             &FileBrowserHandlerExecutor::SetupPermissionsAndDispatchEvent,
-            weak_ptr_factory_.GetWeakPtr(),
-            base::Passed(std::move(file_definition_list)),
-            base::Passed(std::move(entry_definition_list)), handler_pid));
+            weak_ptr_factory_.GetWeakPtr(), std::move(file_definition_list),
+            std::move(entry_definition_list), handler_pid));
   }
 }
 
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index df130c4..d6560092 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -4,7 +4,7 @@
 
 #include <stddef.h>
 
-#include "ash/keyboard/ui/public/keyboard_switches.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_restrictions.h"
@@ -965,7 +965,8 @@
             .EnableMyFilesVolume()
             .DontMountVolumes(),
         TestCase("myFilesUpdatesChildren").EnableMyFilesVolume(),
-        TestCase("myFilesAutoExpandOnce").EnableMyFilesVolume()));
+        TestCase("myFilesAutoExpandOnce").EnableMyFilesVolume(),
+        TestCase("myFilesToolbarDelete").EnableMyFilesVolume()));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     InstallLinuxPackageDialog, /* install_linux_package_dialog.js */
diff --git a/chrome/browser/chromeos/login/kiosk_browsertest.cc b/chrome/browser/chromeos/login/kiosk_browsertest.cc
index 5e541a4..3efe605d 100644
--- a/chrome/browser/chromeos/login/kiosk_browsertest.cc
+++ b/chrome/browser/chromeos/login/kiosk_browsertest.cc
@@ -6,8 +6,8 @@
 #include <vector>
 
 #include "apps/test/app_window_waiter.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/public/cpp/ash_switches.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/interfaces/login_screen_test_api.test-mojom-test-utils.h"
 #include "ash/public/interfaces/wallpaper.mojom.h"
 #include "base/bind.h"
diff --git a/chrome/browser/chromeos/login/users/multi_profile_user_controller_unittest.cc b/chrome/browser/chromeos/login/users/multi_profile_user_controller_unittest.cc
index 372c4382..95f07879 100644
--- a/chrome/browser/chromeos/login/users/multi_profile_user_controller_unittest.cc
+++ b/chrome/browser/chromeos/login/users/multi_profile_user_controller_unittest.cc
@@ -134,7 +134,6 @@
   int VerifyInternal(net::X509Certificate* cert,
                      const std::string& hostname,
                      const std::string& ocsp_response,
-                     const std::string& sct_list,
                      int flags,
                      net::CRLSet* crl_set,
                      const net::CertificateList& additional_trust_anchors,
diff --git a/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc b/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc
index 1827b13..709d632 100644
--- a/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc
+++ b/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc
@@ -262,9 +262,8 @@
   int result = net::OK;
   content::BrowserContext::GetDefaultStoragePartition(profile)
       ->GetNetworkContext()
-      ->VerifyCertificateForTesting(certificate, "127.0.0.1",
-                                    /*ocsp_response=*/std::string(),
-                                    /*sct_list=*/std::string(), &result);
+      ->VerifyCertificateForTesting(certificate, "127.0.0.1", std::string(),
+                                    &result);
   return result;
 }
 
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc b/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
index de8bd9e2..d4dc455 100644
--- a/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
+++ b/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
@@ -9,7 +9,7 @@
 #include <memory>
 #include <utility>
 
-#include "ash/keyboard/ui/public/keyboard_config.mojom.h"
+#include "ash/public/interfaces/keyboard_config.mojom.h"
 #include "base/feature_list.h"
 #include "base/macros.h"
 #include "chrome/browser/chromeos/input_method/input_method_engine.h"
diff --git a/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc b/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc
index 09b9b975..b1d8957d 100644
--- a/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc
+++ b/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc
@@ -192,7 +192,6 @@
   const int flags = 0;
 
   std::string ocsp_response;
-  std::string sct_list;
   net::CertVerifyResult* const verify_result_ptr = verify_result.get();
 
   RequestState* request_state = new RequestState();
@@ -202,7 +201,7 @@
 
   const int return_value = verifier->Verify(
       net::CertVerifier::RequestParams(std::move(cert_chain), details.hostname,
-                                       flags, ocsp_response, sct_list),
+                                       flags, ocsp_response),
       verify_result_ptr, bound_callback, &request_state->request, *net_log);
 
   if (return_value != net::ERR_IO_PENDING) {
diff --git a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
index 0538bb1..0c1ea72 100644
--- a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
+++ b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
@@ -8,8 +8,8 @@
 #include <string>
 #include <utility>
 
-#include "ash/keyboard/ui/public/keyboard_controller_types.mojom.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
+#include "ash/public/interfaces/keyboard_controller_types.mojom.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 6ee95ef..c3d4b78 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1390,11 +1390,6 @@
     "expiry_milestone": -1
   },
   {
-    "name": "enable-physical-keyboard-autocorrect",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "enable-pixel-canvas-recording",
     "owners": [ "malaykeshav", "oshima" ],
     "expiry_milestone": 77
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index a3a28de..ecb094b 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3154,11 +3154,6 @@
 const char kNewZipUnpackerDescription[] =
     "Use the ZIP Archiver for mounting/unpacking ZIP files";
 
-const char kPhysicalKeyboardAutocorrectName[] = "Physical keyboard autocorrect";
-const char kPhysicalKeyboardAutocorrectDescription[] =
-    "Enable physical keyboard autocorrect for US keyboard, which can provide "
-    "suggestions as typing on physical keyboard.";
-
 const char kPrinterProviderSearchAppName[] =
     "Chrome Web Store Gallery app for printer drivers";
 const char kPrinterProviderSearchAppDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 036d5c5..804028f 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1903,9 +1903,6 @@
 extern const char kNewZipUnpackerName[];
 extern const char kNewZipUnpackerDescription[];
 
-extern const char kPhysicalKeyboardAutocorrectName[];
-extern const char kPhysicalKeyboardAutocorrectDescription[];
-
 extern const char kPrinterProviderSearchAppName[];
 extern const char kPrinterProviderSearchAppDescription[];
 
diff --git a/chrome/browser/geolocation/geolocation_browsertest.cc b/chrome/browser/geolocation/geolocation_browsertest.cc
index 85d89493..09b97310 100644
--- a/chrome/browser/geolocation/geolocation_browsertest.cc
+++ b/chrome/browser/geolocation/geolocation_browsertest.cc
@@ -39,7 +39,6 @@
 #include "content/public/test/browser_test_utils.h"
 #include "net/base/escape.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "net/url_request/test_url_fetcher_factory.h"
 #include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
 #include "services/device/public/mojom/geoposition.mojom.h"
 
@@ -172,39 +171,6 @@
   DISALLOW_COPY_AND_ASSIGN(PermissionRequestObserver);
 };
 
-// Observer that waits until a TestURLFetcher with the specified fetcher_id
-// starts, after which it is made available through .fetcher().
-class TestURLFetcherObserver : public net::TestURLFetcher::DelegateForTests {
- public:
-  explicit TestURLFetcherObserver(int expected_fetcher_id)
-      : expected_fetcher_id_(expected_fetcher_id) {
-    factory_.SetDelegateForTests(this);
-  }
-  virtual ~TestURLFetcherObserver() {}
-
-  void Wait() { loop_.Run(); }
-
-  net::TestURLFetcher* fetcher() { return fetcher_; }
-
-  // net::TestURLFetcher::DelegateForTests:
-  void OnRequestStart(int fetcher_id) override {
-    if (fetcher_id == expected_fetcher_id_) {
-      fetcher_ = factory_.GetFetcherByID(fetcher_id);
-      fetcher_->SetDelegateForTests(nullptr);
-      factory_.SetDelegateForTests(nullptr);
-      loop_.Quit();
-    }
-  }
-  void OnChunkUpload(int fetcher_id) override {}
-  void OnRequestEnd(int fetcher_id) override {}
-
- private:
-  const int expected_fetcher_id_;
-  net::TestURLFetcher* fetcher_ = nullptr;
-  net::TestURLFetcherFactory factory_;
-  base::RunLoop loop_;
-};
-
 }  // namespace
 
 
diff --git a/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc b/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc
index c0b2d8b0..dc05e5b 100644
--- a/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc
+++ b/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc
@@ -63,8 +63,10 @@
   child_process_data.metrics_name = kTestGpuProcessName;
 
   provider.BrowserChildProcessLaunchedAndConnected(child_process_data);
-  content::ChildProcessTerminationInfo abnormal_termination_info{
-      base::TERMINATION_STATUS_ABNORMAL_TERMINATION, 1};
+  content::ChildProcessTerminationInfo abnormal_termination_info;
+  abnormal_termination_info.status =
+      base::TERMINATION_STATUS_ABNORMAL_TERMINATION;
+  abnormal_termination_info.exit_code = 1;
   provider.BrowserChildProcessCrashed(child_process_data,
                                       abnormal_termination_info);
   provider.BrowserChildProcessCrashed(child_process_data,
@@ -94,8 +96,10 @@
 
   provider.BrowserChildProcessLaunchedAndConnected(child_process_data);
   const int kExitCode = 1;
-  content::ChildProcessTerminationInfo abnormal_termination_info{
-      base::TERMINATION_STATUS_ABNORMAL_TERMINATION, kExitCode};
+  content::ChildProcessTerminationInfo abnormal_termination_info;
+  abnormal_termination_info.status =
+      base::TERMINATION_STATUS_ABNORMAL_TERMINATION;
+  abnormal_termination_info.exit_code = kExitCode;
   provider.BrowserChildProcessCrashed(child_process_data,
                                       abnormal_termination_info);
   provider.BrowserChildProcessCrashed(child_process_data,
@@ -146,31 +150,35 @@
       rph_factory->CreateRenderProcessHost(profile, site_instance.get()));
 
   // Crash and abnormal termination should increment renderer crash count.
-  content::ChildProcessTerminationInfo crash_details{
-      base::TERMINATION_STATUS_PROCESS_CRASHED, 1};
+  content::ChildProcessTerminationInfo crash_details;
+  crash_details.status = base::TERMINATION_STATUS_PROCESS_CRASHED;
+  crash_details.exit_code = 1;
   provider.Observe(
       content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
       content::Source<content::RenderProcessHost>(host),
       content::Details<content::ChildProcessTerminationInfo>(&crash_details));
 
-  content::ChildProcessTerminationInfo term_details{
-      base::TERMINATION_STATUS_ABNORMAL_TERMINATION, 1};
+  content::ChildProcessTerminationInfo term_details;
+  term_details.status = base::TERMINATION_STATUS_ABNORMAL_TERMINATION;
+  term_details.exit_code = 1;
   provider.Observe(
       content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
       content::Source<content::RenderProcessHost>(host),
       content::Details<content::ChildProcessTerminationInfo>(&term_details));
 
   // Kill does not increment renderer crash count.
-  content::ChildProcessTerminationInfo kill_details{
-      base::TERMINATION_STATUS_PROCESS_WAS_KILLED, 1};
+  content::ChildProcessTerminationInfo kill_details;
+  kill_details.status = base::TERMINATION_STATUS_PROCESS_WAS_KILLED;
+  kill_details.exit_code = 1;
   provider.Observe(
       content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
       content::Source<content::RenderProcessHost>(host),
       content::Details<content::ChildProcessTerminationInfo>(&kill_details));
 
   // Failed launch increments failed launch count.
-  content::ChildProcessTerminationInfo failed_launch_details{
-      base::TERMINATION_STATUS_LAUNCH_FAILED, 1};
+  content::ChildProcessTerminationInfo failed_launch_details;
+  failed_launch_details.status = base::TERMINATION_STATUS_LAUNCH_FAILED;
+  failed_launch_details.exit_code = 1;
   provider.Observe(content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
                    content::Source<content::RenderProcessHost>(host),
                    content::Details<content::ChildProcessTerminationInfo>(
diff --git a/chrome/browser/metrics/plugin_metrics_provider_unittest.cc b/chrome/browser/metrics/plugin_metrics_provider_unittest.cc
index 6475cbf..18702b4 100644
--- a/chrome/browser/metrics/plugin_metrics_provider_unittest.cc
+++ b/chrome/browser/metrics/plugin_metrics_provider_unittest.cc
@@ -168,8 +168,10 @@
 
   // Increase number of process launches which should also start a delayed
   // task.
-  content::ChildProcessTerminationInfo abnormal_termination_info{
-      base::TERMINATION_STATUS_ABNORMAL_TERMINATION, 1};
+  content::ChildProcessTerminationInfo abnormal_termination_info;
+  abnormal_termination_info.status =
+      base::TERMINATION_STATUS_ABNORMAL_TERMINATION;
+  abnormal_termination_info.exit_code = 1;
   content::ChildProcessData child_process_data1(
       content::PROCESS_TYPE_PPAPI_PLUGIN);
   child_process_data1.name = base::UTF8ToUTF16("p1");
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
index f32ff1d..bcd1b0f 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
@@ -2979,39 +2979,3 @@
   EXPECT_EQ(overlay_window->muted_state_for_testing(),
             OverlayWindowViews::MutedState::kNoAudio);
 }
-
-// Tests that when closing the window after the player was reset, the <video>
-// element is still notified.
-IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
-                       ResetPlayerCloseWindowNotifiesElement) {
-  LoadTabAndEnterPictureInPicture(browser());
-  content::WebContents* active_web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  // Video should be in Picture-in-Picture.
-  {
-    bool in_picture_in_picture = false;
-    ASSERT_TRUE(ExecuteScriptAndExtractBool(active_web_contents,
-                                            "isInPictureInPicture();",
-                                            &in_picture_in_picture));
-    EXPECT_TRUE(in_picture_in_picture);
-  }
-
-  // Reset video source and wait for the notification.
-  ASSERT_TRUE(content::ExecuteScript(active_web_contents, "resetVideo();"));
-  base::string16 expected_title = base::ASCIIToUTF16("emptied");
-  EXPECT_EQ(expected_title,
-            content::TitleWatcher(active_web_contents, expected_title)
-                .WaitAndGetTitle());
-
-  window_controller()->Close(true /* should_pause_video */);
-
-  // Video should no longer be in Picture-in-Picture.
-  {
-    bool in_picture_in_picture = false;
-    ASSERT_TRUE(ExecuteScriptAndExtractBool(active_web_contents,
-                                            "isInPictureInPicture();",
-                                            &in_picture_in_picture));
-    EXPECT_FALSE(in_picture_in_picture);
-  }
-}
diff --git a/chrome/browser/resources/downloads/BUILD.gn b/chrome/browser/resources/downloads/BUILD.gn
index ceb9f736..ff97df9 100644
--- a/chrome/browser/resources/downloads/BUILD.gn
+++ b/chrome/browser/resources/downloads/BUILD.gn
@@ -21,7 +21,9 @@
     excludes = [
       "chrome://resources/js/mojo_bindings_lite.js",
       "chrome://downloads/downloads.mojom-lite.js",
+      "chrome://downloads/downloads.mojom-lite.html",
     ]
+    replace_for_html_imports_polyfill = "crisper.js"
 
     deps = [
       ":unpak",
@@ -92,6 +94,7 @@
     ":manager",
     "//ui/webui/resources/js:cr",
   ]
+  externs_list = [ "$externs_path/html_imports.js" ]
 }
 
 js_library("icon_loader") {
diff --git a/chrome/browser/resources/downloads/browser_proxy.html b/chrome/browser/resources/downloads/browser_proxy.html
index 505910d7..610c6459 100644
--- a/chrome/browser/resources/downloads/browser_proxy.html
+++ b/chrome/browser/resources/downloads/browser_proxy.html
@@ -1,4 +1,4 @@
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.html">
-<script src="downloads.mojom-lite.js"></script>
+<link rel="import" href="chrome://downloads/downloads.mojom-lite.html">
 <script src="browser_proxy.js"></script>
diff --git a/chrome/browser/resources/downloads/downloads.html b/chrome/browser/resources/downloads/downloads.html
index dcb81ef..a177651 100644
--- a/chrome/browser/resources/downloads/downloads.html
+++ b/chrome/browser/resources/downloads/downloads.html
@@ -42,6 +42,8 @@
   <command id="clear-all-command" shortcut="Alt|c">
   <command id="undo-command" shortcut="Ctrl|z">
 </if>
+  <script src="chrome://resources/polymer/v1_0/html-imports/html-imports.min.js">
+  </script>
   <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
   <link rel="import" href="chrome://resources/html/cr.html">
   <link rel="import" href="chrome://resources/html/polymer.html">
diff --git a/chrome/browser/resources/downloads/downloads.js b/chrome/browser/resources/downloads/downloads.js
index fe3834c0..6bad17b 100644
--- a/chrome/browser/resources/downloads/downloads.js
+++ b/chrome/browser/resources/downloads/downloads.js
@@ -2,13 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-window.addEventListener('load', function() {
-  downloads.Manager.onLoad().then(function() {
-    requestIdleCallback(function() {
-      chrome.send(
-          'metricsHandler:recordTime',
-          ['Download.ResultsRenderedTime', window.performance.now()]);
-      document.fonts.load('bold 12px Roboto');
+function loadDownloads() {
+  return HTMLImports.whenReady(function() {
+    downloads.Manager.onLoad().then(function() {
+      requestIdleCallback(function() {
+        chrome.send(
+            'metricsHandler:recordTime',
+            ['Download.ResultsRenderedTime', window.performance.now()]);
+        document.fonts.load('bold 12px Roboto');
+      });
     });
   });
-});
+}
+
+if (document.readyState === 'complete') {
+  loadDownloads();
+} else {
+  window.addEventListener('load', loadDownloads);
+}
diff --git a/chrome/browser/resources/downloads/downloads.mojom-lite.html b/chrome/browser/resources/downloads/downloads.mojom-lite.html
new file mode 100644
index 0000000..5a695dbb
--- /dev/null
+++ b/chrome/browser/resources/downloads/downloads.mojom-lite.html
@@ -0,0 +1 @@
+<script src="downloads.mojom-lite.js"></script>
diff --git a/chrome/browser/resources/downloads/downloads_resources.grd b/chrome/browser/resources/downloads/downloads_resources.grd
index ba9c485..bbdb58c 100644
--- a/chrome/browser/resources/downloads/downloads_resources.grd
+++ b/chrome/browser/resources/downloads/downloads_resources.grd
@@ -18,6 +18,9 @@
       <include name="IDR_DOWNLOADS_IMAGES_NO_DOWNLOADS_SVG"
           file="images\no_downloads.svg"
           type="BINDATA" />
+      <include name="IDR_DOWNLOADS_MOJO_LITE_HTML"
+          file="downloads.mojom-lite.html"
+          type="BINDATA" />
       <include name="IDR_DOWNLOADS_MOJO_LITE_JS"
           file="${root_gen_dir}\chrome\browser\ui\webui\downloads\downloads.mojom-lite.js"
           use_base_dir="false"
diff --git a/chrome/browser/resources/downloads/downloads_resources_vulcanized.grd b/chrome/browser/resources/downloads/downloads_resources_vulcanized.grd
index 71179dc..05ad680b 100644
--- a/chrome/browser/resources/downloads/downloads_resources_vulcanized.grd
+++ b/chrome/browser/resources/downloads/downloads_resources_vulcanized.grd
@@ -18,6 +18,9 @@
       <include name="IDR_DOWNLOADS_IMAGES_NO_DOWNLOADS_SVG"
                file="images\no_downloads.svg"
                type="BINDATA" />
+      <include name="IDR_DOWNLOADS_MOJO_LITE_HTML"
+               file="downloads.mojom-lite.html"
+               type="BINDATA" />
       <include name="IDR_DOWNLOADS_MOJO_LITE_JS"
                file="${root_gen_dir}\chrome\browser\ui\webui\downloads\downloads.mojom-lite.js"
                use_base_dir="false"
diff --git a/chrome/browser/resources/extensions/shortcut_input.html b/chrome/browser/resources/extensions/shortcut_input.html
index e7c6b44..d35415d4 100644
--- a/chrome/browser/resources/extensions/shortcut_input.html
+++ b/chrome/browser/resources/extensions/shortcut_input.html
@@ -36,7 +36,8 @@
               '$i18nPolymer{shortcutTooManyModifiers}',
               '$i18nPolymer{shortcutNeedCharacter}')]]"
           value="[[computeText_(capturing_, shortcut, pendingShortcut_)]]">
-        <cr-icon-button id="clear" slot="suffix" class="icon-cancel no-overlap"
+        <cr-icon-button id="clear" aria-label="$i18nPolymer{clear}"
+            slot="suffix" class="icon-cancel no-overlap"
             hidden$="[[computeClearHidden_(capturing_, shortcut)]]"
             on-click="onClearTap_"></cr-icon-button>
       </cr-input>
diff --git a/chrome/browser/resources/settings/device_page/device_page.html b/chrome/browser/resources/settings/device_page/device_page.html
index e0ab2a8..2db1660 100644
--- a/chrome/browser/resources/settings/device_page/device_page.html
+++ b/chrome/browser/resources/settings/device_page/device_page.html
@@ -81,7 +81,7 @@
           </settings-storage>
         </settings-subpage>
       </template>
-      <template is="dom-if" if="[[androidRunning_]]">
+      <template is="dom-if" if="[[androidEnabled_]]">
         <template is="dom-if" route-path="/storage/externalStoragePreferences">
           <settings-subpage
               associated-control="[[$$('#externalStoragePreferencesRow')]]"
diff --git a/chrome/browser/resources/settings/device_page/device_page.js b/chrome/browser/resources/settings/device_page/device_page.js
index f009dad..bf0c44a1 100644
--- a/chrome/browser/resources/settings/device_page/device_page.js
+++ b/chrome/browser/resources/settings/device_page/device_page.js
@@ -101,7 +101,7 @@
     },
 
     /** @private */
-    androidRunning_: {
+    androidEnabled_: {
       type: Boolean,
       value: false,
     },
@@ -124,8 +124,8 @@
     settings.DevicePageBrowserProxyImpl.getInstance().initializeStylus();
 
     this.addWebUIListener(
-        'storage-android-running-changed',
-        this.set.bind(this, 'androidRunning_'));
+        'storage-android-enabled-changed',
+        this.set.bind(this, 'androidEnabled_'));
   },
 
   /**
diff --git a/chrome/browser/resources/settings/device_page/storage.html b/chrome/browser/resources/settings/device_page/storage.html
index 00881f1..d074d9c 100644
--- a/chrome/browser/resources/settings/device_page/storage.html
+++ b/chrome/browser/resources/settings/device_page/storage.html
@@ -233,7 +233,7 @@
           label="$i18n{storageItemOtherUsers}"
           sub-label="$i18n{storageSizeComputing}"></cr-link-row>
     </template>
-    <template is="dom-if" if="[[androidRunning_]]">
+    <template is="dom-if" if="[[androidEnabled_]]">
       <cr-link-row id="externalStoragePreferences" class="hr"
           on-click="onExternalStoragePreferencesTap_"
           label="$i18n{storageExternal}"></cr-link-row>
diff --git a/chrome/browser/resources/settings/device_page/storage.js b/chrome/browser/resources/settings/device_page/storage.js
index 2867765..b96c768f 100644
--- a/chrome/browser/resources/settings/device_page/storage.js
+++ b/chrome/browser/resources/settings/device_page/storage.js
@@ -44,6 +44,12 @@
     },
 
     /** @private */
+    androidEnabled_: {
+      type: Boolean,
+      value: false,
+    },
+
+    /** @private */
     androidRunning_: {
       type: Boolean,
       value: false,
@@ -112,6 +118,9 @@
         'storage-drive-enabled-changed',
         this.handleDriveEnabledChanged_.bind(this));
     this.addWebUIListener(
+        'storage-android-enabled-changed',
+        this.handleAndroidEnabledChanged_.bind(this));
+    this.addWebUIListener(
         'storage-android-running-changed',
         this.handleAndroidRunningChanged_.bind(this));
   },
@@ -277,6 +286,14 @@
   },
 
   /**
+   * @param {boolean} enabled True if Play Store is enabled.
+   * @private
+   */
+  handleAndroidEnabledChanged_: function(enabled) {
+    this.androidEnabled_ = enabled;
+  },
+
+  /**
    * @param {boolean} running True if Android (ARC) is running.
    * @private
    */
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 1e1f6c8..bf9797c 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1809,7 +1809,6 @@
       "//ash",
       "//ash/components/shortcut_viewer",
       "//ash/keyboard/ui",
-      "//ash/keyboard/ui:mojom",
       "//ash/public/cpp",
       "//ash/public/cpp/resources:ash_public_unscaled_resources",
       "//ash/public/cpp/vector_icons",
@@ -3294,8 +3293,6 @@
       "app_list/search/arc/arc_playstore_search_result.h",
       "app_list/search/chrome_search_result.cc",
       "app_list/search/chrome_search_result.h",
-      "app_list/search/common/json_response_fetcher.cc",
-      "app_list/search/common/json_response_fetcher.h",
       "app_list/search/common/url_icon_source.cc",
       "app_list/search/common/url_icon_source.h",
       "app_list/search/crostini/crostini_repository_search_provider.cc",
diff --git a/chrome/browser/ui/app_list/search/common/json_response_fetcher.cc b/chrome/browser/ui/app_list/search/common/json_response_fetcher.cc
deleted file mode 100644
index 35847c2..0000000
--- a/chrome/browser/ui/app_list/search/common/json_response_fetcher.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/app_list/search/common/json_response_fetcher.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/memory/ptr_util.h"
-#include "base/values.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/storage_partition.h"
-#include "content/public/common/service_manager_connection.h"
-#include "net/base/load_flags.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_status.h"
-#include "services/data_decoder/public/cpp/safe_json_parser.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-#include "url/gurl.h"
-
-namespace app_list {
-
-const char kBadResponse[] = "Bad Web Service search response";
-
-JSONResponseFetcher::JSONResponseFetcher(
-    const Callback& callback,
-    content::BrowserContext* browser_context)
-    : callback_(callback),
-      browser_context_(browser_context),
-      weak_factory_(this) {
-  DCHECK(!callback_.is_null());
-}
-
-JSONResponseFetcher::~JSONResponseFetcher() {}
-
-void JSONResponseFetcher::Start(const GURL& query_url) {
-  Stop();
-
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("json_response_fetcher", R"(
-          semantics {
-            sender: "JSON Response Fetcher"
-            description:
-              "Chrome OS downloads data for a web store result."
-            trigger:
-              "When a user initiates a web store search and views results. "
-            data:
-              "JSON data comprising search results. "
-              "No user information is sent."
-            destination: GOOGLE_OWNED_SERVICE
-          }
-          policy {
-            cookies_allowed: NO
-          })");
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  resource_request->url = query_url;
-  resource_request->load_flags =
-      net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DISABLE_CACHE;
-  simple_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
-                                                    traffic_annotation);
-  network::mojom::URLLoaderFactory* loader_factory =
-      content::BrowserContext::GetDefaultStoragePartition(browser_context_)
-          ->GetURLLoaderFactoryForBrowserProcess()
-          .get();
-  simple_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      loader_factory,
-      base::BindOnce(&JSONResponseFetcher::OnSimpleLoaderComplete,
-                     base::Unretained(this)));
-}
-
-void JSONResponseFetcher::Stop() {
-  simple_loader_.reset();
-  weak_factory_.InvalidateWeakPtrs();
-}
-
-void JSONResponseFetcher::OnJsonParseSuccess(
-    std::unique_ptr<base::Value> parsed_json) {
-  if (!parsed_json->is_dict()) {
-    OnJsonParseError(kBadResponse);
-    return;
-  }
-
-  callback_.Run(base::WrapUnique(
-      static_cast<base::DictionaryValue*>(parsed_json.release())));
-}
-
-void JSONResponseFetcher::OnJsonParseError(const std::string& error) {
-  callback_.Run(std::unique_ptr<base::DictionaryValue>());
-}
-
-void JSONResponseFetcher::OnSimpleLoaderComplete(
-    std::unique_ptr<std::string> response_body) {
-  if (!response_body) {
-    OnJsonParseError(kBadResponse);
-    return;
-  }
-
-  // The parser will call us back via one of the callbacks.
-  data_decoder::SafeJsonParser::Parse(
-      content::ServiceManagerConnection::GetForProcess()->GetConnector(),
-      *response_body,
-      base::Bind(&JSONResponseFetcher::OnJsonParseSuccess,
-                 weak_factory_.GetWeakPtr()),
-      base::Bind(&JSONResponseFetcher::OnJsonParseError,
-                 weak_factory_.GetWeakPtr()));
-}
-
-}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/common/json_response_fetcher.h b/chrome/browser/ui/app_list/search/common/json_response_fetcher.h
deleted file mode 100644
index 8f2ddc4..0000000
--- a/chrome/browser/ui/app_list/search/common/json_response_fetcher.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_COMMON_JSON_RESPONSE_FETCHER_H_
-#define CHROME_BROWSER_UI_APP_LIST_SEARCH_COMMON_JSON_RESPONSE_FETCHER_H_
-
-#include <memory>
-#include <string>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-
-class GURL;
-
-namespace base {
-class DictionaryValue;
-class Value;
-}
-
-namespace content {
-class BrowserContext;
-}
-
-namespace network {
-class SimpleURLLoader;
-}
-
-namespace app_list {
-
-// A class that fetches a JSON formatted response from a server and uses a
-// sandboxed utility process to parse it to a DictionaryValue.
-// TODO(rkc): Add the ability to give control of handling http failures to
-// the consumers of this class.
-class JSONResponseFetcher {
- public:
-  // Callback to pass back the parsed json dictionary returned from the server.
-  // Invoked with NULL if there is an error.
-  typedef base::Callback<void(std::unique_ptr<base::DictionaryValue>)> Callback;
-
-  JSONResponseFetcher(const Callback& callback,
-                      content::BrowserContext* browser_context);
-  ~JSONResponseFetcher();
-
-  // Starts to fetch results for the given |query_url|.
-  void Start(const GURL& query_url);
-  void Stop();
-
- private:
-  // Callbacks for SafeJsonParser.
-  void OnJsonParseSuccess(std::unique_ptr<base::Value> parsed_json);
-  void OnJsonParseError(const std::string& error);
-
-  // Invoked from SimpleURLLoader after download is complete.
-  void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
-
-  Callback callback_;
-  content::BrowserContext* browser_context_;
-  std::unique_ptr<network::SimpleURLLoader> simple_loader_;
-  base::WeakPtrFactory<JSONResponseFetcher> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(JSONResponseFetcher);
-};
-
-}  // namespace app_list
-
-#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_COMMON_JSON_RESPONSE_FETCHER_H_
diff --git a/chrome/browser/ui/app_list/search/mixer.cc b/chrome/browser/ui/app_list/search/mixer.cc
index 4a38185..ae14179 100644
--- a/chrome/browser/ui/app_list/search/mixer.cc
+++ b/chrome/browser/ui/app_list/search/mixer.cc
@@ -156,19 +156,24 @@
 }
 
 void Mixer::FetchResults(const base::string16& query) {
-  if (ranker_)
-    ranker_->FetchRankings(query);
+  if (non_app_ranker_)
+    non_app_ranker_->FetchRankings(query);
   for (const auto& group : groups_)
-    group->FetchResults(ranker_.get());
+    group->FetchResults(non_app_ranker_.get());
 }
 
-void Mixer::SetSearchResultRanker(std::unique_ptr<SearchResultRanker> ranker) {
-  ranker_ = std::move(ranker);
+void Mixer::SetNonAppSearchResultRanker(
+    std::unique_ptr<SearchResultRanker> ranker) {
+  non_app_ranker_ = std::move(ranker);
+}
+
+SearchResultRanker* Mixer::GetNonAppSearchResultRanker() {
+  return non_app_ranker_.get();
 }
 
 void Mixer::Train(const std::string& id, RankingItemType type) {
-  if (ranker_)
-    ranker_->Train(id, type);
+  if (non_app_ranker_)
+    non_app_ranker_->Train(id, type);
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/mixer.h b/chrome/browser/ui/app_list/search/mixer.h
index 777443b4..93b766bd 100644
--- a/chrome/browser/ui/app_list/search/mixer.h
+++ b/chrome/browser/ui/app_list/search/mixer.h
@@ -50,9 +50,13 @@
   // Collects the results, sorts and publishes them.
   void MixAndPublish(size_t num_max_results, const base::string16& query);
 
-  // Sets a SearchResultRanker to re-rank search results before they are
+  // Sets a SearchResultRanker to re-rank non-app search results before they are
   // published.
-  void SetSearchResultRanker(std::unique_ptr<SearchResultRanker> ranker);
+  void SetNonAppSearchResultRanker(std::unique_ptr<SearchResultRanker> ranker);
+
+  // Get a pointer to the SearchResultRanker owned by this object used for all
+  // non-app ranking.
+  SearchResultRanker* GetNonAppSearchResultRanker();
 
   // Handle a training signal.
   void Train(const std::string& id, RankingItemType type);
@@ -88,7 +92,7 @@
   Groups groups_;
 
   // Adaptive models used for re-ranking search results.
-  std::unique_ptr<SearchResultRanker> ranker_;
+  std::unique_ptr<SearchResultRanker> non_app_ranker_;
 
   DISALLOW_COPY_AND_ASSIGN(Mixer);
 };
diff --git a/chrome/browser/ui/app_list/search/search_controller.cc b/chrome/browser/ui/app_list/search/search_controller.cc
index 308f13e..2a81d51 100644
--- a/chrome/browser/ui/app_list/search/search_controller.cc
+++ b/chrome/browser/ui/app_list/search/search_controller.cc
@@ -51,10 +51,13 @@
                                    AppListControllerDelegate* list_controller,
                                    Profile* profile)
     : mixer_(std::make_unique<Mixer>(model_updater)),
-      ranker_(std::make_unique<AppSearchResultRanker>(
+      app_ranker_(std::make_unique<AppSearchResultRanker>(
           profile->GetPath(),
           chromeos::ProfileHelper::IsEphemeralUserProfile(profile))),
-      list_controller_(list_controller) {}
+      list_controller_(list_controller) {
+  mixer_->SetNonAppSearchResultRanker(
+      std::make_unique<SearchResultRanker>(profile));
+}
 
 SearchController::~SearchController() {}
 
@@ -150,9 +153,12 @@
   return nullptr;
 }
 
-void SearchController::SetSearchResultRanker(
-    std::unique_ptr<SearchResultRanker> ranker) {
-  mixer_->SetSearchResultRanker(std::move(ranker));
+AppSearchResultRanker* SearchController::GetAppSearchResultRanker() {
+  return app_ranker_.get();
+}
+
+SearchResultRanker* SearchController::GetNonAppSearchResultRanker() {
+  return mixer_->GetNonAppSearchResultRanker();
 }
 
 void SearchController::Train(const std::string& id, RankingItemType type) {
@@ -176,8 +182,4 @@
   mixer_->Train(id, type);
 }
 
-AppSearchResultRanker* SearchController::GetSearchResultRanker() {
-  return ranker_.get();
-}
-
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_controller.h b/chrome/browser/ui/app_list/search/search_controller.h
index 79e3ca9..a8c70ff 100644
--- a/chrome/browser/ui/app_list/search/search_controller.h
+++ b/chrome/browser/ui/app_list/search/search_controller.h
@@ -54,16 +54,16 @@
   ChromeSearchResult* FindSearchResult(const std::string& result_id);
   ChromeSearchResult* GetResultByTitleForTest(const std::string& title);
 
-  // Sets a SearchResultRanker to re-rank search results before they are
-  // published. The Mixer owned by the SearchController will take ownership of
-  // |ranker|.
-  void SetSearchResultRanker(std::unique_ptr<SearchResultRanker> ranker);
-
   // Sends training signal to each |providers_|
   void Train(const std::string& id, RankingItemType type);
 
-  // Get the app search result ranker owned by this object.
-  AppSearchResultRanker* GetSearchResultRanker();
+  // Gets the search result ranker owned by this object that is used for ranking
+  // apps.
+  AppSearchResultRanker* GetAppSearchResultRanker();
+
+  // Gets the search result ranker owned by the Mixer that is used for all
+  // other ranking.
+  SearchResultRanker* GetNonAppSearchResultRanker();
 
  private:
   // Invoked when the search results are changed.
@@ -77,10 +77,10 @@
   // The query associated with the most recent search.
   base::string16 last_query_;
 
+  std::unique_ptr<Mixer> mixer_;
   using Providers = std::vector<std::unique_ptr<SearchProvider>>;
   Providers providers_;
-  std::unique_ptr<Mixer> mixer_;
-  std::unique_ptr<AppSearchResultRanker> ranker_;
+  std::unique_ptr<AppSearchResultRanker> app_ranker_;
   AppListControllerDelegate* list_controller_;
 
   DISALLOW_COPY_AND_ASSIGN(SearchController);
diff --git a/chrome/browser/ui/app_list/search/search_controller_factory.cc b/chrome/browser/ui/app_list/search/search_controller_factory.cc
index a816f91..baccde2 100644
--- a/chrome/browser/ui/app_list/search/search_controller_factory.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_factory.cc
@@ -75,7 +75,8 @@
   std::unique_ptr<SearchController> controller =
       std::make_unique<SearchController>(model_updater, list_controller,
                                          profile);
-  AppSearchResultRanker* ranker = controller->GetSearchResultRanker();
+
+  AppSearchResultRanker* app_ranker = controller->GetAppSearchResultRanker();
 
   // Add mixer groups. There are four main groups: answer card, apps
   // and omnibox. Each group has a "soft" maximum number of results. However, if
@@ -94,7 +95,7 @@
   controller->AddProvider(apps_group_id, std::make_unique<AppSearchProvider>(
                                              profile, list_controller,
                                              base::DefaultClock::GetInstance(),
-                                             model_updater, ranker));
+                                             model_updater, app_ranker));
   controller->AddProvider(omnibox_group_id, std::make_unique<OmniboxProvider>(
                                                 profile, list_controller));
   if (app_list_features::IsAnswerCardEnabled()) {
@@ -155,7 +156,7 @@
     controller->AddProvider(
         app_shortcut_group_id,
         std::make_unique<ArcAppShortcutsSearchProvider>(
-            kMaxAppShortcutResults, profile, list_controller, ranker));
+            kMaxAppShortcutResults, profile, list_controller, app_ranker));
   }
 
   // TODO(https://crbug.com/921429): Put feature switch in ash/public/app_list/
@@ -168,9 +169,6 @@
         std::make_unique<CrostiniRepositorySearchProvider>(profile));
   }
 
-  controller->SetSearchResultRanker(
-      std::make_unique<SearchResultRanker>(profile));
-
   return controller;
 }
 
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
index a069e7f..cdcd116 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
@@ -49,7 +49,9 @@
 
 }  // namespace
 
-SearchResultRanker::SearchResultRanker(Profile* profile) {
+SearchResultRanker::SearchResultRanker(Profile* profile)
+    : enable_zero_state_mixed_types_(
+          app_list_features::IsZeroStateMixedTypesRankerEnabled()) {
   if (app_list_features::IsAdaptiveResultRankerEnabled()) {
     RecurrenceRankerConfigProto config;
     config.set_min_seconds_between_saves(240u);
@@ -73,18 +75,43 @@
   }
 
   profile_ = profile;
-  if (auto* notifier =
-          file_manager::file_tasks::FileTasksNotifier::GetForProfile(profile_))
-    notifier->AddObserver(this);
-  /*file_tasks_observer_.Add(
-      file_manager::file_tasks::FileTasksNotifierFactory::GetInstance()
-          ->GetForProfile(profile));*/
+  if (enable_zero_state_mixed_types_) {
+    if (auto* notifier =
+            file_manager::file_tasks::FileTasksNotifier::GetForProfile(
+                profile_)) {
+      notifier->AddObserver(this);
+    }
+
+    RecurrenceRankerConfigProto config;
+    config.set_min_seconds_between_saves(240u);
+    config.set_condition_limit(0u);
+    config.set_condition_decay(0.5f);
+
+    config.set_target_limit(base::GetFieldTrialParamByFeatureAsInt(
+        app_list_features::kEnableZeroStateMixedTypesRanker, "target_limit",
+        200));
+    config.set_target_decay(base::GetFieldTrialParamByFeatureAsDouble(
+        app_list_features::kEnableZeroStateMixedTypesRanker, "target_decay",
+        0.8f));
+
+    // Despite not changing any fields, this sets the predictor to the default
+    // predictor.
+    config.mutable_default_predictor();
+
+    zero_state_mixed_types_ranker_ = std::make_unique<RecurrenceRanker>(
+        profile->GetPath().AppendASCII("zero_state_mixed_types_ranker.proto"),
+        config, chromeos::ProfileHelper::IsEphemeralUserProfile(profile));
+  }
 }
 
 SearchResultRanker::~SearchResultRanker() {
-  if (auto* notifier =
-          file_manager::file_tasks::FileTasksNotifier::GetForProfile(profile_))
-    notifier->RemoveObserver(this);
+  if (enable_zero_state_mixed_types_) {
+    if (auto* notifier =
+            file_manager::file_tasks::FileTasksNotifier::GetForProfile(
+                profile_)) {
+      notifier->RemoveObserver(this);
+    }
+  }
 }
 
 void SearchResultRanker::FetchRankings(const base::string16& query) {
@@ -141,7 +168,15 @@
 
 void SearchResultRanker::OnFilesOpened(
     const std::vector<FileOpenEvent>& file_opens) {
-  // TODO(959679): route file open events to a model as training signals.
+  if (enable_zero_state_mixed_types_) {
+    DCHECK(zero_state_mixed_types_ranker_);
+    for (const auto& file_open : file_opens)
+      zero_state_mixed_types_ranker_->Record(file_open.path.value());
+  }
+}
+
+RecurrenceRanker* SearchResultRanker::get_zero_state_mixed_types_ranker() {
+  return zero_state_mixed_types_ranker_.get();
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h
index 29a1e53..858bf65a 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h
@@ -51,6 +51,8 @@
   // file_manager::file_tasks::FileTaskObserver:
   void OnFilesOpened(const std::vector<FileOpenEvent>& file_opens) override;
 
+  RecurrenceRanker* get_zero_state_mixed_types_ranker();
+
  private:
   // Records the time of the last call to FetchRankings() and is used to
   // limit the number of queries to the models within a short timespan.
@@ -68,13 +70,15 @@
   // affect apps.
   std::unique_ptr<RecurrenceRanker> results_list_group_ranker_;
 
+  // Ranks files and previous queries for launcher zero-state.
+  std::unique_ptr<RecurrenceRanker> zero_state_mixed_types_ranker_;
+
   // TODO(931149): Move the AppSearchResultRanker instance and associated logic
   // to here.
 
   Profile* profile_;
-  /*ScopedObserver<file_manager::file_tasks::FileTasksNotifier,
-                 file_manager::file_tasks::FileTasksObserver>
-      file_tasks_observer_{this};*/
+
+  const bool enable_zero_state_mixed_types_;
 };
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.cc b/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.cc
index 2c487b6c..da2c62aa 100644
--- a/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.cc
+++ b/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.cc
@@ -7,8 +7,8 @@
 #include <utility>
 
 #include "ash/keyboard/ui/keyboard_controller.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/resources/keyboard_resource_util.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/interfaces/constants.mojom.h"
 #include "base/bind.h"
 #include "base/callback.h"
diff --git a/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h b/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h
index 43359a8b..2912fde 100644
--- a/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h
+++ b/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h
@@ -9,7 +9,7 @@
 #include <set>
 #include <vector>
 
-#include "ash/keyboard/ui/public/keyboard_config.mojom.h"
+#include "ash/public/interfaces/keyboard_config.mojom.h"
 #include "ash/public/interfaces/keyboard_controller.mojom.h"
 #include "base/callback_forward.h"
 #include "base/macros.h"
diff --git a/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc b/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc
index 326cdee..9c5401d43 100644
--- a/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "ash/keyboard/ui/keyboard_controller.h"
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/resources/keyboard_resource_util.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/macros.h"
diff --git a/chrome/browser/ui/ash/keyboard/keyboard_end_to_end_browsertest.cc b/chrome/browser/ui/ash/keyboard/keyboard_end_to_end_browsertest.cc
index 9d497cf..4b009f2 100644
--- a/chrome/browser/ui/ash/keyboard/keyboard_end_to_end_browsertest.cc
+++ b/chrome/browser/ui/ash/keyboard/keyboard_end_to_end_browsertest.cc
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/keyboard/ui/public/keyboard_switches.h"
 #include "ash/keyboard/ui/resources/keyboard_resource_util.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "base/command_line.h"
 #include "base/files/file.h"
 #include "base/run_loop.h"
diff --git a/chrome/browser/ui/cocoa/browser_window_command_handler.mm b/chrome/browser/ui/cocoa/browser_window_command_handler.mm
index a32d7ff..f470b1d 100644
--- a/chrome/browser/ui/cocoa/browser_window_command_handler.mm
+++ b/chrome/browser/ui/cocoa/browser_window_command_handler.mm
@@ -13,10 +13,10 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #include "components/remote_cocoa/common/bridged_native_widget_host.mojom.h"
 #include "content/public/browser/web_contents.h"
 #import "ui/base/cocoa/cocoa_base_utils.h"
-#include "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 namespace {
 
diff --git a/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm b/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
index 8a7192c..ce8f820 100644
--- a/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
+++ b/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
@@ -6,12 +6,12 @@
 
 #include "base/logging.h"
 #include "chrome/browser/global_keyboard_shortcuts_mac.h"
+#include "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #include "components/remote_cocoa/common/bridged_native_widget_host.mojom.h"
 #include "content/public/browser/native_web_keyboard_event.h"
 #include "ui/base/accelerators/accelerator_manager.h"
 #include "ui/content_accelerators/accelerator_util.h"
 #include "ui/views/widget/widget.h"
-#include "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 @implementation ChromeCommandDispatcherDelegate
 
diff --git a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm
index 69456e6..833e0837 100644
--- a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm
+++ b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm
@@ -5,8 +5,8 @@
 #import "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.h"
 
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #include "ui/views/cocoa/bridged_native_widget_host_impl.h"
-#include "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 @implementation FullscreenToolbarControllerViews
 
diff --git a/chrome/browser/ui/cocoa/javascript_app_modal_dialog_cocoa.h b/chrome/browser/ui/cocoa/javascript_app_modal_dialog_cocoa.h
index a5b0e85..a6cb956b 100644
--- a/chrome/browser/ui/cocoa/javascript_app_modal_dialog_cocoa.h
+++ b/chrome/browser/ui/cocoa/javascript_app_modal_dialog_cocoa.h
@@ -12,8 +12,8 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/app_modal/native_app_modal_dialog.h"
+#include "components/remote_cocoa/app_shim/alert.h"
 #include "components/remote_cocoa/common/alert.mojom.h"
-#include "ui/views_bridge_mac/alert.h"
 
 class PopunderPreventer;
 
diff --git a/chrome/browser/ui/cocoa/javascript_app_modal_dialog_cocoa.mm b/chrome/browser/ui/cocoa/javascript_app_modal_dialog_cocoa.mm
index e739556..29cc502 100644
--- a/chrome/browser/ui/cocoa/javascript_app_modal_dialog_cocoa.mm
+++ b/chrome/browser/ui/cocoa/javascript_app_modal_dialog_cocoa.mm
@@ -17,6 +17,7 @@
 #include "components/app_modal/javascript_app_modal_dialog.h"
 #include "components/app_modal/javascript_dialog_manager.h"
 #include "components/app_modal/javascript_native_dialog_factory.h"
+#include "components/remote_cocoa/app_shim/alert.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
@@ -26,7 +27,6 @@
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/cocoa/bridge_factory_host.h"
 #include "ui/views/cocoa/bridged_native_widget_host_impl.h"
-#include "ui/views_bridge_mac/alert.h"
 
 using views_bridge_mac::mojom::AlertDisposition;
 
diff --git a/chrome/browser/ui/cocoa/touchbar/browser_window_touch_bar_controller_browsertest.mm b/chrome/browser/ui/cocoa/touchbar/browser_window_touch_bar_controller_browsertest.mm
index 0c6c6bf..2610388 100644
--- a/chrome/browser/ui/cocoa/touchbar/browser_window_touch_bar_controller_browsertest.mm
+++ b/chrome/browser/ui/cocoa/touchbar/browser_window_touch_bar_controller_browsertest.mm
@@ -14,6 +14,7 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/prefs/pref_service.h"
+#include "components/remote_cocoa/app_shim/window_touch_bar_delegate.h"
 #include "components/search_engines/default_search_manager.h"
 #include "components/search_engines/search_engines_test_util.h"
 #include "components/search_engines/template_url_data.h"
@@ -21,7 +22,6 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gtest_mac.h"
-#include "ui/views_bridge_mac/window_touch_bar_delegate.h"
 
 // TODO(spqchan): Write tests that will check for page load and bookmark
 // updates.
diff --git a/chrome/browser/ui/extensions/OWNERS b/chrome/browser/ui/extensions/OWNERS
index b8cca04d..dd0cbe0 100644
--- a/chrome/browser/ui/extensions/OWNERS
+++ b/chrome/browser/ui/extensions/OWNERS
@@ -1,8 +1,15 @@
 # App-y stuff
 benwells@chromium.org
+
+per-file bookmark_app_*=alancutter@chromium.org
+per-file bookmark_app_*=mgiuca@chromium.org
+per-file bookmark_app_*=ortuno@chromium.org
 per-file hosted_app_*=alancutter@chromium.org
 per-file hosted_app_*=mgiuca@chromium.org
 per-file hosted_app_*=ortuno@chromium.org
+per-file pwa_*=alancutter@chromium.org
+per-file pwa_*=mgiuca@chromium.org
+per-file pwa_*=ortuno@chromium.org
 
 # Extension-y stuff
 finnur@chromium.org
diff --git a/chrome/browser/ui/views/apps/app_window_native_widget_mac.mm b/chrome/browser/ui/views/apps/app_window_native_widget_mac.mm
index 2df0332..2f9db3846 100644
--- a/chrome/browser/ui/views/apps/app_window_native_widget_mac.mm
+++ b/chrome/browser/ui/views/apps/app_window_native_widget_mac.mm
@@ -7,10 +7,10 @@
 #import <Cocoa/Cocoa.h>
 
 #import "chrome/browser/ui/cocoa/apps/titlebar_background_view.h"
+#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
 #include "components/remote_cocoa/common/bridged_native_widget.mojom.h"
 #include "extensions/browser/app_window/native_app_window.h"
 #import "ui/base/cocoa/window_size_constants.h"
-#import "ui/views_bridge_mac/native_widget_mac_nswindow.h"
 
 AppWindowNativeWidgetMac::AppWindowNativeWidgetMac(
     views::Widget* widget,
diff --git a/chrome/browser/ui/views/extensions/OWNERS b/chrome/browser/ui/views/extensions/OWNERS
index 061ecf1..87bcf12 100644
--- a/chrome/browser/ui/views/extensions/OWNERS
+++ b/chrome/browser/ui/views/extensions/OWNERS
@@ -1,6 +1,4 @@
-benwells@chromium.org
-finnur@chromium.org
-rdevlin.cronin@chromium.org
+file://chrome/browser/ui/extensions/OWNERS
 
 # TEAM: extensions-dev@chromium.org
 # COMPONENT: Platform>Extensions
diff --git a/chrome/browser/ui/views/frame/browser_frame_mac.mm b/chrome/browser/ui/views/frame/browser_frame_mac.mm
index 418ad1db..fb6fb15 100644
--- a/chrome/browser/ui/views/frame/browser_frame_mac.mm
+++ b/chrome/browser/ui/views/frame/browser_frame_mac.mm
@@ -23,6 +23,9 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
+#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
+#import "components/remote_cocoa/app_shim/window_touch_bar_delegate.h"
 #include "components/remote_cocoa/common/bridge_factory.mojom.h"
 #include "components/remote_cocoa/common/bridged_native_widget.mojom.h"
 #include "components/remote_cocoa/common/bridged_native_widget_host.mojom.h"
@@ -31,9 +34,6 @@
 #import "ui/base/cocoa/window_size_constants.h"
 #include "ui/base/l10n/l10n_util.h"
 #import "ui/views/cocoa/bridged_native_widget_host_impl.h"
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
-#import "ui/views_bridge_mac/native_widget_mac_nswindow.h"
-#import "ui/views_bridge_mac/window_touch_bar_delegate.h"
 
 namespace {
 
diff --git a/chrome/browser/ui/views/select_file_dialog_extension_browsertest.cc b/chrome/browser/ui/views/select_file_dialog_extension_browsertest.cc
index 03bfe52..21966cd 100644
--- a/chrome/browser/ui/views/select_file_dialog_extension_browsertest.cc
+++ b/chrome/browser/ui/views/select_file_dialog_extension_browsertest.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/keyboard/ui/public/keyboard_switches.h"
+#include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "ash/public/cpp/test/shell_test_api.h"
 #include "base/bind_helpers.h"
 #include "base/files/file_util.h"
diff --git a/chrome/browser/ui/webui/downloads/downloads_ui.cc b/chrome/browser/ui/webui/downloads/downloads_ui.cc
index d0518761..1e17ab3 100644
--- a/chrome/browser/ui/webui/downloads/downloads_ui.cc
+++ b/chrome/browser/ui/webui/downloads/downloads_ui.cc
@@ -39,6 +39,7 @@
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/resources/grit/webui_resources.h"
 
 using content::BrowserContext;
 using content::DownloadManager;
@@ -138,8 +139,9 @@
                           IDR_DOWNLOADS_IMAGES_NO_DOWNLOADS_SVG);
   source->AddResourcePath("downloads.mojom-lite.js",
                           IDR_DOWNLOADS_MOJO_LITE_JS);
-
 #if BUILDFLAG(OPTIMIZE_WEBUI)
+  source->AddResourcePath("downloads.mojom-lite.html",
+                          IDR_DOWNLOADS_MOJO_LITE_HTML);
   source->AddResourcePath("crisper.js", IDR_DOWNLOADS_CRISPER_JS);
   source->SetDefaultResource(IDR_DOWNLOADS_VULCANIZED_HTML);
 #else
@@ -147,6 +149,9 @@
     source->AddResourcePath(kDownloadsResources[i].name,
                             kDownloadsResources[i].value);
   }
+  // Add the subpage loader, to load subpages in non-optimized builds.
+  source->AddResourcePath("subpage_loader.html", IDR_WEBUI_HTML_SUBPAGE_LOADER);
+  source->AddResourcePath("subpage_loader.js", IDR_WEBUI_JS_SUBPAGE_LOADER);
   source->SetDefaultResource(IDR_DOWNLOADS_DOWNLOADS_HTML);
 #endif
 
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chrome/browser/ui/webui/extensions/extensions_ui.cc
index 2a6e521..48427bf0 100644
--- a/chrome/browser/ui/webui/extensions/extensions_ui.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -118,6 +118,7 @@
     {"back", IDS_ACCNAME_BACK},
     {"cancel", IDS_CANCEL},
     {"close", IDS_CLOSE},
+    {"clear", IDS_CLEAR},
     {"confirm", IDS_CONFIRM},
     {"controlledSettingPolicy", IDS_CONTROLLED_SETTING_POLICY},
     {"done", IDS_DONE},
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
index eec0877..522ef6b 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
@@ -34,6 +34,7 @@
 #include "chromeos/cryptohome/cryptohome_util.h"
 #include "chromeos/dbus/cryptohome/cryptohome_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
+#include "components/arc/arc_prefs.h"
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/arc_util.h"
 #include "components/arc/session/arc_bridge_service.h"
@@ -111,6 +112,13 @@
 }
 
 void StorageHandler::OnJavascriptAllowed() {
+  pref_change_registrar_.Init(profile_->GetPrefs());
+  pref_change_registrar_.Add(
+      arc::prefs::kArcEnabled,
+      base::BindRepeating(&StorageHandler::OnArcEnabledChanged,
+                          // |this| always outlives |pref_change_registrar_|.
+                          base::Unretained(this)));
+
   // Start observing the mojo connection UpdateAndroidSize() relies on. Note
   // that OnConnectionReady() will be called immediately if the connection has
   // already been established.
@@ -131,6 +139,9 @@
       ->arc_bridge_service()
       ->storage_manager()
       ->RemoveObserver(this);
+
+  // Stop observing the pref changes for the same reason.
+  pref_change_registrar_.RemoveAll();
 }
 
 void StorageHandler::HandleUpdateStorageInfo(const base::ListValue* args) {
@@ -143,6 +154,7 @@
   UpdateAndroidSize();
   UpdateCrostiniSize();
   UpdateOtherUsersSize();
+  OnArcEnabledChanged();
 }
 
 void StorageHandler::HandleOpenDownloads(
@@ -428,5 +440,11 @@
   FireWebUIListener("storage-android-running-changed", base::Value(false));
 }
 
+void StorageHandler::OnArcEnabledChanged() {
+  FireWebUIListener(
+      "storage-android-enabled-changed",
+      base::Value(arc::IsArcPlayStoreEnabledForProfile(profile_)));
+}
+
 }  // namespace settings
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h
index b8ac8464..423c7fc 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h
@@ -19,6 +19,7 @@
 #include "components/arc/common/storage_manager.mojom.h"
 #include "components/arc/session/connection_observer.h"
 #include "components/arc/storage_manager/arc_storage_manager.h"
+#include "components/prefs/pref_change_registrar.h"
 #include "components/user_manager/user.h"
 
 class Profile;
@@ -110,6 +111,9 @@
   // Callback to save the fetched user sizes and update the UI.
   void OnGetOtherUserSize(base::Optional<cryptohome::BaseReply> reply);
 
+  // Callback to update ARC related UI.
+  void OnArcEnabledChanged();
+
   // Total size of cache data in browsing data.
   int64_t browser_cache_size_;
 
@@ -145,6 +149,10 @@
   bool is_android_running_;
 
   Profile* const profile_;
+
+  // Used to watch ARC prefs for changes so the UI can be notified.
+  PrefChangeRegistrar pref_change_registrar_;
+
   base::WeakPtrFactory<StorageHandler> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(StorageHandler);
diff --git a/chrome/browser/web_applications/components/install_finalizer.h b/chrome/browser/web_applications/components/install_finalizer.h
index 78eeffe..8235135 100644
--- a/chrome/browser/web_applications/components/install_finalizer.h
+++ b/chrome/browser/web_applications/components/install_finalizer.h
@@ -28,6 +28,8 @@
  public:
   using InstallFinalizedCallback =
       base::OnceCallback<void(const AppId& app_id, InstallResultCode code)>;
+  using UninstallExternalWebAppCallback =
+      base::OnceCallback<void(bool uninstalled)>;
   using CreateOsShortcutsCallback =
       base::OnceCallback<void(bool shortcuts_created)>;
 
@@ -46,6 +48,11 @@
                                const FinalizeOptions& options,
                                InstallFinalizedCallback callback) = 0;
 
+  // Removes the external app for |app_url| from disk and registrar. Fails if
+  // there is no installed external app for |app_url|.
+  virtual void UninstallExternalWebApp(const GURL& app_url,
+                                       UninstallExternalWebAppCallback) = 0;
+
   virtual bool CanCreateOsShortcuts() const = 0;
   virtual void CreateOsShortcuts(const AppId& app_id,
                                  bool add_to_desktop,
diff --git a/chrome/browser/web_applications/extensions/BUILD.gn b/chrome/browser/web_applications/extensions/BUILD.gn
index bea8a41..b523ab8 100644
--- a/chrome/browser/web_applications/extensions/BUILD.gn
+++ b/chrome/browser/web_applications/extensions/BUILD.gn
@@ -16,8 +16,6 @@
     "bookmark_app_registrar.h",
     "bookmark_app_tab_helper.cc",
     "bookmark_app_tab_helper.h",
-    "bookmark_app_uninstaller.cc",
-    "bookmark_app_uninstaller.h",
     "bookmark_app_util.cc",
     "bookmark_app_util.h",
     "pending_bookmark_app_manager.cc",
@@ -46,7 +44,6 @@
   sources = [
     "bookmark_app_install_finalizer_unittest.cc",
     "bookmark_app_installation_task_unittest.cc",
-    "bookmark_app_uninstaller_unittest.cc",
     "bookmark_app_util_unittest.cc",
     "externally_installed_web_app_prefs_unittest.cc",
     "install_manager_bookmark_app_unittest.cc",
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
index f71447f8..5c1875f 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
@@ -23,6 +23,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/install/crx_install_error.h"
+#include "extensions/browser/uninstall_reason.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_id.h"
 #include "extensions/common/extension_set.h"
@@ -70,7 +71,7 @@
 }  // namespace
 
 BookmarkAppInstallFinalizer::BookmarkAppInstallFinalizer(Profile* profile)
-    : profile_(profile) {
+    : profile_(profile), externally_installed_app_prefs_(profile->GetPrefs()) {
   crx_installer_factory_ = base::BindRepeating([](Profile* profile) {
     ExtensionService* extension_service =
         ExtensionSystem::Get(profile)->extension_service();
@@ -124,6 +125,41 @@
   crx_installer->InstallWebApp(web_app_info);
 }
 
+void BookmarkAppInstallFinalizer::UninstallExternalWebApp(
+    const GURL& app_url,
+    UninstallExternalWebAppCallback callback) {
+  base::Optional<web_app::AppId> app_id =
+      externally_installed_app_prefs_.LookupAppId(app_url);
+  if (!app_id.has_value()) {
+    LOG(WARNING) << "Couldn't uninstall app with url " << app_url
+                 << "; No corresponding extension for url.";
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), false));
+    return;
+  }
+
+  if (!ExtensionRegistry::Get(profile_)->enabled_extensions().GetByID(
+          app_id.value())) {
+    LOG(WARNING) << "Couldn't uninstall app with url " << app_url
+                 << "; Extension not installed.";
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), false));
+    return;
+  }
+
+  base::string16 error;
+  bool uninstalled =
+      ExtensionSystem::Get(profile_)->extension_service()->UninstallExtension(
+          app_id.value(), UNINSTALL_REASON_ORPHANED_EXTERNAL_EXTENSION, &error);
+
+  if (!uninstalled) {
+    LOG(WARNING) << "Couldn't uninstall app with url " << app_url << ". "
+                 << error;
+  }
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), uninstalled));
+}
+
 bool BookmarkAppInstallFinalizer::CanCreateOsShortcuts() const {
   return CanBookmarkAppCreateOsShortcuts();
 }
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
index 42685c5..7956bd80 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
@@ -8,6 +8,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "chrome/browser/web_applications/components/externally_installed_web_app_prefs.h"
 #include "chrome/browser/web_applications/components/install_finalizer.h"
 
 class Profile;
@@ -29,6 +30,9 @@
   void FinalizeInstall(const WebApplicationInfo& web_app_info,
                        const FinalizeOptions& options,
                        InstallFinalizedCallback callback) override;
+  void UninstallExternalWebApp(
+      const GURL& app_url,
+      UninstallExternalWebAppCallback callback) override;
   bool CanCreateOsShortcuts() const override;
   void CreateOsShortcuts(const web_app::AppId& app_id,
                          bool add_to_desktop,
@@ -53,6 +57,7 @@
  private:
   CrxInstallerFactory crx_installer_factory_;
   Profile* profile_;
+  web_app::ExternallyInstalledWebAppPrefs externally_installed_app_prefs_;
 
   DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstallFinalizer);
 };
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
index 2852cd8..f2644fe 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
@@ -24,6 +24,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_registry_observer.h"
 #include "extensions/browser/install/crx_install_error.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension_id.h"
@@ -34,6 +35,7 @@
 namespace {
 
 const GURL kWebAppUrl("https://foo.example");
+const GURL kAlternateWebAppUrl("https://bar.example");
 const char kWebAppTitle[] = "Foo Title";
 
 }  // namespace
@@ -91,6 +93,45 @@
                                         false /* autoupdate_enabled */);
   }
 
+  web_app::AppId InstallExternalApp(BookmarkAppInstallFinalizer* finalizer,
+                                    const GURL& app_url) {
+    auto info = std::make_unique<WebApplicationInfo>();
+    info->app_url = app_url;
+    info->title = base::ASCIIToUTF16(kWebAppTitle);
+
+    web_app::InstallFinalizer::FinalizeOptions options;
+    options.policy_installed = true;
+
+    web_app::AppId app_id;
+    base::RunLoop run_loop;
+    finalizer->FinalizeInstall(
+        *info, options,
+        base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
+                                       web_app::InstallResultCode code) {
+          ASSERT_EQ(web_app::InstallResultCode::kSuccess, code);
+          app_id = installed_app_id;
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+
+    web_app::ExternallyInstalledWebAppPrefs(profile()->GetPrefs())
+        .Insert(app_url, app_id, web_app::InstallSource::kExternalPolicy);
+
+    return app_id;
+  }
+
+  void SimulateExternalAppUninstalledByUser(const web_app::AppId& app_id) {
+    ExtensionRegistry::Get(profile())->RemoveEnabled(app_id);
+    auto* extension_prefs = ExtensionPrefs::Get(profile());
+    extension_prefs->OnExtensionUninstalled(app_id, Manifest::EXTERNAL_POLICY,
+                                            false /* external_uninstall */);
+    DCHECK(extension_prefs->IsExternalExtensionUninstalled(app_id));
+  }
+
+  const ExtensionSet& enabled_extensions() {
+    return ExtensionRegistry::Get(profile())->enabled_extensions();
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstallFinalizerTest);
 };
@@ -336,6 +377,113 @@
       installer.CanSkipAppUpdateForSync(app_id, info_with_diff_description));
 }
 
+TEST_F(BookmarkAppInstallFinalizerTest, UninstallExternalWebApp_Successful) {
+  BookmarkAppInstallFinalizer finalizer(profile());
+
+  InstallExternalApp(&finalizer, kWebAppUrl);
+  ASSERT_EQ(1u, enabled_extensions().size());
+
+  base::RunLoop run_loop;
+  finalizer.UninstallExternalWebApp(
+      kWebAppUrl, base::BindLambdaForTesting([&](bool uninstalled) {
+        EXPECT_TRUE(uninstalled);
+        EXPECT_EQ(0u, enabled_extensions().size());
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+}
+
+TEST_F(BookmarkAppInstallFinalizerTest, UninstallExternalWebApp_Multiple) {
+  BookmarkAppInstallFinalizer finalizer(profile());
+
+  auto foo_app_id = InstallExternalApp(&finalizer, kWebAppUrl);
+  auto bar_app_id = InstallExternalApp(&finalizer, kAlternateWebAppUrl);
+  ASSERT_EQ(2u, enabled_extensions().size());
+
+  // Uninstall one app.
+  {
+    base::RunLoop run_loop;
+    finalizer.UninstallExternalWebApp(
+        kWebAppUrl, base::BindLambdaForTesting([&](bool uninstalled) {
+          EXPECT_TRUE(uninstalled);
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+  }
+
+  EXPECT_EQ(1u, enabled_extensions().size());
+  EXPECT_TRUE(enabled_extensions().Contains(bar_app_id));
+
+  // Uninstall the second app.
+  {
+    base::RunLoop run_loop;
+    finalizer.UninstallExternalWebApp(
+        kAlternateWebAppUrl, base::BindLambdaForTesting([&](bool uninstalled) {
+          EXPECT_TRUE(uninstalled);
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+  }
+  EXPECT_EQ(0u, enabled_extensions().size());
+}
+
+TEST_F(BookmarkAppInstallFinalizerTest,
+       UninstallExternalWebApp_UninstalledExternalApp) {
+  BookmarkAppInstallFinalizer finalizer(profile());
+
+  auto app_id = InstallExternalApp(&finalizer, kWebAppUrl);
+  SimulateExternalAppUninstalledByUser(app_id);
+
+  base::RunLoop run_loop;
+  finalizer.UninstallExternalWebApp(
+      kWebAppUrl, base::BindLambdaForTesting([&](bool uninstalled) {
+        EXPECT_FALSE(uninstalled);
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+}
+
+TEST_F(BookmarkAppInstallFinalizerTest,
+       UninstallExternalWebApp_FailsNeverInstalled) {
+  BookmarkAppInstallFinalizer finalizer(profile());
+  base::RunLoop run_loop;
+  finalizer.UninstallExternalWebApp(
+      kWebAppUrl, base::BindLambdaForTesting([&](bool uninstalled) {
+        EXPECT_FALSE(uninstalled);
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+}
+
+TEST_F(BookmarkAppInstallFinalizerTest,
+       UninstallExternalWebApp_FailsAlreadyUninstalled) {
+  BookmarkAppInstallFinalizer finalizer(profile());
+
+  InstallExternalApp(&finalizer, kWebAppUrl);
+
+  // Uninstall the app.
+  {
+    base::RunLoop run_loop;
+    finalizer.UninstallExternalWebApp(
+        kWebAppUrl, base::BindLambdaForTesting([&](bool uninstalled) {
+          EXPECT_TRUE(uninstalled);
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+  }
+
+  // Try to uninstall it again.
+  {
+    base::RunLoop run_loop;
+    finalizer.UninstallExternalWebApp(
+        kWebAppUrl, base::BindLambdaForTesting([&](bool uninstalled) {
+          EXPECT_FALSE(uninstalled);
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+  }
+}
+
 TEST_F(BookmarkAppInstallFinalizerTest, NotLocallyInstalled) {
   BookmarkAppInstallFinalizer installer(profile());
 
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_installation_task_unittest.cc b/chrome/browser/web_applications/extensions/bookmark_app_installation_task_unittest.cc
index 3454417..5005acb 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_installation_task_unittest.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_installation_task_unittest.cc
@@ -178,6 +178,12 @@
             std::move(callback)));
   }
 
+  void UninstallExternalWebApp(
+      const GURL& app_url,
+      UninstallExternalWebAppCallback callback) override {
+    NOTREACHED();
+  }
+
   bool CanCreateOsShortcuts() const override { return true; }
 
   void CreateOsShortcuts(const web_app::AppId& app_id,
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_uninstaller.cc b/chrome/browser/web_applications/extensions/bookmark_app_uninstaller.cc
deleted file mode 100644
index 74e9021..0000000
--- a/chrome/browser/web_applications/extensions/bookmark_app_uninstaller.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/web_applications/extensions/bookmark_app_uninstaller.h"
-
-#include "base/optional.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/web_applications/components/app_registrar.h"
-#include "extensions/browser/extension_system.h"
-#include "extensions/browser/uninstall_reason.h"
-
-namespace extensions {
-
-BookmarkAppUninstaller::BookmarkAppUninstaller(Profile* profile,
-                                               web_app::AppRegistrar* registrar)
-    : profile_(profile),
-      registrar_(registrar),
-      externally_installed_app_prefs_(profile->GetPrefs()) {}
-
-BookmarkAppUninstaller::~BookmarkAppUninstaller() = default;
-
-void BookmarkAppUninstaller::UninstallApp(const GURL& app_url,
-                                          UninstallCallback callback) {
-  base::Optional<web_app::AppId> app_id =
-      externally_installed_app_prefs_.LookupAppId(app_url);
-  if (!app_id.has_value()) {
-    LOG(WARNING) << "Couldn't uninstall app with url " << app_url
-                 << "; No corresponding extension for url.";
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), false));
-    return;
-  }
-
-  if (!registrar_->IsInstalled(app_id.value())) {
-    LOG(WARNING) << "Couldn't uninstall app with url " << app_url
-                 << "; Extension not installed.";
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), false));
-    return;
-  }
-
-  base::string16 error;
-  bool uninstalled =
-      ExtensionSystem::Get(profile_)->extension_service()->UninstallExtension(
-          app_id.value(), UNINSTALL_REASON_ORPHANED_EXTERNAL_EXTENSION, &error);
-
-  if (!uninstalled) {
-    LOG(WARNING) << "Couldn't uninstall app with url " << app_url << ". "
-                 << error;
-  }
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), uninstalled));
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_uninstaller.h b/chrome/browser/web_applications/extensions/bookmark_app_uninstaller.h
deleted file mode 100644
index a8ee388..0000000
--- a/chrome/browser/web_applications/extensions/bookmark_app_uninstaller.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_UNINSTALLER_H_
-#define CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_UNINSTALLER_H_
-
-#include "chrome/browser/web_applications/components/externally_installed_web_app_prefs.h"
-
-class Profile;
-class GURL;
-
-namespace web_app {
-class AppRegistrar;
-}
-
-namespace extensions {
-
-class BookmarkAppUninstaller {
- public:
-  using UninstallCallback = base::OnceCallback<void(bool uninstalled)>;
-
-  BookmarkAppUninstaller(Profile* profile, web_app::AppRegistrar* registrar);
-  virtual ~BookmarkAppUninstaller();
-
-  // Runs |callback| with true if the app with |app_url| was successfully
-  // uninstalled. Runs callback with false if the app doesn't not exist, or the
-  // app failed to be uninstalled.
-  virtual void UninstallApp(const GURL& app_url, UninstallCallback callback);
-
- private:
-  Profile* profile_;
-  web_app::AppRegistrar* registrar_;
-  web_app::ExternallyInstalledWebAppPrefs externally_installed_app_prefs_;
-};
-
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_UNINSTALLER_H_
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_uninstaller_unittest.cc b/chrome/browser/web_applications/extensions/bookmark_app_uninstaller_unittest.cc
deleted file mode 100644
index 754ba12c..0000000
--- a/chrome/browser/web_applications/extensions/bookmark_app_uninstaller_unittest.cc
+++ /dev/null
@@ -1,216 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/web_applications/extensions/bookmark_app_uninstaller.h"
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/scoped_observer.h"
-#include "base/test/bind_test_util.h"
-#include "chrome/browser/extensions/test_extension_system.h"
-#include "chrome/browser/web_applications/components/web_app_constants.h"
-#include "chrome/browser/web_applications/extensions/bookmark_app_registrar.h"
-#include "chrome/browser/web_applications/test/test_app_registrar.h"
-#include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/crx_file/id_util.h"
-#include "content/public/test/test_utils.h"
-#include "extensions/browser/extension_prefs.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/browser/extension_registry_observer.h"
-#include "extensions/common/extension_builder.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace extensions {
-
-namespace {
-
-const GURL kFooWebAppUrl("https://foo.example");
-const GURL kBarWebAppUrl("https://bar.example");
-
-class TestExtensionRegistryObserver : public ExtensionRegistryObserver {
- public:
-  explicit TestExtensionRegistryObserver(ExtensionRegistry* registry) {
-    extension_registry_observer_.Add(registry);
-  }
-
-  ~TestExtensionRegistryObserver() override = default;
-
-  const std::vector<std::string>& uninstalled_extension_ids() {
-    return uninstalled_extension_ids_;
-  }
-
-  void ResetResults() { uninstalled_extension_ids_.clear(); }
-
-  // ExtensionRegistryObserver
-  void OnExtensionUninstalled(content::BrowserContext* browser_context,
-                              const Extension* extension,
-                              UninstallReason reason) override {
-    uninstalled_extension_ids_.push_back(extension->id());
-  }
-
- private:
-  std::vector<std::string> uninstalled_extension_ids_;
-
-  ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
-      extension_registry_observer_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(TestExtensionRegistryObserver);
-};
-
-std::string GenerateFakeAppId(const GURL& url) {
-  return crx_file::id_util::GenerateId("fake_app_id_for:" + url.spec());
-}
-
-}  // namespace
-
-class BookmarkAppUninstallerTest : public ChromeRenderViewHostTestHarness {
- public:
-  BookmarkAppUninstallerTest() = default;
-  ~BookmarkAppUninstallerTest() override = default;
-
-  void SetUp() override {
-    ChromeRenderViewHostTestHarness::SetUp();
-
-    TestExtensionSystem* test_system =
-        static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()));
-    test_system->CreateExtensionService(base::CommandLine::ForCurrentProcess(),
-                                        profile()->GetPath(),
-                                        false /* autoupdate_enabled */);
-    test_extension_registry_observer_ =
-        std::make_unique<TestExtensionRegistryObserver>(
-            ExtensionRegistry::Get(profile()));
-
-    registrar_ = std::make_unique<BookmarkAppRegistrar>(profile());
-
-    uninstaller_ =
-        std::make_unique<BookmarkAppUninstaller>(profile(), registrar_.get());
-  }
-
-  void TearDown() override {
-    // Delete the observer before ExtensionRegistry is deleted.
-    test_extension_registry_observer_.reset();
-    ChromeRenderViewHostTestHarness::TearDown();
-  }
-
-  std::string SimulateInstalledApp(const GURL& app_url) {
-    std::string app_id = GenerateFakeAppId(app_url);
-
-    auto extension = ExtensionBuilder("FooBar")
-                         .SetLocation(Manifest::EXTERNAL_POLICY)
-                         .SetID(app_id)
-                         .Build();
-    ExtensionRegistry::Get(profile())->AddEnabled(extension);
-    web_app::ExternallyInstalledWebAppPrefs(profile()->GetPrefs())
-        .Insert(app_url, app_id, web_app::InstallSource::kExternalPolicy);
-    return app_id;
-  }
-
-  void SimulateExternalAppUninstalledByUser(const GURL& app_url) {
-    const std::string app_id = GenerateFakeAppId(app_url);
-    ExtensionRegistry::Get(profile())->RemoveEnabled(app_id);
-    ExtensionPrefs::Get(profile())->OnExtensionUninstalled(
-        app_id, Manifest::EXTERNAL_POLICY, false /* external_uninstall */);
-  }
-
-  bool UninstallAppAndWait(const GURL& app_url) {
-    base::RunLoop run_loop;
-    base::Optional<bool> result;
-    uninstaller().UninstallApp(
-        app_url, base::BindLambdaForTesting([&](bool uninstalled) {
-          result = uninstalled;
-          run_loop.Quit();
-        }));
-    run_loop.Run();
-    return result.value();
-  }
-
-  void ResetResults() { test_extension_registry_observer_->ResetResults(); }
-
-  BookmarkAppUninstaller& uninstaller() { return *uninstaller_; }
-
-  const std::vector<std::string>& uninstalled_extension_ids() {
-    return test_extension_registry_observer_->uninstalled_extension_ids();
-  }
-
-  const ExtensionSet& enabled_extensions() {
-    return ExtensionRegistry::Get(profile())->enabled_extensions();
-  }
-
- private:
-  std::unique_ptr<TestExtensionRegistryObserver>
-      test_extension_registry_observer_;
-
-  std::unique_ptr<BookmarkAppRegistrar> registrar_;
-  std::unique_ptr<BookmarkAppUninstaller> uninstaller_;
-
-  DISALLOW_COPY_AND_ASSIGN(BookmarkAppUninstallerTest);
-};
-
-TEST_F(BookmarkAppUninstallerTest, Uninstall_Successful) {
-  SimulateInstalledApp(kFooWebAppUrl);
-  ASSERT_EQ(1u, enabled_extensions().size());
-
-  EXPECT_TRUE(UninstallAppAndWait(kFooWebAppUrl));
-  content::RunAllTasksUntilIdle();
-
-  EXPECT_EQ(1u, uninstalled_extension_ids().size());
-  EXPECT_EQ(0u, enabled_extensions().size());
-}
-
-TEST_F(BookmarkAppUninstallerTest, Uninstall_Multiple) {
-  auto foo_app_id = SimulateInstalledApp(kFooWebAppUrl);
-  auto bar_app_id = SimulateInstalledApp(kBarWebAppUrl);
-  ASSERT_EQ(2u, enabled_extensions().size());
-
-  EXPECT_TRUE(UninstallAppAndWait(kBarWebAppUrl));
-  content::RunAllTasksUntilIdle();
-
-  EXPECT_EQ(1u, uninstalled_extension_ids().size());
-  EXPECT_EQ(1u, enabled_extensions().size());
-  EXPECT_FALSE(enabled_extensions().Contains(bar_app_id));
-  EXPECT_TRUE(enabled_extensions().Contains(foo_app_id));
-
-  ResetResults();
-
-  EXPECT_TRUE(UninstallAppAndWait(kFooWebAppUrl));
-  content::RunAllTasksUntilIdle();
-
-  EXPECT_EQ(1u, uninstalled_extension_ids().size());
-  EXPECT_TRUE(enabled_extensions().is_empty());
-}
-
-TEST_F(BookmarkAppUninstallerTest, Uninstall_UninstalledExternalApp) {
-  SimulateInstalledApp(kFooWebAppUrl);
-  SimulateExternalAppUninstalledByUser(kFooWebAppUrl);
-
-  EXPECT_FALSE(UninstallAppAndWait(kFooWebAppUrl));
-}
-
-// Tests trying to uninstall an app that was never installed.
-TEST_F(BookmarkAppUninstallerTest, Uninstall_FailsNeverInstalled) {
-  EXPECT_FALSE(UninstallAppAndWait(kFooWebAppUrl));
-}
-
-// Tests trying to uninstall an app that was previously uninstalled.
-TEST_F(BookmarkAppUninstallerTest, Uninstall_FailsAlreadyUninstalled) {
-  SimulateInstalledApp(kFooWebAppUrl);
-
-  EXPECT_TRUE(UninstallAppAndWait(kFooWebAppUrl));
-  content::RunAllTasksUntilIdle();
-
-  EXPECT_EQ(1u, uninstalled_extension_ids().size());
-  EXPECT_TRUE(enabled_extensions().is_empty());
-
-  ResetResults();
-
-  EXPECT_FALSE(UninstallAppAndWait(kFooWebAppUrl));
-  content::RunAllTasksUntilIdle();
-
-  EXPECT_EQ(0u, uninstalled_extension_ids().size());
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
index 3be7d49..27045fe5 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
@@ -52,8 +52,6 @@
     : profile_(profile),
       registrar_(registrar),
       install_finalizer_(install_finalizer),
-      uninstaller_(
-          std::make_unique<BookmarkAppUninstaller>(profile_, registrar_)),
       externally_installed_app_prefs_(profile->GetPrefs()),
       url_loader_(std::make_unique<web_app::WebAppUrlLoader>()),
       task_factory_(base::BindRepeating(&InstallationTaskCreateWrapper)) {}
@@ -93,7 +91,7 @@
     std::vector<GURL> uninstall_urls,
     const UninstallCallback& callback) {
   for (auto& url : uninstall_urls) {
-    uninstaller_->UninstallApp(
+    install_finalizer_->UninstallExternalWebApp(
         url, base::BindOnce(
                  [](const UninstallCallback& callback, const GURL& app_url,
                     bool uninstalled) { callback.Run(app_url, uninstalled); },
@@ -124,11 +122,6 @@
   task_factory_ = std::move(task_factory);
 }
 
-void PendingBookmarkAppManager::SetUninstallerForTesting(
-    std::unique_ptr<BookmarkAppUninstaller> uninstaller) {
-  uninstaller_ = std::move(uninstaller);
-}
-
 void PendingBookmarkAppManager::SetUrlLoaderForTesting(
     std::unique_ptr<web_app::WebAppUrlLoader> url_loader) {
   url_loader_ = std::move(url_loader);
@@ -272,7 +265,7 @@
           install_options.url);
 
   if (app_id.has_value() && registrar_->IsInstalled(app_id.value())) {
-    uninstaller_->UninstallApp(
+    install_finalizer_->UninstallExternalWebApp(
         install_options.url,
         base::BindOnce(&PendingBookmarkAppManager::OnPlaceholderUninstalled,
                        weak_ptr_factory_.GetWeakPtr()));
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
index 428978f3..ef0361b 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
@@ -19,7 +19,6 @@
 #include "chrome/browser/web_applications/components/pending_app_manager.h"
 #include "chrome/browser/web_applications/components/web_app_url_loader.h"
 #include "chrome/browser/web_applications/extensions/bookmark_app_installation_task.h"
-#include "chrome/browser/web_applications/extensions/bookmark_app_uninstaller.h"
 
 class GURL;
 class Profile;
@@ -73,8 +72,6 @@
       web_app::InstallSource install_source) const override;
 
   void SetTaskFactoryForTesting(TaskFactory task_factory);
-  void SetUninstallerForTesting(
-      std::unique_ptr<BookmarkAppUninstaller> uninstaller);
   void SetUrlLoaderForTesting(
       std::unique_ptr<web_app::WebAppUrlLoader> url_loader);
 
@@ -102,7 +99,6 @@
   Profile* profile_;
   web_app::AppRegistrar* registrar_;
   web_app::InstallFinalizer* install_finalizer_;
-  std::unique_ptr<BookmarkAppUninstaller> uninstaller_;
   web_app::ExternallyInstalledWebAppPrefs externally_installed_app_prefs_;
 
   // unique_ptr so that it can be replaced in tests.
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
index 711b92bc..69300c2 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
@@ -146,58 +146,6 @@
   DISALLOW_COPY_AND_ASSIGN(TestBookmarkAppInstallationTask);
 };
 
-class TestBookmarkAppUninstaller : public BookmarkAppUninstaller {
- public:
-  TestBookmarkAppUninstaller(Profile* profile,
-                             web_app::TestAppRegistrar* registrar)
-      : BookmarkAppUninstaller(profile, registrar), registrar_(registrar) {}
-
-  ~TestBookmarkAppUninstaller() override = default;
-
-  size_t uninstall_call_count() { return uninstall_call_count_; }
-
-  const std::vector<GURL>& uninstalled_app_urls() {
-    return uninstalled_app_urls_;
-  }
-
-  const GURL& last_uninstalled_app_url() { return uninstalled_app_urls_[0]; }
-
-  void SetNextResultForTesting(const GURL& app_url, bool result) {
-    DCHECK(!base::ContainsKey(next_result_map_, app_url));
-    next_result_map_[app_url] = result;
-  }
-
-  // BookmarkAppUninstaller
-  void UninstallApp(const GURL& app_url, UninstallCallback callback) override {
-    DCHECK(base::ContainsKey(next_result_map_, app_url));
-
-    ++uninstall_call_count_;
-    uninstalled_app_urls_.push_back(app_url);
-
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(base::BindLambdaForTesting(
-                                      [&, app_url](UninstallCallback callback) {
-                                        bool result = next_result_map_[app_url];
-                                        next_result_map_.erase(app_url);
-
-                                        if (result)
-                                          registrar_->RemoveAsInstalled(
-                                              GenerateFakeAppId(app_url));
-                                        std::move(callback).Run(result);
-                                      }),
-                                  std::move(callback)));
-  }
-
- private:
-  std::map<GURL, bool> next_result_map_;
-  web_app::TestAppRegistrar* registrar_;
-
-  size_t uninstall_call_count_ = 0;
-  std::vector<GURL> uninstalled_app_urls_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestBookmarkAppUninstaller);
-};
-
 }  // namespace
 
 class PendingBookmarkAppManagerTest : public ChromeRenderViewHostTestHarness {
@@ -223,7 +171,6 @@
   }
 
   void TearDown() override {
-    uninstaller_ = nullptr;
     ChromeRenderViewHostTestHarness::TearDown();
   }
 
@@ -349,14 +296,6 @@
         profile(), registrar_.get(), install_finalizer_.get());
     manager->SetTaskFactoryForTesting(successful_installation_task_creator());
 
-    // The test suite doesn't support multiple uninstallers.
-    DCHECK_EQ(nullptr, uninstaller_);
-
-    auto uninstaller = std::make_unique<TestBookmarkAppUninstaller>(
-        profile(), registrar_.get());
-    uninstaller_ = uninstaller.get();
-    manager->SetUninstallerForTesting(std::move(uninstaller));
-
     // The test suite doesn't support multiple loaders.
     DCHECK_EQ(nullptr, url_loader_);
 
@@ -382,24 +321,28 @@
     return install_placeholder_run_count_;
   }
 
-  size_t uninstall_call_count() { return uninstaller_->uninstall_call_count(); }
+  size_t uninstall_call_count() {
+    return install_finalizer_->uninstall_external_web_app_urls().size();
+  }
 
   const std::vector<GURL>& uninstalled_app_urls() {
-    return uninstaller_->uninstalled_app_urls();
+    return install_finalizer_->uninstall_external_web_app_urls();
   }
 
   const GURL& last_uninstalled_app_url() {
-    return uninstaller_->last_uninstalled_app_url();
+    return install_finalizer_->uninstall_external_web_app_urls().back();
   }
 
   web_app::TestAppRegistrar* registrar() { return registrar_.get(); }
 
   web_app::TestWebAppUiDelegate* ui_delegate() { return ui_delegate_.get(); }
 
-  TestBookmarkAppUninstaller* uninstaller() { return uninstaller_; }
-
   web_app::TestWebAppUrlLoader* url_loader() { return url_loader_; }
 
+  web_app::TestInstallFinalizer* install_finalizer() {
+    return install_finalizer_.get();
+  }
+
  private:
   base::Optional<web_app::InstallOptions> last_app_info_;
   size_t install_run_count_ = 0;
@@ -412,7 +355,6 @@
   std::unique_ptr<web_app::TestWebAppUiDelegate> ui_delegate_;
   std::unique_ptr<web_app::TestInstallFinalizer> install_finalizer_;
 
-  TestBookmarkAppUninstaller* uninstaller_ = nullptr;
   web_app::TestWebAppUrlLoader* url_loader_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(PendingBookmarkAppManagerTest);
@@ -1125,7 +1067,8 @@
   auto pending_app_manager = GetPendingBookmarkAppManagerWithTestFactories();
   registrar()->AddAsInstalled(GenerateFakeAppId(kFooWebAppUrl));
 
-  uninstaller()->SetNextResultForTesting(kFooWebAppUrl, true);
+  install_finalizer()->SetNextUninstallExternalWebAppResult(kFooWebAppUrl,
+                                                            true);
   UninstallAppsResults results = UninstallAppsAndWait(
       pending_app_manager.get(), std::vector<GURL>{kFooWebAppUrl});
 
@@ -1138,7 +1081,8 @@
 TEST_F(PendingBookmarkAppManagerTest, UninstallApps_Fails) {
   auto pending_app_manager = GetPendingBookmarkAppManagerWithTestFactories();
 
-  uninstaller()->SetNextResultForTesting(kFooWebAppUrl, false);
+  install_finalizer()->SetNextUninstallExternalWebAppResult(kFooWebAppUrl,
+                                                            false);
   UninstallAppsResults results = UninstallAppsAndWait(
       pending_app_manager.get(), std::vector<GURL>{kFooWebAppUrl});
   EXPECT_EQ(results, UninstallAppsResults({{kFooWebAppUrl, false}}));
@@ -1152,8 +1096,10 @@
   registrar()->AddAsInstalled(GenerateFakeAppId(kFooWebAppUrl));
   registrar()->AddAsInstalled(GenerateFakeAppId(kBarWebAppUrl));
 
-  uninstaller()->SetNextResultForTesting(kFooWebAppUrl, true);
-  uninstaller()->SetNextResultForTesting(kBarWebAppUrl, true);
+  install_finalizer()->SetNextUninstallExternalWebAppResult(kFooWebAppUrl,
+                                                            true);
+  install_finalizer()->SetNextUninstallExternalWebAppResult(kBarWebAppUrl,
+                                                            true);
   UninstallAppsResults results =
       UninstallAppsAndWait(pending_app_manager.get(),
                            std::vector<GURL>{kFooWebAppUrl, kBarWebAppUrl});
@@ -1180,7 +1126,8 @@
             run_loop.Quit();
           }));
 
-  uninstaller()->SetNextResultForTesting(kFooWebAppUrl, false);
+  install_finalizer()->SetNextUninstallExternalWebAppResult(kFooWebAppUrl,
+                                                            false);
   UninstallAppsResults uninstall_results = UninstallAppsAndWait(
       pending_app_manager.get(), std::vector<GURL>{kFooWebAppUrl});
   EXPECT_EQ(uninstall_results, UninstallAppsResults({{kFooWebAppUrl, false}}));
@@ -1212,7 +1159,8 @@
     install_options.reinstall_placeholder = true;
     url_loader()->SetNextLoadUrlResult(
         kFooWebAppUrl, web_app::WebAppUrlLoader::Result::kUrlLoaded);
-    uninstaller()->SetNextResultForTesting(kFooWebAppUrl, true);
+    install_finalizer()->SetNextUninstallExternalWebAppResult(kFooWebAppUrl,
+                                                              true);
 
     base::Optional<GURL> url;
     base::Optional<web_app::InstallResultCode> code;
@@ -1255,7 +1203,8 @@
     install_options.reinstall_placeholder = true;
     url_loader()->SetNextLoadUrlResult(
         kFooWebAppUrl, web_app::WebAppUrlLoader::Result::kUrlLoaded);
-    uninstaller()->SetNextResultForTesting(kFooWebAppUrl, false);
+    install_finalizer()->SetNextUninstallExternalWebAppResult(kFooWebAppUrl,
+                                                              false);
 
     base::Optional<GURL> url;
     base::Optional<web_app::InstallResultCode> code;
@@ -1298,7 +1247,8 @@
     install_options.reinstall_placeholder = true;
     url_loader()->SetNextLoadUrlResult(
         kFooWebAppUrl, web_app::WebAppUrlLoader::Result::kRedirectedUrlLoaded);
-    uninstaller()->SetNextResultForTesting(kFooWebAppUrl, true);
+    install_finalizer()->SetNextUninstallExternalWebAppResult(kFooWebAppUrl,
+                                                              true);
 
     base::Optional<GURL> url;
     base::Optional<web_app::InstallResultCode> code;
@@ -1346,7 +1296,8 @@
     ui_delegate()->SetNumWindowsForApp(GenerateFakeAppId(kFooWebAppUrl), 0);
     url_loader()->SetNextLoadUrlResult(
         kFooWebAppUrl, web_app::WebAppUrlLoader::Result::kUrlLoaded);
-    uninstaller()->SetNextResultForTesting(kFooWebAppUrl, true);
+    install_finalizer()->SetNextUninstallExternalWebAppResult(kFooWebAppUrl,
+                                                              true);
 
     base::Optional<GURL> url;
     base::Optional<web_app::InstallResultCode> code;
@@ -1391,7 +1342,8 @@
     ui_delegate()->SetNumWindowsForApp(GenerateFakeAppId(kFooWebAppUrl), 1);
     url_loader()->SetNextLoadUrlResult(
         kFooWebAppUrl, web_app::WebAppUrlLoader::Result::kUrlLoaded);
-    uninstaller()->SetNextResultForTesting(kFooWebAppUrl, true);
+    install_finalizer()->SetNextUninstallExternalWebAppResult(kFooWebAppUrl,
+                                                              true);
 
     base::Optional<GURL> url;
     base::Optional<web_app::InstallResultCode> code;
diff --git a/chrome/browser/web_applications/test/test_install_finalizer.cc b/chrome/browser/web_applications/test/test_install_finalizer.cc
index 8889f65..ddc52a2 100644
--- a/chrome/browser/web_applications/test/test_install_finalizer.cc
+++ b/chrome/browser/web_applications/test/test_install_finalizer.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/web_applications/test/test_install_finalizer.h"
 
 #include "base/callback.h"
+#include "base/test/bind_test_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
@@ -42,6 +43,24 @@
       FROM_HERE, base::BindOnce(std::move(callback), app_id, code));
 }
 
+void TestInstallFinalizer::UninstallExternalWebApp(
+    const GURL& app_url,
+    UninstallExternalWebAppCallback callback) {
+  DCHECK(base::ContainsKey(next_uninstall_external_web_app_results_, app_url));
+  uninstall_external_web_app_urls_.push_back(app_url);
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          base::BindLambdaForTesting(
+              [this, app_url](UninstallExternalWebAppCallback callback) {
+                bool result = next_uninstall_external_web_app_results_[app_url];
+                next_uninstall_external_web_app_results_.erase(app_url);
+                std::move(callback).Run(result);
+              }),
+          std::move(callback)));
+}
+
 bool TestInstallFinalizer::CanCreateOsShortcuts() const {
   return true;
 }
@@ -95,4 +114,11 @@
   next_result_code_ = code;
 }
 
+void TestInstallFinalizer::SetNextUninstallExternalWebAppResult(
+    const GURL& app_url,
+    bool uninstalled) {
+  DCHECK(!base::ContainsKey(next_uninstall_external_web_app_results_, app_url));
+  next_uninstall_external_web_app_results_[app_url] = uninstalled;
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/test/test_install_finalizer.h b/chrome/browser/web_applications/test/test_install_finalizer.h
index 0251eb5..ddff3d6 100644
--- a/chrome/browser/web_applications/test/test_install_finalizer.h
+++ b/chrome/browser/web_applications/test/test_install_finalizer.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_TEST_TEST_INSTALL_FINALIZER_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_TEST_TEST_INSTALL_FINALIZER_H_
 
+#include <map>
 #include <memory>
 
 #include "base/macros.h"
@@ -24,6 +25,9 @@
   void FinalizeInstall(const WebApplicationInfo& web_app_info,
                        const FinalizeOptions& options,
                        InstallFinalizedCallback callback) override;
+  void UninstallExternalWebApp(
+      const GURL& app_url,
+      UninstallExternalWebAppCallback callback) override;
   bool CanCreateOsShortcuts() const override;
   void CreateOsShortcuts(const AppId& app_id,
                          bool add_to_desktop,
@@ -42,6 +46,8 @@
 
   void SetNextFinalizeInstallResult(const AppId& app_id,
                                     InstallResultCode code);
+  void SetNextUninstallExternalWebAppResult(const GURL& app_url,
+                                            bool uninstalled);
 
   std::unique_ptr<WebApplicationInfo> web_app_info() {
     return std::move(web_app_info_copy_);
@@ -51,6 +57,10 @@
     return finalize_options_list_;
   }
 
+  const std::vector<GURL>& uninstall_external_web_app_urls() const {
+    return uninstall_external_web_app_urls_;
+  }
+
   int num_create_os_shortcuts_calls() { return num_create_os_shortcuts_calls_; }
   int num_reparent_tab_calls() { return num_reparent_tab_calls_; }
   int num_reveal_appshim_calls() { return num_reveal_appshim_calls_; }
@@ -59,9 +69,11 @@
  private:
   std::unique_ptr<WebApplicationInfo> web_app_info_copy_;
   std::vector<FinalizeOptions> finalize_options_list_;
+  std::vector<GURL> uninstall_external_web_app_urls_;
 
   base::Optional<AppId> next_app_id_;
   base::Optional<InstallResultCode> next_result_code_;
+  std::map<GURL, bool> next_uninstall_external_web_app_results_;
 
   int num_create_os_shortcuts_calls_ = 0;
   int num_reparent_tab_calls_ = 0;
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index 91da2b0..5bc64fbc 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -81,6 +81,12 @@
                      std::move(web_app)));
 }
 
+void WebAppInstallFinalizer::UninstallExternalWebApp(
+    const GURL& app_url,
+    UninstallExternalWebAppCallback callback) {
+  NOTIMPLEMENTED();
+}
+
 void WebAppInstallFinalizer::OnDataWritten(InstallFinalizedCallback callback,
                                            std::unique_ptr<WebApp> web_app,
                                            bool success) {
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.h b/chrome/browser/web_applications/web_app_install_finalizer.h
index e1a8bb4..06a450af 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.h
+++ b/chrome/browser/web_applications/web_app_install_finalizer.h
@@ -29,6 +29,9 @@
   void FinalizeInstall(const WebApplicationInfo& web_app_info,
                        const FinalizeOptions& options,
                        InstallFinalizedCallback callback) override;
+  void UninstallExternalWebApp(
+      const GURL& app_url,
+      UninstallExternalWebAppCallback callback) override;
   bool CanCreateOsShortcuts() const override;
   void CreateOsShortcuts(const AppId& app_id,
                          bool add_to_desktop,
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 3a710a6..0d6ed14 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -1603,7 +1603,8 @@
   // Imports Polyfill so that they will not require native imports. Return true
   // for only pages that have not been updated yet. See
   // https://crbug.com/937747.
-  bool canUsePolyfill = url.host() == chrome::kChromeUIExtensionsHost;
+  bool canUsePolyfill = url.host() == chrome::kChromeUIExtensionsHost ||
+                        url.host() == chrome::kChromeUIDownloadsHost;
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
   canUsePolyfill |= url.host() == chrome::kChromeUIPrintHost;
 #endif
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ApplicationData.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ApplicationData.java
index 53cf717..cc544f1 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ApplicationData.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ApplicationData.java
@@ -6,10 +6,10 @@
 
 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
 
-import android.annotation.SuppressLint;
 import android.content.Context;
 import android.support.v4.content.ContextCompat;
 
+import org.chromium.base.FileUtils;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 
@@ -42,17 +42,11 @@
     public static void clearAppData(final Context targetContext) {
         CriteriaHelper.pollInstrumentationThread(
                 new Criteria() {
-                    private boolean mDataRemoved;
-
-                    // The lint check for calling apply() rather than commit() on a shared pref
-                    // was recently renamed.
-                    @SuppressLint({"ApplySharedPref", "CommitPrefEdits"})
                     @Override
                     public boolean isSatisfied() {
-                        if (!mDataRemoved && !removeAppData(targetContext)) {
+                        if (!removeAppData(targetContext)) {
                             return false;
                         }
-                        mDataRemoved = true;
                         // We have to make sure the cache directory still exists, as the framework
                         // will try to create it otherwise and will fail for sandbox processes with
                         // a NullPointerException.
@@ -65,8 +59,6 @@
 
     /**
      * Remove all files and directories under the given application directory, except 'lib'.
-     *
-     * @return whether removal succeeded.
      */
     private static boolean removeAppData(final Context targetContext) {
         File dataDir = ContextCompat.getDataDir(targetContext);
@@ -90,7 +82,7 @@
                 removeSharedPrefs(file);
                 continue;
             }
-            if (!removeFile(file)) {
+            if (!FileUtils.recursivelyDeleteFile(file)) {
                 return false;
             }
         }
@@ -105,22 +97,4 @@
             }
         }
     }
-
-    /**
-     * Remove the given file or directory.
-     *
-     * @param file the file or directory to remove.
-     *
-     * @return whether removal succeeded.
-     */
-    private static boolean removeFile(File file) {
-        if (file.isDirectory()) {
-            File[] files = file.listFiles();
-            if (files == null) return true;
-            for (File sub_file : files) {
-                if (!removeFile(sub_file)) return false;
-            }
-        }
-        return file.delete();
-    }
 }
diff --git a/chrome/test/base/mojo_web_ui_browser_test.cc b/chrome/test/base/mojo_web_ui_browser_test.cc
index 764ff7e..d244a150 100644
--- a/chrome/test/base/mojo_web_ui_browser_test.cc
+++ b/chrome/test/base/mojo_web_ui_browser_test.cc
@@ -8,6 +8,7 @@
 #include "base/bind_helpers.h"
 #include "base/macros.h"
 #include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/web_ui_test_handler.h"
 #include "chrome/common/chrome_paths.h"
@@ -104,9 +105,22 @@
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   if (use_mojo_lite_bindings_) {
+    base::string16 file_content =
+        l10n_util::GetStringUTF16(IDR_WEB_UI_TEST_MOJO_LITE_JS);
+    // The generated script assumes that mojo has already been imported by the
+    // page. This is not the case when native HTML imports are disabled. If
+    // the polyfill is in place, wait for HTMLImports.whenReady().
+    base::string16 wrapped_file_content =
+        base::UTF8ToUTF16(
+            "const promise = typeof HTMLImports === 'undefined' ? "
+            "Promise.resolve() : "
+            "new Promise(resolve => { "
+            "HTMLImports.whenReady(resolve); "
+            "}); "
+            "promise.then(() => {") +
+        file_content + base::UTF8ToUTF16("});");
     web_contents->GetMainFrame()->ExecuteJavaScriptForTests(
-        l10n_util::GetStringUTF16(IDR_WEB_UI_TEST_MOJO_LITE_JS),
-        base::NullCallback());
+        wrapped_file_content, base::NullCallback());
   } else {
     web_contents->GetMainFrame()->ExecuteJavaScriptForTests(
         l10n_util::GetStringUTF16(IDR_WEB_UI_TEST_MOJO_JS),
diff --git a/chrome/test/data/media/picture-in-picture/window-size.html b/chrome/test/data/media/picture-in-picture/window-size.html
index 1443db9..50c954c 100644
--- a/chrome/test/data/media/picture-in-picture/window-size.html
+++ b/chrome/test/data/media/picture-in-picture/window-size.html
@@ -145,12 +145,5 @@
       .catch(e => { document.title = 'failed to enter Picture-in-Picture after leaving'; });
     });
   }
-
-  function resetVideo() {
-    video.addEventListener('emptied', () => {
-      document.title = 'emptied';
-    }, { once: true });
-    video.src = '';
-  }
 </script>
 </html>
diff --git a/chrome/test/data/webui/downloads/downloads_browsertest.js b/chrome/test/data/webui/downloads/downloads_browsertest.js
index ceb5557..5c082e7 100644
--- a/chrome/test/data/webui/downloads/downloads_browsertest.js
+++ b/chrome/test/data/webui/downloads/downloads_browsertest.js
@@ -23,6 +23,21 @@
   },
 
   /** @override */
+  loaderFile: 'subpage_loader.html',
+
+  // The name of the custom element under test. Should be overridden by
+  // subclasses that are loading the URL of a non-element.
+  get customElementName() {
+    const r = /chrome\:\/\/downloads\/([a-zA-Z-_]+)\.html/;
+    const result = r.exec(this.browsePreload);
+    if (!result || result.length < 1) {
+      // Loading the main page, so wait for downloads manager.
+      return 'downloads-manager';
+    }
+    return 'downloads-' + result[1].replace(/_/gi, '-');
+  },
+
+  /** @override */
   runAccessibilityChecks: true,
 };
 
@@ -110,6 +125,14 @@
 
   /** @override */
   browsePreload: 'chrome://downloads/a/b/',
+
+  /** @override */
+  loaderFile: '',
+
+  /** @override */
+  get customElementName() {
+    return null;
+  }
 };
 
 TEST_F('DownloadsUrlTest', 'All', function() {
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index f2c32b8..6ed0877 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -237,10 +237,6 @@
 // Disables per-user timezone.
 const char kDisablePerUserTimezone[] = "disable-per-user-timezone";
 
-// Disables suggestions while typing on a physical keyboard.
-const char kDisablePhysicalKeyboardAutocorrect[] =
-    "disable-physical-keyboard-autocorrect";
-
 // Disables rollback option on reset screen.
 const char kDisableRollbackOption[] = "disable-rollback-option";
 
@@ -295,10 +291,6 @@
 // Enables the marketing opt-in screen in OOBE.
 const char kEnableMarketingOptInScreen[] = "enable-market-opt-in";
 
-// Enables suggestions while typing on a physical keyboard.
-const char kEnablePhysicalKeyboardAutocorrect[] =
-    "enable-physical-keyboard-autocorrect";
-
 // Enables request of tablet site (via user agent override).
 const char kEnableRequestTabletSite[] = "enable-request-tablet-site";
 
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index d28d89d..e6a900b 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -85,8 +85,6 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDisableNewZIPUnpacker[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kDisablePerUserTimezone[];
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
-extern const char kDisablePhysicalKeyboardAutocorrect[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDisableRollbackOption[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kDisableSigninFrameClientCerts[];
@@ -112,8 +110,6 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kEnableMarketingOptInScreen[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
-extern const char kEnablePhysicalKeyboardAutocorrect[];
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kEnableRequestTabletSite[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kEnableTouchCalibrationSetting[];
diff --git a/components/components_strings.grd b/components/components_strings.grd
index 33c5baf..3b165b2 100644
--- a/components/components_strings.grd
+++ b/components/components_strings.grd
@@ -238,6 +238,9 @@
       <message name="IDS_CLOSE" desc="A generic term for Close on buttons and menus.">
         Close
       </message>
+      <message name="IDS_CLEAR" desc="A generic term for Clearing input fields.">
+        Clear
+      </message>
       <message name="IDS_DONE" desc="A generic term for Done on buttons and menus.">
         Done
       </message>
diff --git a/components/crash/content/browser/child_exit_observer_android.cc b/components/crash/content/browser/child_exit_observer_android.cc
index 595a52744..dd252440 100644
--- a/components/crash/content/browser/child_exit_observer_android.cc
+++ b/components/crash/content/browser/child_exit_observer_android.cc
@@ -38,6 +38,7 @@
       content_info.remaining_process_with_moderate_binding;
   info->remaining_process_with_waived_binding =
       content_info.remaining_process_with_waived_binding;
+  info->best_effort_reverse_rank = content_info.best_effort_reverse_rank;
   info->was_oom_protected_status =
       content_info.status == base::TERMINATION_STATUS_OOM_PROTECTED;
 }
diff --git a/components/crash/content/browser/child_exit_observer_android.h b/components/crash/content/browser/child_exit_observer_android.h
index 1059f0c4..ae292f6 100644
--- a/components/crash/content/browser/child_exit_observer_android.h
+++ b/components/crash/content/browser/child_exit_observer_android.h
@@ -70,6 +70,7 @@
     int remaining_process_with_strong_binding = 0;
     int remaining_process_with_moderate_binding = 0;
     int remaining_process_with_waived_binding = 0;
+    int best_effort_reverse_rank = -1;
 
     // Note this is slightly different |has_oom_protection_bindings|.
     // This is equivalent to status == TERMINATION_STATUS_NORMAL_TERMINATION,
diff --git a/components/crash/content/browser/crash_metrics_reporter_android.cc b/components/crash/content/browser/crash_metrics_reporter_android.cc
index 9ddeee9f..38af0400 100644
--- a/components/crash/content/browser/crash_metrics_reporter_android.cc
+++ b/components/crash/content/browser/crash_metrics_reporter_android.cc
@@ -287,6 +287,17 @@
         info.remaining_process_with_strong_binding, 20);
   }
 
+  if (android_oom_kill) {
+    if (info.best_effort_reverse_rank >= 0) {
+      UMA_HISTOGRAM_EXACT_LINEAR("Stability.Android.OomKillReverseRank",
+                                 info.best_effort_reverse_rank, 50);
+    }
+    if (info.best_effort_reverse_rank != -2) {
+      UMA_HISTOGRAM_BOOLEAN("Stability.Android.OomKillReverseRankSuccess",
+                            info.best_effort_reverse_rank != -1);
+    }
+  }
+
   ReportLegacyCrashUma(info, crashed);
   NotifyObservers(info.process_host_id, reported_counts);
 }
diff --git a/components/drive/chromeos/file_system.cc b/components/drive/chromeos/file_system.cc
index 39a57ed..e760eb8 100644
--- a/components/drive/chromeos/file_system.cc
+++ b/components/drive/chromeos/file_system.cc
@@ -953,7 +953,7 @@
   base::RepeatingClosure closure = base::BarrierClosure(
       num_callbacks,
       base::BindOnce(&FileSystem::OnGetMetadata, weak_ptr_factory_.GetWeakPtr(),
-                     base::Passed(std::move(callback)), base::Owned(metadata),
+                     std::move(callback), base::Owned(metadata),
                      base::Owned(team_drive_metadata)));
 
   metadata->refreshing = default_corpus_change_list_loader_->IsRefreshing();
diff --git a/components/drive/chromeos/file_system/get_file_for_saving_operation.cc b/components/drive/chromeos/file_system/get_file_for_saving_operation.cc
index 21d010367..3c2a7fe5 100644
--- a/components/drive/chromeos/file_system/get_file_for_saving_operation.cc
+++ b/components/drive/chromeos/file_system/get_file_for_saving_operation.cc
@@ -123,8 +123,8 @@
                      file_closer, entry_ptr),
       base::BindOnce(
           &GetFileForSavingOperation::GetFileForSavingAfterOpenForWrite,
-          weak_ptr_factory_.GetWeakPtr(), base::Passed(std::move(callback)),
-          cache_path, std::move(entry), base::Owned(file_closer)));
+          weak_ptr_factory_.GetWeakPtr(), std::move(callback), cache_path,
+          std::move(entry), base::Owned(file_closer)));
 }
 
 void GetFileForSavingOperation::GetFileForSavingAfterOpenForWrite(
diff --git a/components/exo/BUILD.gn b/components/exo/BUILD.gn
index 1bd0221..c36b09b 100644
--- a/components/exo/BUILD.gn
+++ b/components/exo/BUILD.gn
@@ -86,7 +86,6 @@
   if (is_chromeos) {
     deps += [
       "//ash/keyboard/ui",
-      "//ash/keyboard/ui:mojom",
       "//ash/public/cpp",
     ]
     sources += [
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index 2cd7455a..6d2fdaa 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -7,7 +7,6 @@
 #include <alpha-compositing-unstable-v1-server-protocol.h>
 #include <aura-shell-server-protocol.h>
 #include <cursor-shapes-unstable-v1-server-protocol.h>
-#include <gaming-input-unstable-v1-server-protocol.h>
 #include <gaming-input-unstable-v2-server-protocol.h>
 #include <grp.h>
 #include <input-timestamps-unstable-v1-server-protocol.h>
diff --git a/components/exo/wayland/zcr_gaming_input.cc b/components/exo/wayland/zcr_gaming_input.cc
index 5d5ba65f..ecdecc0 100644
--- a/components/exo/wayland/zcr_gaming_input.cc
+++ b/components/exo/wayland/zcr_gaming_input.cc
@@ -4,7 +4,6 @@
 
 #include "components/exo/wayland/zcr_gaming_input.h"
 
-#include <gaming-input-unstable-v1-server-protocol.h>
 #include <gaming-input-unstable-v2-server-protocol.h>
 #include <wayland-server-core.h>
 #include <wayland-server-protocol-core.h>
diff --git a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadCallable.java b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadCallable.java
index 2b43ebf..f7038cb3 100644
--- a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadCallable.java
+++ b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadCallable.java
@@ -8,6 +8,7 @@
 import android.support.annotation.IntDef;
 
 import org.chromium.base.ContextUtils;
+import org.chromium.base.FileUtils;
 import org.chromium.base.Log;
 import org.chromium.base.StreamUtil;
 import org.chromium.base.VisibleForTesting;
@@ -16,14 +17,11 @@
 import org.chromium.components.minidump_uploader.util.HttpURLConnectionFactoryImpl;
 
 import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.net.HttpURLConnection;
@@ -119,7 +117,8 @@
                 return MinidumpUploadStatus.FAILURE;
             }
             minidumpInputStream = new FileInputStream(mFileToUpload);
-            streamCopy(minidumpInputStream, new GZIPOutputStream(connection.getOutputStream()));
+            FileUtils.copyStream(
+                    minidumpInputStream, new GZIPOutputStream(connection.getOutputStream()));
             boolean success = handleExecutionResponse(connection);
 
             return success ? MinidumpUploadStatus.SUCCESS : MinidumpUploadStatus.FAILURE;
@@ -127,14 +126,11 @@
             // ArrayIndexOutOfBoundsException due to bad GZIPOutputStream implementation on some
             // old sony devices.
             // For now just log the stack trace.
-            Log.w(TAG, "Error while uploading " + mFileToUpload.getName(), e);
+            Log.w(TAG, "Error while uploading %s", mFileToUpload.getName(), e);
             return MinidumpUploadStatus.FAILURE;
         } finally {
             connection.disconnect();
-
-            if (minidumpInputStream != null) {
-                StreamUtil.closeQuietly(minidumpInputStream);
-            }
+            StreamUtil.closeQuietly(minidumpInputStream);
         }
     }
 
@@ -174,9 +170,9 @@
         if (isSuccessful(responseCode)) {
             String responseContent = getResponseContentAsString(connection);
             // The crash server returns the crash ID.
-            String uploadId = responseContent != null ? responseContent : "unknown";
+            String uploadId = responseContent.isEmpty() ? "unknown" : responseContent;
             String crashFileName = mFileToUpload.getName();
-            Log.i(TAG, "Minidump " + crashFileName + " uploaded successfully, id: " + uploadId);
+            Log.i(TAG, "Minidump %s uploaded successfully, id: %s", crashFileName, uploadId);
 
             // TODO(acleung): MinidumpUploadService is in charge of renaming while this class is
             // in charge of deleting. We should move all the file system operations into
@@ -284,33 +280,8 @@
      */
     private static String getResponseContentAsString(HttpURLConnection connection)
             throws IOException {
-        String responseContent = null;
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        streamCopy(connection.getInputStream(), baos);
-        if (baos.size() > 0) {
-            responseContent = baos.toString();
-        }
-        return responseContent;
-    }
-
-    /**
-     * Copies all available data from |inStream| to |outStream|. Closes both
-     * streams when done.
-     *
-     * @param inStream the stream to read
-     * @param outStream the stream to write to
-     * @throws IOException
-     */
-    private static void streamCopy(InputStream inStream, OutputStream outStream)
-            throws IOException {
-        byte[] temp = new byte[4096];
-        int bytesRead = inStream.read(temp);
-        while (bytesRead >= 0) {
-            outStream.write(temp, 0, bytesRead);
-            bytesRead = inStream.read(temp);
-        }
-        inStream.close();
-        outStream.close();
+        byte[] bytes = FileUtils.readStream(connection.getInputStream());
+        return new String(bytes);
     }
 
     // TODO(gayane): Remove this function and unused prefs in M51. crbug.com/555022
diff --git a/components/remote_cocoa/app_shim/BUILD.gn b/components/remote_cocoa/app_shim/BUILD.gn
index 72fa7f8..46cbd19 100644
--- a/components/remote_cocoa/app_shim/BUILD.gn
+++ b/components/remote_cocoa/app_shim/BUILD.gn
@@ -17,34 +17,34 @@
 
   configs += [ ":app_shim_warnings" ]
   sources = [
-    "//ui/views_bridge_mac/alert.h",
-    "//ui/views_bridge_mac/alert.mm",
-    "//ui/views_bridge_mac/bridge_factory_impl.h",
-    "//ui/views_bridge_mac/bridge_factory_impl.mm",
-    "//ui/views_bridge_mac/bridged_content_view.h",
-    "//ui/views_bridge_mac/bridged_content_view.mm",
-    "//ui/views_bridge_mac/bridged_content_view_touch_bar.mm",
-    "//ui/views_bridge_mac/bridged_native_widget_host_helper.h",
-    "//ui/views_bridge_mac/bridged_native_widget_impl.h",
-    "//ui/views_bridge_mac/bridged_native_widget_impl.mm",
-    "//ui/views_bridge_mac/browser_native_widget_window_mac.h",
-    "//ui/views_bridge_mac/browser_native_widget_window_mac.mm",
-    "//ui/views_bridge_mac/cocoa_mouse_capture.h",
-    "//ui/views_bridge_mac/cocoa_mouse_capture.mm",
-    "//ui/views_bridge_mac/cocoa_mouse_capture_delegate.h",
-    "//ui/views_bridge_mac/cocoa_window_move_loop.h",
-    "//ui/views_bridge_mac/cocoa_window_move_loop.mm",
-    "//ui/views_bridge_mac/drag_drop_client.h",
-    "//ui/views_bridge_mac/native_widget_mac_frameless_nswindow.h",
-    "//ui/views_bridge_mac/native_widget_mac_frameless_nswindow.mm",
-    "//ui/views_bridge_mac/native_widget_mac_nswindow.h",
-    "//ui/views_bridge_mac/native_widget_mac_nswindow.mm",
-    "//ui/views_bridge_mac/views_bridge_mac_export.h",
-    "//ui/views_bridge_mac/views_nswindow_delegate.h",
-    "//ui/views_bridge_mac/views_nswindow_delegate.mm",
-    "//ui/views_bridge_mac/views_scrollbar_bridge.h",
-    "//ui/views_bridge_mac/views_scrollbar_bridge.mm",
-    "//ui/views_bridge_mac/window_touch_bar_delegate.h",
+    "alert.h",
+    "alert.mm",
+    "bridge_factory_impl.h",
+    "bridge_factory_impl.mm",
+    "bridged_content_view.h",
+    "bridged_content_view.mm",
+    "bridged_content_view_touch_bar.mm",
+    "bridged_native_widget_host_helper.h",
+    "bridged_native_widget_impl.h",
+    "bridged_native_widget_impl.mm",
+    "browser_native_widget_window_mac.h",
+    "browser_native_widget_window_mac.mm",
+    "drag_drop_client.h",
+    "mouse_capture.h",
+    "mouse_capture.mm",
+    "mouse_capture_delegate.h",
+    "native_widget_mac_frameless_nswindow.h",
+    "native_widget_mac_frameless_nswindow.mm",
+    "native_widget_mac_nswindow.h",
+    "native_widget_mac_nswindow.mm",
+    "remote_cocoa_app_shim_export.h",
+    "views_nswindow_delegate.h",
+    "views_nswindow_delegate.mm",
+    "views_scrollbar_bridge.h",
+    "views_scrollbar_bridge.mm",
+    "window_move_loop.h",
+    "window_move_loop.mm",
+    "window_touch_bar_delegate.h",
   ]
   defines = [ "VIEWS_BRIDGE_MAC_IMPLEMENTATION" ]
   deps = [
diff --git a/ui/views_bridge_mac/alert.h b/components/remote_cocoa/app_shim/alert.h
similarity index 87%
rename from ui/views_bridge_mac/alert.h
rename to components/remote_cocoa/app_shim/alert.h
index c2f2205..0f0cf9d 100644
--- a/ui/views_bridge_mac/alert.h
+++ b/components/remote_cocoa/app_shim/alert.h
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_BRIDGE_MAC_ALERT_H_
-#define UI_VIEWS_BRIDGE_MAC_ALERT_H_
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_ALERT_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_ALERT_H_
 
 #import <Cocoa/Cocoa.h>
 
 #include "base/mac/scoped_nsobject.h"
+#include "components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h"
 #include "components/remote_cocoa/common/alert.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "ui/gfx/text_elider.h"
-#include "ui/views_bridge_mac/views_bridge_mac_export.h"
 
 @class AlertBridgeHelper;
 
@@ -19,7 +19,7 @@
 
 // Class that displays an NSAlert with associated UI as described by the mojo
 // AlertBridge interface.
-class VIEWS_BRIDGE_MAC_EXPORT AlertBridge
+class REMOTE_COCOA_APP_SHIM_EXPORT AlertBridge
     : public views_bridge_mac::mojom::AlertBridge {
  public:
   // Creates a new alert which controls its own lifetime. It will destroy itself
@@ -62,4 +62,4 @@
 
 }  // namespace views_bridge_mac
 
-#endif  // UI_VIEWS_BRIDGE_MAC_ALERT_H_
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_ALERT_H_
diff --git a/ui/views_bridge_mac/alert.mm b/components/remote_cocoa/app_shim/alert.mm
similarity index 99%
rename from ui/views_bridge_mac/alert.mm
rename to components/remote_cocoa/app_shim/alert.mm
index 9b7310a9..0169e235 100644
--- a/ui/views_bridge_mac/alert.mm
+++ b/components/remote_cocoa/app_shim/alert.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/views_bridge_mac/alert.h"
+#include "components/remote_cocoa/app_shim/alert.h"
 
 #include "base/bind.h"
 #include "base/i18n/rtl.h"
diff --git a/ui/views_bridge_mac/bridge_factory_impl.h b/components/remote_cocoa/app_shim/bridge_factory_impl.h
similarity index 72%
rename from ui/views_bridge_mac/bridge_factory_impl.h
rename to components/remote_cocoa/app_shim/bridge_factory_impl.h
index ca22fc4..18702bac7 100644
--- a/ui/views_bridge_mac/bridge_factory_impl.h
+++ b/components/remote_cocoa/app_shim/bridge_factory_impl.h
@@ -2,23 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_BRIDGE_MAC_BRIDGE_FACTORY_IMPL_H_
-#define UI_VIEWS_BRIDGE_MAC_BRIDGE_FACTORY_IMPL_H_
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_BRIDGE_FACTORY_IMPL_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_BRIDGE_FACTORY_IMPL_H_
 
+#include "components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h"
 #include "components/remote_cocoa/common/alert.mojom.h"
 #include "components/remote_cocoa/common/bridge_factory.mojom.h"
 #include "components/remote_cocoa/common/bridged_native_widget.mojom.h"
 #include "components/remote_cocoa/common/bridged_native_widget_host.mojom.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
-#include "ui/views_bridge_mac/views_bridge_mac_export.h"
 
-// TODO(ccameron): This file is to be moved to /ui/views_bridge_mac when
-// possible. For now, put it in the namespace of that path.
+// TODO(ccameron): This file is to be moved to /components/remote_cocoa/app_shim
+// when possible. For now, put it in the namespace of that path.
 namespace views_bridge_mac {
 
 // The factory that creates BridgedNativeWidget instances. This object is to
 // be instantiated in app shim processes.
-class VIEWS_BRIDGE_MAC_EXPORT BridgeFactoryImpl : public mojom::BridgeFactory {
+class REMOTE_COCOA_APP_SHIM_EXPORT BridgeFactoryImpl
+    : public mojom::BridgeFactory {
  public:
   static BridgeFactoryImpl* Get();
   void BindRequest(mojom::BridgeFactoryAssociatedRequest request);
@@ -41,4 +42,4 @@
 
 }  // namespace views_bridge_mac
 
-#endif  // UI_VIEWS_BRIDGE_MAC_BRIDGE_FACTORY_IMPL_H_
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_BRIDGE_FACTORY_IMPL_H_
diff --git a/ui/views_bridge_mac/bridge_factory_impl.mm b/components/remote_cocoa/app_shim/bridge_factory_impl.mm
similarity index 93%
rename from ui/views_bridge_mac/bridge_factory_impl.mm
rename to components/remote_cocoa/app_shim/bridge_factory_impl.mm
index 3f2a92c..bc9efda 100644
--- a/ui/views_bridge_mac/bridge_factory_impl.mm
+++ b/components/remote_cocoa/app_shim/bridge_factory_impl.mm
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/views_bridge_mac/bridge_factory_impl.h"
+#include "components/remote_cocoa/app_shim/bridge_factory_impl.h"
 
 #include "base/bind.h"
 #include "base/no_destructor.h"
+#include "components/remote_cocoa/app_shim/alert.h"
+#include "components/remote_cocoa/app_shim/bridged_native_widget_host_helper.h"
+#include "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
 #include "ui/base/cocoa/remote_accessibility_api.h"
-#include "ui/views_bridge_mac/alert.h"
-#include "ui/views_bridge_mac/bridged_native_widget_host_helper.h"
-#include "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 namespace views_bridge_mac {
 
diff --git a/ui/views_bridge_mac/bridged_content_view.h b/components/remote_cocoa/app_shim/bridged_content_view.h
similarity index 81%
rename from ui/views_bridge_mac/bridged_content_view.h
rename to components/remote_cocoa/app_shim/bridged_content_view.h
index ab25f8a..af42e05 100644
--- a/ui/views_bridge_mac/bridged_content_view.h
+++ b/components/remote_cocoa/app_shim/bridged_content_view.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_BRIDGE_MAC_BRIDGED_CONTENT_VIEW_H_
-#define UI_VIEWS_BRIDGE_MAC_BRIDGED_CONTENT_VIEW_H_
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_BRIDGED_CONTENT_VIEW_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_BRIDGED_CONTENT_VIEW_H_
 
 #import <Cocoa/Cocoa.h>
 
 #include "base/strings/string16.h"
+#include "components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h"
 #import "ui/base/cocoa/tool_tip_base_view.h"
 #import "ui/base/cocoa/tracking_area.h"
-#include "ui/views_bridge_mac/views_bridge_mac_export.h"
 
 namespace ui {
 class TextInputClient;
@@ -23,11 +23,11 @@
 // The NSView that sits as the root contentView of the NSWindow, whilst it has
 // a views::RootView present. Bridges requests from Cocoa to the hosted
 // views::View.
-VIEWS_BRIDGE_MAC_EXPORT
-@interface BridgedContentView : ToolTipBaseView<NSTextInputClient,
-                                                NSUserInterfaceValidations,
-                                                NSDraggingSource,
-                                                NSServicesMenuRequestor> {
+REMOTE_COCOA_APP_SHIM_EXPORT
+@interface BridgedContentView : ToolTipBaseView <NSTextInputClient,
+                                                 NSUserInterfaceValidations,
+                                                 NSDraggingSource,
+                                                 NSServicesMenuRequestor> {
  @private
   // Weak, reset by clearView.
   views::BridgedNativeWidgetImpl* bridge_;
@@ -83,4 +83,4 @@
 
 @end
 
-#endif  // UI_VIEWS_BRIDGE_MAC_BRIDGED_CONTENT_VIEW_H_
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_BRIDGED_CONTENT_VIEW_H_
diff --git a/ui/views_bridge_mac/bridged_content_view.mm b/components/remote_cocoa/app_shim/bridged_content_view.mm
similarity index 98%
rename from ui/views_bridge_mac/bridged_content_view.mm
rename to components/remote_cocoa/app_shim/bridged_content_view.mm
index c8082beb..53c71de 100644
--- a/ui/views_bridge_mac/bridged_content_view.mm
+++ b/components/remote_cocoa/app_shim/bridged_content_view.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ui/views_bridge_mac/bridged_content_view.h"
+#import "components/remote_cocoa/app_shim/bridged_content_view.h"
 
 #include "base/logging.h"
 #import "base/mac/foundation_util.h"
@@ -11,6 +11,9 @@
 #import "base/mac/sdk_forward_declarations.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/sys_string_conversions.h"
+#include "components/remote_cocoa/app_shim/bridged_native_widget_host_helper.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
+#import "components/remote_cocoa/app_shim/drag_drop_client.h"
 #include "components/remote_cocoa/common/bridged_native_widget_host.mojom.h"
 #import "ui/base/cocoa/appkit_utils.h"
 #include "ui/base/cocoa/cocoa_base_utils.h"
@@ -30,9 +33,6 @@
 #import "ui/gfx/mac/coordinate_conversion.h"
 #import "ui/gfx/path_mac.h"
 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
-#include "ui/views_bridge_mac/bridged_native_widget_host_helper.h"
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
-#import "ui/views_bridge_mac/drag_drop_client.h"
 
 namespace {
 
@@ -1248,8 +1248,8 @@
 }
 
 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
-  NSArray* objects =
-      [pboard readObjectsForClasses:@[ [NSString class] ] options:0];
+  NSArray* objects = [pboard readObjectsForClasses:@ [[NSString class]]
+      options:0];
   DCHECK([objects count] == 1);
   [self insertText:[objects lastObject]];
   return YES;
@@ -1468,7 +1468,7 @@
   if (!bridge_)
     return nil;
   return [bridge_->host_helper()->GetNativeViewAccessible()
-              accessibilityFocusedUIElement];
+      accessibilityFocusedUIElement];
 }
 
 @end
diff --git a/ui/views_bridge_mac/bridged_content_view_touch_bar.mm b/components/remote_cocoa/app_shim/bridged_content_view_touch_bar.mm
similarity index 94%
rename from ui/views_bridge_mac/bridged_content_view_touch_bar.mm
rename to components/remote_cocoa/app_shim/bridged_content_view_touch_bar.mm
index 5deafda..ebc9f2a 100644
--- a/ui/views_bridge_mac/bridged_content_view_touch_bar.mm
+++ b/components/remote_cocoa/app_shim/bridged_content_view_touch_bar.mm
@@ -6,10 +6,10 @@
 #import "base/mac/scoped_nsobject.h"
 #import "base/mac/sdk_forward_declarations.h"
 #include "base/strings/sys_string_conversions.h"
+#import "components/remote_cocoa/app_shim/bridged_content_view.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #include "components/remote_cocoa/common/bridged_native_widget_host.mojom.h"
 #import "ui/base/cocoa/touch_bar_forward_declarations.h"
-#import "ui/views_bridge_mac/bridged_content_view.h"
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 namespace {
 
@@ -20,7 +20,7 @@
 
 }  // namespace
 
-@interface BridgedContentView (TouchBarAdditions)<NSTouchBarDelegate>
+@interface BridgedContentView (TouchBarAdditions) <NSTouchBarDelegate>
 - (void)touchBarButtonAction:(id)sender;
 @end
 
diff --git a/ui/views_bridge_mac/bridged_native_widget_host_helper.h b/components/remote_cocoa/app_shim/bridged_native_widget_host_helper.h
similarity index 85%
rename from ui/views_bridge_mac/bridged_native_widget_host_helper.h
rename to components/remote_cocoa/app_shim/bridged_native_widget_host_helper.h
index cfb144c..c7e19c64 100644
--- a/ui/views_bridge_mac/bridged_native_widget_host_helper.h
+++ b/components/remote_cocoa/app_shim/bridged_native_widget_host_helper.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_BRIDGE_MAC_BRIDGED_NATIVE_WIDGET_HOST_HELPER_H_
-#define UI_VIEWS_BRIDGE_MAC_BRIDGED_NATIVE_WIDGET_HOST_HELPER_H_
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_BRIDGED_NATIVE_WIDGET_HOST_HELPER_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_BRIDGED_NATIVE_WIDGET_HOST_HELPER_H_
 
+#include "components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/events/event_utils.h"
 #include "ui/gfx/decorated_text.h"
 #include "ui/gfx/geometry/point.h"
-#include "ui/views_bridge_mac/views_bridge_mac_export.h"
 
 @class NSView;
 
@@ -25,7 +25,7 @@
 // This provides an easier-to-use interface than the mojo for selected
 // functions. It also is temporarily exposing functionality that is not yet
 // implemented over mojo.
-class VIEWS_BRIDGE_MAC_EXPORT BridgedNativeWidgetHostHelper {
+class REMOTE_COCOA_APP_SHIM_EXPORT BridgedNativeWidgetHostHelper {
  public:
   virtual ~BridgedNativeWidgetHostHelper() = default;
 
@@ -63,4 +63,4 @@
 
 }  // namespace views_bridge_mac
 
-#endif  // UI_VIEWS_BRIDGE_MAC_BRIDGED_NATIVE_WIDGET_HOST_HELPER_H_
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_BRIDGED_NATIVE_WIDGET_HOST_HELPER_H_
diff --git a/ui/views_bridge_mac/bridged_native_widget_impl.h b/components/remote_cocoa/app_shim/bridged_native_widget_impl.h
similarity index 96%
rename from ui/views_bridge_mac/bridged_native_widget_impl.h
rename to components/remote_cocoa/app_shim/bridged_native_widget_impl.h
index 57047cf..14d966bf3 100644
--- a/ui/views_bridge_mac/bridged_native_widget_impl.h
+++ b/components/remote_cocoa/app_shim/bridged_native_widget_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_BRIDGE_MAC_BRIDGED_NATIVE_WIDGET_IMPL_H_
-#define UI_VIEWS_BRIDGE_MAC_BRIDGED_NATIVE_WIDGET_IMPL_H_
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_BRIDGED_NATIVE_WIDGET_IMPL_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_BRIDGED_NATIVE_WIDGET_IMPL_H_
 
 #import <Cocoa/Cocoa.h>
 
@@ -12,6 +12,8 @@
 
 #import "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
+#import "components/remote_cocoa/app_shim/mouse_capture_delegate.h"
+#include "components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h"
 #include "components/remote_cocoa/common/bridged_native_widget.mojom.h"
 #include "components/remote_cocoa/common/text_input_host.mojom.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
@@ -21,8 +23,6 @@
 #include "ui/base/cocoa/ns_view_ids.h"
 #include "ui/base/ime/text_input_client.h"
 #include "ui/display/display_observer.h"
-#import "ui/views_bridge_mac/cocoa_mouse_capture_delegate.h"
-#include "ui/views_bridge_mac/views_bridge_mac_export.h"
 
 @class BridgedContentView;
 @class ModalShowAnimationWithLayer;
@@ -57,7 +57,7 @@
 // A bridge to an NSWindow managed by an instance of NativeWidgetMac or
 // DesktopNativeWidgetMac. Serves as a helper class to bridge requests from the
 // NativeWidgetMac to the Cocoa window. Behaves a bit like an aura::Window.
-class VIEWS_BRIDGE_MAC_EXPORT BridgedNativeWidgetImpl
+class REMOTE_COCOA_APP_SHIM_EXPORT BridgedNativeWidgetImpl
     : public views_bridge_mac::mojom::BridgedNativeWidget,
       public display::DisplayObserver,
       public ui::CATransactionCoordinator::PreCommitObserver,
@@ -376,4 +376,4 @@
 
 }  // namespace views
 
-#endif  // UI_VIEWS_BRIDGE_MAC_BRIDGED_NATIVE_WIDGET_IMPL_H_
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_BRIDGED_NATIVE_WIDGET_IMPL_H_
diff --git a/ui/views_bridge_mac/bridged_native_widget_impl.mm b/components/remote_cocoa/app_shim/bridged_native_widget_impl.mm
similarity index 97%
rename from ui/views_bridge_mac/bridged_native_widget_impl.mm
rename to components/remote_cocoa/app_shim/bridged_native_widget_impl.mm
index a8d39db..24142bf 100644
--- a/ui/views_bridge_mac/bridged_native_widget_impl.mm
+++ b/components/remote_cocoa/app_shim/bridged_native_widget_impl.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 
 #import <objc/runtime.h>
 #include <stddef.h>
@@ -18,6 +18,14 @@
 #include "base/no_destructor.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/sys_string_conversions.h"
+#import "components/remote_cocoa/app_shim/bridged_content_view.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_host_helper.h"
+#import "components/remote_cocoa/app_shim/browser_native_widget_window_mac.h"
+#import "components/remote_cocoa/app_shim/mouse_capture.h"
+#import "components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.h"
+#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
+#import "components/remote_cocoa/app_shim/views_nswindow_delegate.h"
+#import "components/remote_cocoa/app_shim/window_move_loop.h"
 #include "components/remote_cocoa/common/bridged_native_widget_host.mojom.h"
 #include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
 #include "ui/base/cocoa/cocoa_base_utils.h"
@@ -32,14 +40,6 @@
 #include "ui/gfx/geometry/dip_util.h"
 #import "ui/gfx/mac/coordinate_conversion.h"
 #import "ui/gfx/mac/nswindow_frame_controls.h"
-#import "ui/views_bridge_mac/bridged_content_view.h"
-#import "ui/views_bridge_mac/bridged_native_widget_host_helper.h"
-#import "ui/views_bridge_mac/browser_native_widget_window_mac.h"
-#import "ui/views_bridge_mac/cocoa_mouse_capture.h"
-#import "ui/views_bridge_mac/cocoa_window_move_loop.h"
-#import "ui/views_bridge_mac/native_widget_mac_frameless_nswindow.h"
-#import "ui/views_bridge_mac/native_widget_mac_nswindow.h"
-#import "ui/views_bridge_mac/views_nswindow_delegate.h"
 
 using views_bridge_mac::mojom::VisibilityTransition;
 using views_bridge_mac::mojom::WindowVisibilityState;
@@ -62,7 +62,7 @@
 
 // Self-owning animation delegate that starts a hide animation, then calls
 // -[NSWindow close] when the animation ends, releasing itself.
-@interface ViewsNSWindowCloseAnimator : NSObject<NSAnimationDelegate> {
+@interface ViewsNSWindowCloseAnimator : NSObject <NSAnimationDelegate> {
  @private
   base::scoped_nsobject<NSWindow> window_;
   base::scoped_nsobject<NSAnimation> animation_;
@@ -106,7 +106,7 @@
 // animation: in that case, the shadow is never invalidated so retains the
 // shadow calculated before a translate is applied.
 @interface ModalShowAnimationWithLayer
-    : ConstrainedWindowAnimationShow<NSAnimationDelegate>
+    : ConstrainedWindowAnimationShow <NSAnimationDelegate>
 @end
 
 @implementation ModalShowAnimationWithLayer {
@@ -533,8 +533,8 @@
                                                 const gfx::Rect& bounds) {
   DCHECK(!bridged_view_);
 
-  bridged_view_.reset(
-      [[BridgedContentView alloc] initWithBridge:this bounds:bounds]);
+  bridged_view_.reset([[BridgedContentView alloc] initWithBridge:this
+                                                          bounds:bounds]);
   bridged_view_id_mapping_ = std::make_unique<ui::ScopedNSViewIdMapping>(
       ns_view_id, bridged_view_.get());
 
diff --git a/components/remote_cocoa/app_shim/browser_native_widget_window_mac.h b/components/remote_cocoa/app_shim/browser_native_widget_window_mac.h
new file mode 100644
index 0000000..26045e9
--- /dev/null
+++ b/components/remote_cocoa/app_shim/browser_native_widget_window_mac.h
@@ -0,0 +1,13 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_BROWSER_NATIVE_WIDGET_WINDOW_MAC_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_BROWSER_NATIVE_WIDGET_WINDOW_MAC_H_
+
+#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
+
+@interface BrowserNativeWidgetWindow : NativeWidgetMacNSWindow
+@end
+
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_BROWSER_NATIVE_WIDGET_WINDOW_MAC_H_
diff --git a/ui/views_bridge_mac/browser_native_widget_window_mac.mm b/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm
similarity index 94%
rename from ui/views_bridge_mac/browser_native_widget_window_mac.mm
rename to components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm
index 1d5e9102..67727935 100644
--- a/ui/views_bridge_mac/browser_native_widget_window_mac.mm
+++ b/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ui/views_bridge_mac/browser_native_widget_window_mac.h"
+#import "components/remote_cocoa/app_shim/browser_native_widget_window_mac.h"
 
 #import <AppKit/AppKit.h>
 
+#include "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #include "components/remote_cocoa/common/bridged_native_widget_host.mojom.h"
-#include "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 @interface NSWindow (PrivateBrowserNativeWidgetAPI)
 + (Class)frameViewClassForStyleMask:(NSUInteger)windowStyle;
diff --git a/ui/views_bridge_mac/drag_drop_client.h b/components/remote_cocoa/app_shim/drag_drop_client.h
similarity index 75%
rename from ui/views_bridge_mac/drag_drop_client.h
rename to components/remote_cocoa/app_shim/drag_drop_client.h
index 3efa842..b725faa 100644
--- a/ui/views_bridge_mac/drag_drop_client.h
+++ b/components/remote_cocoa/app_shim/drag_drop_client.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_BRIDGE_MAC_DRAG_DROP_CLIENT_H_
-#define UI_VIEWS_BRIDGE_MAC_DRAG_DROP_CLIENT_H_
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_DRAG_DROP_CLIENT_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_DRAG_DROP_CLIENT_H_
 
 #import <Cocoa/Cocoa.h>
 
-#include "ui/views_bridge_mac/views_bridge_mac_export.h"
+#include "components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h"
 
 namespace views_bridge_mac {
 
@@ -15,7 +15,7 @@
 // DragDropClientMac in the browser process. This interface should eventually
 // become mojo-ified, but at the moment only passes raw pointers (consequently,
 // drag-drop behavior does not work in RemoteMacViews).
-class VIEWS_BRIDGE_MAC_EXPORT DragDropClient {
+class REMOTE_COCOA_APP_SHIM_EXPORT DragDropClient {
  public:
   virtual ~DragDropClient() {}
 
@@ -34,4 +34,4 @@
 
 }  // namespace views_bridge_mac
 
-#endif  // UI_VIEWS_BRIDGE_MAC_DRAG_DROP_CLIENT_H_
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_DRAG_DROP_CLIENT_H_
diff --git a/ui/views_bridge_mac/cocoa_mouse_capture.h b/components/remote_cocoa/app_shim/mouse_capture.h
similarity index 82%
rename from ui/views_bridge_mac/cocoa_mouse_capture.h
rename to components/remote_cocoa/app_shim/mouse_capture.h
index b7dedad..1742913 100644
--- a/ui/views_bridge_mac/cocoa_mouse_capture.h
+++ b/components/remote_cocoa/app_shim/mouse_capture.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_BRIDGE_MAC_COCOA_MOUSE_CAPTURE_H_
-#define UI_VIEWS_BRIDGE_MAC_COCOA_MOUSE_CAPTURE_H_
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_MOUSE_CAPTURE_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_MOUSE_CAPTURE_H_
 
 #include <memory>
 
 #include "base/macros.h"
-#include "ui/views_bridge_mac/views_bridge_mac_export.h"
+#include "components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h"
 
 @class NSWindow;
 
@@ -21,7 +21,7 @@
 // menu should dismiss the menu and "swallow" the mouse event. All events are
 // forwarded, but only events to the same application are "swallowed", which is
 // consistent with how native NSMenus behave.
-class VIEWS_BRIDGE_MAC_EXPORT CocoaMouseCapture {
+class REMOTE_COCOA_APP_SHIM_EXPORT CocoaMouseCapture {
  public:
   explicit CocoaMouseCapture(CocoaMouseCaptureDelegate* delegate);
   ~CocoaMouseCapture();
@@ -50,4 +50,4 @@
 
 }  // namespace views_bridge_mac
 
-#endif  // UI_VIEWS_BRIDGE_MAC_COCOA_MOUSE_CAPTURE_H_
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_MOUSE_CAPTURE_H_
diff --git a/ui/views_bridge_mac/cocoa_mouse_capture.mm b/components/remote_cocoa/app_shim/mouse_capture.mm
similarity index 96%
rename from ui/views_bridge_mac/cocoa_mouse_capture.mm
rename to components/remote_cocoa/app_shim/mouse_capture.mm
index 21f538b..a61a341 100644
--- a/ui/views_bridge_mac/cocoa_mouse_capture.mm
+++ b/components/remote_cocoa/app_shim/mouse_capture.mm
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ui/views_bridge_mac/cocoa_mouse_capture.h"
+#import "components/remote_cocoa/app_shim/mouse_capture.h"
 
 #import <Cocoa/Cocoa.h>
 
 #include "base/logging.h"
 #include "base/macros.h"
+#import "components/remote_cocoa/app_shim/mouse_capture_delegate.h"
 #include "ui/base/cocoa/weak_ptr_nsobject.h"
-#import "ui/views_bridge_mac/cocoa_mouse_capture_delegate.h"
 
 namespace views_bridge_mac {
 
diff --git a/ui/views_bridge_mac/cocoa_mouse_capture_delegate.h b/components/remote_cocoa/app_shim/mouse_capture_delegate.h
similarity index 80%
rename from ui/views_bridge_mac/cocoa_mouse_capture_delegate.h
rename to components/remote_cocoa/app_shim/mouse_capture_delegate.h
index 41093fa..89a1f297 100644
--- a/ui/views_bridge_mac/cocoa_mouse_capture_delegate.h
+++ b/components/remote_cocoa/app_shim/mouse_capture_delegate.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_BRIDGE_MAC_COCOA_MOUSE_CAPTURE_DELEGATE_H_
-#define UI_VIEWS_BRIDGE_MAC_COCOA_MOUSE_CAPTURE_DELEGATE_H_
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_MOUSE_CAPTURE_DELEGATE_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_MOUSE_CAPTURE_DELEGATE_H_
 
 @class NSEvent;
 @class NSWindow;
@@ -28,4 +28,4 @@
 
 }  // namespace views_bridge_mac
 
-#endif  // UI_VIEWS_BRIDGE_MAC_COCOA_MOUSE_CAPTURE_DELEGATE_H_
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_MOUSE_CAPTURE_DELEGATE_H_
diff --git a/ui/views_bridge_mac/native_widget_mac_frameless_nswindow.h b/components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.h
similarity index 61%
rename from ui/views_bridge_mac/native_widget_mac_frameless_nswindow.h
rename to components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.h
index afe3482..805f4ef 100644
--- a/ui/views_bridge_mac/native_widget_mac_frameless_nswindow.h
+++ b/components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_BRIDGE_MAC_NATIVE_WIDGET_MAC_FRAMELESS_NSWINDOW_H_
-#define UI_VIEWS_BRIDGE_MAC_NATIVE_WIDGET_MAC_FRAMELESS_NSWINDOW_H_
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_NATIVE_WIDGET_MAC_FRAMELESS_NSWINDOW_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_NATIVE_WIDGET_MAC_FRAMELESS_NSWINDOW_H_
 
-#import "ui/views_bridge_mac/native_widget_mac_nswindow.h"
+#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
 
 // Overrides contentRect <-> frameRect conversion methods to keep them equal to
 // each other, even for windows that do not use NSBorderlessWindowMask. This
@@ -14,4 +14,4 @@
 @interface NativeWidgetMacFramelessNSWindow : NativeWidgetMacNSWindow
 @end
 
-#endif  // UI_VIEWS_BRIDGE_MAC_NATIVE_WIDGET_MAC_FRAMELESS_NSWINDOW_H_
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_NATIVE_WIDGET_MAC_FRAMELESS_NSWINDOW_H_
diff --git a/ui/views_bridge_mac/native_widget_mac_frameless_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.mm
similarity index 89%
rename from ui/views_bridge_mac/native_widget_mac_frameless_nswindow.mm
rename to components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.mm
index 583003b9..8416c7c 100644
--- a/ui/views_bridge_mac/native_widget_mac_frameless_nswindow.mm
+++ b/components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ui/views_bridge_mac/native_widget_mac_frameless_nswindow.h"
+#import "components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.h"
 
 @interface NSWindow (PrivateAPI)
 + (Class)frameViewClassForStyleMask:(NSUInteger)windowStyle;
diff --git a/ui/views_bridge_mac/native_widget_mac_nswindow.h b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
similarity index 80%
rename from ui/views_bridge_mac/native_widget_mac_nswindow.h
rename to components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
index 04636461..5b481e48 100644
--- a/ui/views_bridge_mac/native_widget_mac_nswindow.h
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_BRIDGE_MAC_NATIVE_WIDGET_MAC_NSWINDOW_H_
-#define UI_VIEWS_BRIDGE_MAC_NATIVE_WIDGET_MAC_NSWINDOW_H_
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_NATIVE_WIDGET_MAC_NSWINDOW_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_NATIVE_WIDGET_MAC_NSWINDOW_H_
 
 #import <Cocoa/Cocoa.h>
 
 #include "base/mac/foundation_util.h"
+#include "components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h"
 #import "ui/base/cocoa/command_dispatcher.h"
-#include "ui/views_bridge_mac/views_bridge_mac_export.h"
 
 namespace views {
 class BridgedNativeWidgetImpl;
@@ -26,18 +26,18 @@
 @interface NSThemeFrame : NSView
 @end
 
-VIEWS_BRIDGE_MAC_EXPORT
+REMOTE_COCOA_APP_SHIM_EXPORT
 @interface NativeWidgetMacNSWindowBorderlessFrame : NSNextStepFrame
 @end
 
-VIEWS_BRIDGE_MAC_EXPORT
+REMOTE_COCOA_APP_SHIM_EXPORT
 @interface NativeWidgetMacNSWindowTitledFrame : NSThemeFrame
 @end
 
 // The NSWindow used by BridgedNativeWidget. Provides hooks into AppKit that
 // can only be accomplished by overriding methods.
-VIEWS_BRIDGE_MAC_EXPORT
-@interface NativeWidgetMacNSWindow : NSWindow<CommandDispatchingWindow>
+REMOTE_COCOA_APP_SHIM_EXPORT
+@interface NativeWidgetMacNSWindow : NSWindow <CommandDispatchingWindow>
 
 // Set a CommandDispatcherDelegate, i.e. to implement key event handling.
 - (void)setCommandDispatcherDelegate:(id<CommandDispatcherDelegate>)delegate;
@@ -61,4 +61,4 @@
 @property(assign, nonatomic) views::BridgedNativeWidgetImpl* bridgeImpl;
 @end
 
-#endif  // UI_VIEWS_BRIDGE_MAC_NATIVE_WIDGET_MAC_NSWINDOW_H_
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_NATIVE_WIDGET_MAC_NSWINDOW_H_
diff --git a/ui/views_bridge_mac/native_widget_mac_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
similarity index 96%
rename from ui/views_bridge_mac/native_widget_mac_nswindow.mm
rename to components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
index ef634ee..aa2bb3a 100644
--- a/ui/views_bridge_mac/native_widget_mac_nswindow.mm
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ui/views_bridge_mac/native_widget_mac_nswindow.h"
+#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
 
 #include "base/mac/foundation_util.h"
 #import "base/mac/sdk_forward_declarations.h"
+#include "components/remote_cocoa/app_shim/bridged_native_widget_host_helper.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
+#import "components/remote_cocoa/app_shim/views_nswindow_delegate.h"
+#import "components/remote_cocoa/app_shim/window_touch_bar_delegate.h"
 #include "components/remote_cocoa/common/bridged_native_widget_host.mojom.h"
 #import "ui/base/cocoa/user_interface_item_command_handler.h"
 #import "ui/base/cocoa/window_size_constants.h"
-#include "ui/views_bridge_mac/bridged_native_widget_host_helper.h"
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
-#import "ui/views_bridge_mac/views_nswindow_delegate.h"
-#import "ui/views_bridge_mac/window_touch_bar_delegate.h"
 
 @interface NSWindow (Private)
 + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle;
diff --git a/components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h b/components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h
new file mode 100644
index 0000000..ccc66f2
--- /dev/null
+++ b/components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h
@@ -0,0 +1,32 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_REMOTE_COCOA_APP_SHIM_EXPORT_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_REMOTE_COCOA_APP_SHIM_EXPORT_H_
+
+// Defines REMOTE_COCOA_APP_SHIM_EXPORT so that functionality implemented by the
+// RemoteMacViews module can be exported to consumers.
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(VIEWS_BRIDGE_MAC_IMPLEMENTATION)
+#define REMOTE_COCOA_APP_SHIM_EXPORT __declspec(dllexport)
+#else
+#define REMOTE_COCOA_APP_SHIM_EXPORT __declspec(dllimport)
+#endif  // defined(VIEWS_BRIDGE_MAC_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+#if defined(VIEWS_BRIDGE_MAC_IMPLEMENTATION)
+#define REMOTE_COCOA_APP_SHIM_EXPORT __attribute__((visibility("default")))
+#else
+#define REMOTE_COCOA_APP_SHIM_EXPORT
+#endif
+#endif
+
+#else  // defined(COMPONENT_BUILD)
+#define REMOTE_COCOA_APP_SHIM_EXPORT
+#endif
+
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_REMOTE_COCOA_APP_SHIM_EXPORT_H_
diff --git a/ui/views_bridge_mac/views_nswindow_delegate.h b/components/remote_cocoa/app_shim/views_nswindow_delegate.h
similarity index 79%
rename from ui/views_bridge_mac/views_nswindow_delegate.h
rename to components/remote_cocoa/app_shim/views_nswindow_delegate.h
index 247ee4a..29761e4 100644
--- a/ui/views_bridge_mac/views_nswindow_delegate.h
+++ b/components/remote_cocoa/app_shim/views_nswindow_delegate.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_BRIDGE_MAC_VIEWS_NSWINDOW_DELEGATE_H_
-#define UI_VIEWS_BRIDGE_MAC_VIEWS_NSWINDOW_DELEGATE_H_
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_VIEWS_NSWINDOW_DELEGATE_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_VIEWS_NSWINDOW_DELEGATE_H_
 
 #import <Cocoa/Cocoa.h>
 
 #import "base/mac/scoped_nsobject.h"
-#include "ui/views_bridge_mac/views_bridge_mac_export.h"
+#include "components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h"
 
 namespace views {
 class BridgedNativeWidgetImpl;
@@ -16,8 +16,8 @@
 
 // The delegate set on the NSWindow when a views::BridgedNativeWidgetImpl is
 // initialized.
-VIEWS_BRIDGE_MAC_EXPORT
-@interface ViewsNSWindowDelegate : NSObject<NSWindowDelegate> {
+REMOTE_COCOA_APP_SHIM_EXPORT
+@interface ViewsNSWindowDelegate : NSObject <NSWindowDelegate> {
  @private
   views::BridgedNativeWidgetImpl* parent_;  // Weak. Owns this.
   base::scoped_nsobject<NSCursor> cursor_;
@@ -48,4 +48,4 @@
 
 @end
 
-#endif  // UI_VIEWS_BRIDGE_MAC_VIEWS_NSWINDOW_DELEGATE_H_
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_VIEWS_NSWINDOW_DELEGATE_H_
diff --git a/ui/views_bridge_mac/views_nswindow_delegate.mm b/components/remote_cocoa/app_shim/views_nswindow_delegate.mm
similarity index 94%
rename from ui/views_bridge_mac/views_nswindow_delegate.mm
rename to components/remote_cocoa/app_shim/views_nswindow_delegate.mm
index 3fbbec3..39e05b8 100644
--- a/ui/views_bridge_mac/views_nswindow_delegate.mm
+++ b/components/remote_cocoa/app_shim/views_nswindow_delegate.mm
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ui/views_bridge_mac/views_nswindow_delegate.h"
+#import "components/remote_cocoa/app_shim/views_nswindow_delegate.h"
 
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/mac/mac_util.h"
 #include "base/threading/thread_task_runner_handle.h"
+#import "components/remote_cocoa/app_shim/bridged_content_view.h"
+#include "components/remote_cocoa/app_shim/bridged_native_widget_host_helper.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #include "components/remote_cocoa/common/bridged_native_widget_host.mojom.h"
-#import "ui/views_bridge_mac/bridged_content_view.h"
-#include "ui/views_bridge_mac/bridged_native_widget_host_helper.h"
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 @implementation ViewsNSWindowDelegate
 
@@ -62,7 +62,7 @@
         break;
       currentWindow = parentWindow;
       if ([currentWindow isKeyWindow]) {
-        [(newCursor ? newCursor : [NSCursor arrowCursor])set];
+        [(newCursor ? newCursor : [NSCursor arrowCursor]) set];
         break;
       }
     }
@@ -211,8 +211,7 @@
   int32_t sheetPositionY = 0;
   parent_->host()->GetSheetOffsetY(&sheetPositionY);
   NSView* view = [window contentView];
-  NSPoint pointInView =
-      NSMakePoint(0, NSMaxY([view bounds]) - sheetPositionY);
+  NSPoint pointInView = NSMakePoint(0, NSMaxY([view bounds]) - sheetPositionY);
   NSPoint pointInWindow = [view convertPoint:pointInView toView:nil];
 
   // As per NSWindowDelegate documentation, the origin indicates the top left
diff --git a/ui/views_bridge_mac/views_scrollbar_bridge.h b/components/remote_cocoa/app_shim/views_scrollbar_bridge.h
similarity index 77%
rename from ui/views_bridge_mac/views_scrollbar_bridge.h
rename to components/remote_cocoa/app_shim/views_scrollbar_bridge.h
index 11d4fdb..a58f7fe 100644
--- a/ui/views_bridge_mac/views_scrollbar_bridge.h
+++ b/components/remote_cocoa/app_shim/views_scrollbar_bridge.h
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_BRIDGE_MAC_VIEWS_SCROLLBAR_BRIDGE_H_
-#define UI_VIEWS_BRIDGE_MAC_VIEWS_SCROLLBAR_BRIDGE_H_
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_VIEWS_SCROLLBAR_BRIDGE_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_VIEWS_SCROLLBAR_BRIDGE_H_
 
 #import <Cocoa/Cocoa.h>
 
 #import "base/mac/scoped_nsobject.h"
-#include "ui/views_bridge_mac/views_bridge_mac_export.h"
+#include "components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h"
 
 // The delegate set to ViewsScrollbarBridge.
-class VIEWS_BRIDGE_MAC_EXPORT ViewsScrollbarBridgeDelegate {
+class REMOTE_COCOA_APP_SHIM_EXPORT ViewsScrollbarBridgeDelegate {
  public:
   // Invoked by ViewsScrollbarBridge when the system informs the process that
   // the preferred scroller style has changed
@@ -20,7 +20,7 @@
 
 // A bridge to NSScroller managed by NativeCocoaScrollbar. Serves as a helper
 // class to bridge NSScroller notifications and functions to CocoaScrollbar.
-VIEWS_BRIDGE_MAC_EXPORT
+REMOTE_COCOA_APP_SHIM_EXPORT
 @interface ViewsScrollbarBridge : NSObject {
  @private
   ViewsScrollbarBridgeDelegate* delegate_;  // Weak. Owns this.
diff --git a/ui/views_bridge_mac/views_scrollbar_bridge.mm b/components/remote_cocoa/app_shim/views_scrollbar_bridge.mm
similarity index 94%
rename from ui/views_bridge_mac/views_scrollbar_bridge.mm
rename to components/remote_cocoa/app_shim/views_scrollbar_bridge.mm
index 2e7eea3..6efbd9d 100644
--- a/ui/views_bridge_mac/views_scrollbar_bridge.mm
+++ b/components/remote_cocoa/app_shim/views_scrollbar_bridge.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ui/views_bridge_mac/views_scrollbar_bridge.h"
+#import "components/remote_cocoa/app_shim/views_scrollbar_bridge.h"
 
 #import "base/mac/sdk_forward_declarations.h"
 
diff --git a/ui/views_bridge_mac/cocoa_window_move_loop.h b/components/remote_cocoa/app_shim/window_move_loop.h
similarity index 87%
rename from ui/views_bridge_mac/cocoa_window_move_loop.h
rename to components/remote_cocoa/app_shim/window_move_loop.h
index ff074d8..5f50adf 100644
--- a/ui/views_bridge_mac/cocoa_window_move_loop.h
+++ b/components/remote_cocoa/app_shim/window_move_loop.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_BRIDGE_MAC_COCOA_WINDOW_MOVE_LOOP_H_
-#define UI_VIEWS_BRIDGE_MAC_COCOA_WINDOW_MOVE_LOOP_H_
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_WINDOW_MOVE_LOOP_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_WINDOW_MOVE_LOOP_H_
 
 #import <Cocoa/Cocoa.h>
 
@@ -50,4 +50,4 @@
 
 }  // namespace views
 
-#endif  // UI_VIEWS_BRIDGE_MAC_COCOA_WINDOW_MOVE_LOOP_H_
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_WINDOW_MOVE_LOOP_H_
diff --git a/ui/views_bridge_mac/cocoa_window_move_loop.mm b/components/remote_cocoa/app_shim/window_move_loop.mm
similarity index 93%
rename from ui/views_bridge_mac/cocoa_window_move_loop.mm
rename to components/remote_cocoa/app_shim/window_move_loop.mm
index f35e625..61159a8 100644
--- a/ui/views_bridge_mac/cocoa_window_move_loop.mm
+++ b/components/remote_cocoa/app_shim/window_move_loop.mm
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/views_bridge_mac/cocoa_window_move_loop.h"
+#include "components/remote_cocoa/app_shim/window_move_loop.h"
 
 #include "base/debug/stack_trace.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "components/crash/core/common/crash_key.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #include "ui/display/screen.h"
 #import "ui/gfx/mac/coordinate_conversion.h"
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 // When event monitors process the events the full list of monitors is cached,
 // and if we unregister the event monitor that's at the end of the list while
@@ -113,8 +113,8 @@
     strong->quit_closure_.Run();
     return event;  // Process the MouseUp.
   };
-  id monitor =
-      [NSEvent addLocalMonitorForEventsMatchingMask:mask handler:handler];
+  id monitor = [NSEvent addLocalMonitorForEventsMatchingMask:mask
+                                                     handler:handler];
 
   run_loop.Run();
   [NSEvent removeMonitor:monitor];
diff --git a/ui/views_bridge_mac/window_touch_bar_delegate.h b/components/remote_cocoa/app_shim/window_touch_bar_delegate.h
similarity index 62%
rename from ui/views_bridge_mac/window_touch_bar_delegate.h
rename to components/remote_cocoa/app_shim/window_touch_bar_delegate.h
index 9de8671..d222c524 100644
--- a/ui/views_bridge_mac/window_touch_bar_delegate.h
+++ b/components/remote_cocoa/app_shim/window_touch_bar_delegate.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_BRIDGE_MAC_WINDOW_TOUCH_BAR_DELEGATE_H_
-#define UI_VIEWS_BRIDGE_MAC_WINDOW_TOUCH_BAR_DELEGATE_H_
+#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_WINDOW_TOUCH_BAR_DELEGATE_H_
+#define COMPONENTS_REMOTE_COCOA_APP_SHIM_WINDOW_TOUCH_BAR_DELEGATE_H_
 
 #import <Cocoa/Cocoa.h>
 
@@ -11,11 +11,11 @@
 
 // Bridge delegate class for NativeWidgetMacNSWindow and
 // BrowserWindowTouchBarMac.
-@protocol WindowTouchBarDelegate<NSObject>
+@protocol WindowTouchBarDelegate <NSObject>
 
 // Creates and returns a touch bar for the browser window.
 - (NSTouchBar*)makeTouchBar API_AVAILABLE(macos(10.12.2));
 
 @end
 
-#endif  // UI_VIEWS_BRIDGE_MAC_WINDOW_TOUCH_BAR_DELEGATE_H_
+#endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_WINDOW_TOUCH_BAR_DELEGATE_H_
diff --git a/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java b/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java
index 01054b7..12314097 100644
--- a/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java
+++ b/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java
@@ -9,13 +9,13 @@
 import android.support.annotation.IntDef;
 
 import org.chromium.base.ContextUtils;
+import org.chromium.base.FileUtils;
 import org.chromium.base.Log;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.CachedMetrics.SparseHistogramSample;
 import org.chromium.base.metrics.CachedMetrics.TimesHistogramSample;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.annotation.Retention;
@@ -47,7 +47,6 @@
     private static final String VARIATIONS_SERVER_URL =
             "https://clientservices.googleapis.com/chrome-variations/seed?osname=";
 
-    private static final int BUFFER_SIZE = 4096;
     private static final int READ_TIMEOUT = 3000; // time in ms
     private static final int REQUEST_TIMEOUT = 1000; // time in ms
 
@@ -272,22 +271,6 @@
         }
     }
 
-    /**
-     * Convert a input stream into a byte array.
-     * @param inputStream the input stream
-     * @return the byte array which holds the data from the input stream
-     * @throws IOException if I/O error occurs when reading data from the input stream
-     */
-    public static byte[] convertInputStreamToByteArray(InputStream inputStream) throws IOException {
-        ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
-        byte[] buffer = new byte[BUFFER_SIZE];
-        int charactersReadCount = 0;
-        while ((charactersReadCount = inputStream.read(buffer)) != -1) {
-            byteBuffer.write(buffer, 0, charactersReadCount);
-        }
-        return byteBuffer.toByteArray();
-    }
-
     private String getHeaderFieldOrEmpty(HttpURLConnection connection, String name) {
         String headerField = connection.getHeaderField(name);
         if (headerField == null) {
@@ -300,7 +283,7 @@
         InputStream inputStream = null;
         try {
             inputStream = connection.getInputStream();
-            return convertInputStreamToByteArray(inputStream);
+            return FileUtils.readStream(inputStream);
         } finally {
             if (inputStream != null) {
                 inputStream.close();
diff --git a/content/browser/child_process_launcher_helper_android.cc b/content/browser/child_process_launcher_helper_android.cc
index 50f5ee1..bed56cf 100644
--- a/content/browser/child_process_launcher_helper_android.cc
+++ b/content/browser/child_process_launcher_helper_android.cc
@@ -190,7 +190,8 @@
     jboolean clean_exit,
     jint remaining_process_with_strong_binding,
     jint remaining_process_with_moderate_binding,
-    jint remaining_process_with_waived_binding) {
+    jint remaining_process_with_waived_binding,
+    jint reverse_rank) {
   ChildProcessTerminationInfo* info =
       reinterpret_cast<ChildProcessTerminationInfo*>(termination_info_ptr);
   info->binding_state =
@@ -203,6 +204,7 @@
       remaining_process_with_moderate_binding;
   info->remaining_process_with_waived_binding =
       remaining_process_with_waived_binding;
+  info->best_effort_reverse_rank = reverse_rank;
 }
 
 // static
diff --git a/content/browser/media/media_web_contents_observer.cc b/content/browser/media/media_web_contents_observer.cc
index 5c029100..b5dfc64 100644
--- a/content/browser/media/media_web_contents_observer.cc
+++ b/content/browser/media/media_web_contents_observer.cc
@@ -9,8 +9,10 @@
 #include "build/build_config.h"
 #include "content/browser/media/audible_metrics.h"
 #include "content/browser/media/audio_stream_monitor.h"
+#include "content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/media/media_player_delegate_messages.h"
+#include "content/public/browser/picture_in_picture_window_controller.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "ipc/ipc_message_macros.h"
diff --git a/content/browser/picture_in_picture/picture_in_picture_session.cc b/content/browser/picture_in_picture/picture_in_picture_session.cc
index 0e6c05f..641f514e2 100644
--- a/content/browser/picture_in_picture/picture_in_picture_session.cc
+++ b/content/browser/picture_in_picture/picture_in_picture_session.cc
@@ -7,6 +7,7 @@
 #include "content/browser/picture_in_picture/picture_in_picture_service_impl.h"
 #include "content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/media/media_player_delegate_messages.h"
 
 namespace content {
 
@@ -76,7 +77,7 @@
   if (is_stopping_)
     return;
 
-  StopInternal(base::NullCallback());
+  StopInternal(base::DoNothing());
 }
 
 void PictureInPictureSession::StopInternal(StopCallback callback) {
@@ -86,12 +87,13 @@
 
   GetWebContentsImpl()->ExitPictureInPicture();
 
-  // `OnStopped()` should only be called if there is no callback to run, as a
-  // contract in the API.
-  if (callback)
-    std::move(callback).Run();
-  else
-    observer_->OnStopped();
+  std::move(callback).Run();
+
+  // TODO(mlamouri): move to observer_->Stop();
+  player_id_->render_frame_host->Send(
+      new MediaPlayerDelegateMsg_EndPictureInPictureMode(
+          player_id_->render_frame_host->GetRoutingID(),
+          player_id_->delegate_id));
 
   if (auto* controller = GetController())
     controller->SetActiveSession(nullptr);
@@ -102,7 +104,7 @@
 
 void PictureInPictureSession::OnConnectionError() {
   // StopInternal() will self destruct which will close the bindings.
-  StopInternal(base::NullCallback());
+  StopInternal(base::DoNothing());
 }
 
 WebContentsImpl* PictureInPictureSession::GetWebContentsImpl() {
diff --git a/content/browser/service_worker/fake_service_worker.cc b/content/browser/service_worker/fake_service_worker.cc
index bbc0a43..12ea329 100644
--- a/content/browser/service_worker/fake_service_worker.cc
+++ b/content/browser/service_worker/fake_service_worker.cc
@@ -150,7 +150,7 @@
 void FakeServiceWorker::DispatchPeriodicSyncEvent(
     const std::string& tag,
     base::TimeDelta timeout,
-    DispatchSyncEventCallback callback) {
+    DispatchPeriodicSyncEventCallback callback) {
   NOTIMPLEMENTED();
 }
 
diff --git a/content/browser/service_worker/fake_service_worker.h b/content/browser/service_worker/fake_service_worker.h
index e565c4f..ed00f5e1 100644
--- a/content/browser/service_worker/fake_service_worker.h
+++ b/content/browser/service_worker/fake_service_worker.h
@@ -86,9 +86,10 @@
                          bool last_chance,
                          base::TimeDelta timeout,
                          DispatchSyncEventCallback callback) override;
-  void DispatchPeriodicSyncEvent(const std::string& tag,
-                                 base::TimeDelta timeout,
-                                 DispatchSyncEventCallback callback) override;
+  void DispatchPeriodicSyncEvent(
+      const std::string& tag,
+      base::TimeDelta timeout,
+      DispatchPeriodicSyncEventCallback callback) override;
   void DispatchAbortPaymentEvent(
       payments::mojom::PaymentHandlerResponseCallbackPtr response_callback,
       DispatchAbortPaymentEventCallback callback) override;
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 34d96aa8..7a09b30 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -50,7 +50,6 @@
 #include "third_party/blink/public/resources/grit/blink_resources.h"
 #include "third_party/zlib/google/compression_utils.h"
 #include "ui/base/layout.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/events/gestures/blink/web_gesture_curve_impl.h"
 
 #if defined(OS_ANDROID)
@@ -78,8 +77,6 @@
 #if defined(OS_ANDROID)
   return std::make_unique<WebThemeEngineAndroid>();
 #elif defined(OS_MACOSX)
-  if (features::IsFormControlsRefreshEnabled())
-    return std::make_unique<WebThemeEngineDefault>();
   return std::make_unique<WebThemeEngineMac>();
 #else
   return std::make_unique<WebThemeEngineDefault>();
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 160d89d..b6a8d8a 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -23,7 +23,6 @@
 #include "services/network/public/cpp/features.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/events/blink/blink_features.h"
 #include "ui/gfx/switches.h"
 #include "ui/gl/gl_switches.h"
@@ -502,9 +501,6 @@
 
   WebRuntimeFeatures::EnableDisplayLocking(
       base::FeatureList::IsEnabled(blink::features::kDisplayLocking));
-
-  WebRuntimeFeatures::EnableFormControlsRefresh(
-      features::IsFormControlsRefreshEnabled());
 }
 
 }  // namespace
diff --git a/content/common/media/media_player_delegate_messages.h b/content/common/media/media_player_delegate_messages.h
index e9219e9..706adc9 100644
--- a/content/common/media/media_player_delegate_messages.h
+++ b/content/common/media/media_player_delegate_messages.h
@@ -13,6 +13,7 @@
 
 #include <stdint.h>
 
+#include "components/viz/common/surfaces/surface_id.h"
 #include "content/common/content_export.h"
 #include "ipc/ipc_message_macros.h"
 #include "media/base/media_content_type.h"
@@ -60,6 +61,9 @@
                     int /* delegate_id, distinguishes instances */,
                     double /* is_persistent */)
 
+IPC_MESSAGE_ROUTED1(MediaPlayerDelegateMsg_EndPictureInPictureMode,
+                    int /* delegate_id, distinguishes instances */)
+
 // ----------------------------------------------------------------------------
 // Messages from the renderer notifying the browser of playback state changes.
 // ----------------------------------------------------------------------------
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
index 6d5e262..3d30bd0 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
@@ -168,6 +168,7 @@
                     sLauncherByPid.remove(connection.getPid());
                     if (mBindingManager != null) mBindingManager.removeConnection(connection);
                     if (mRanking != null) {
+                        setReverseRankWhenConnectionLost(mRanking.getReverseRank(connection));
                         mRanking.removeConnection(connection);
                         if (mBindingManager != null) mBindingManager.rankingChanged();
                     }
@@ -183,6 +184,10 @@
     private @ChildProcessImportance int mEffectiveImportance = ChildProcessImportance.MODERATE;
     private boolean mVisible;
 
+    // Protects fields below that are accessed on client thread as well.
+    private final Object mLock = new Object();
+    private int mReverseRankWhenConnectionLost;
+
     @CalledByNative
     private static FileDescriptorInfo makeFdInfo(
             int id, int fd, boolean autoClose, long offset, long size) {
@@ -423,9 +428,12 @@
         if (sandboxed) {
             mRanking = sSandboxedChildConnectionRanking;
             mBindingManager = sBindingManager;
+            mReverseRankWhenConnectionLost = -1;
         } else {
             mRanking = null;
             mBindingManager = null;
+            // -2 means not applicable.
+            mReverseRankWhenConnectionLost = -2;
         }
     }
 
@@ -441,6 +449,18 @@
         return TextUtils.isEmpty(mProcessType) ? "" : mProcessType;
     }
 
+    private void setReverseRankWhenConnectionLost(int reverseRank) {
+        synchronized (mLock) {
+            mReverseRankWhenConnectionLost = reverseRank;
+        }
+    }
+
+    private int getReverseRankWhenConnectionLost() {
+        synchronized (mLock) {
+            return mReverseRankWhenConnectionLost;
+        }
+    }
+
     // Called on client (UI or IO) thread.
     @CalledByNative
     private void getTerminationInfoAndStop(long terminationInfoPtr) {
@@ -450,11 +470,14 @@
         // access it afterwards.
         if (connection == null) return;
 
+        // Note there is no guarantee that connection lost has happened. However ChildProcessRanking
+        // is not thread safe, so this is the best we can do.
+        int reverseRank = getReverseRankWhenConnectionLost();
         int bindingCounts[] = connection.remainingBindingStateCountsCurrentOrWhenDied();
         nativeSetTerminationInfo(terminationInfoPtr, connection.bindingStateCurrentOrWhenDied(),
                 connection.isKilledByUs(), connection.hasCleanExit(),
                 bindingCounts[ChildBindingState.STRONG], bindingCounts[ChildBindingState.MODERATE],
-                bindingCounts[ChildBindingState.WAIVED]);
+                bindingCounts[ChildBindingState.WAIVED], reverseRank);
         LauncherThread.post(() -> mLauncher.stop());
     }
 
@@ -700,5 +723,5 @@
 
     private static native void nativeSetTerminationInfo(long termiantionInfoPtr,
             @ChildBindingState int bindingState, boolean killedByUs, boolean cleanExit,
-            int remainingStrong, int remainingModerate, int remainingWaived);
+            int remainingStrong, int remainingModerate, int remainingWaived, int reverseRank);
 }
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java
index 2e48e5b..e3e19879 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java
@@ -66,7 +66,7 @@
                 return 1;
             }
 
-            return (int) (o1.frameDepth - o2.frameDepth);
+            return Long.signum(o1.frameDepth - o2.frameDepth);
         }
 
         @Override
@@ -241,6 +241,17 @@
         return mRankings.get(mRankings.size() - 1).connection;
     }
 
+    /**
+     * @return reverse rank. Eg lowest ranked connection will have value 0.
+     */
+    public int getReverseRank(ChildProcessConnection connection) {
+        assert connection != null;
+        assert mRankings.size() > 0;
+        int i = indexOf(connection);
+        assert i != -1;
+        return mRankings.size() - 1 - i;
+    }
+
     private int indexOf(ChildProcessConnection connection) {
         for (int i = 0; i < mRankings.size(); ++i) {
             if (mRankings.get(i).connection == connection) return i;
diff --git a/content/public/android/junit/src/org/chromium/content/browser/ChildProcessRankingTest.java b/content/public/android/junit/src/org/chromium/content/browser/ChildProcessRankingTest.java
index 172171c0..4bbcb10f 100644
--- a/content/public/android/junit/src/org/chromium/content/browser/ChildProcessRankingTest.java
+++ b/content/public/android/junit/src/org/chromium/content/browser/ChildProcessRankingTest.java
@@ -230,6 +230,25 @@
     }
 
     @Test
+    public void testFrameDepthIntOverflow() {
+        ChildProcessConnection c1 = createConnection();
+        ChildProcessConnection c2 = createConnection();
+        ChildProcessConnection c3 = createConnection();
+        ChildProcessRanking ranking = new ChildProcessRanking();
+
+        // Native can pass up the maximum value of unsigned int.
+        long intOverflow = ((long) Integer.MAX_VALUE) * 2;
+        ranking.addConnection(c3, true /* foreground */, intOverflow - 1 /* frameDepth */,
+                true /* intersectsViewport */, ChildProcessImportance.NORMAL);
+        ranking.addConnection(c2, true /* foreground */, 10 /* frameDepth */,
+                true /* intersectsViewport */, ChildProcessImportance.NORMAL);
+        ranking.addConnection(c1, true /* foreground */, intOverflow /* frameDepth */,
+                true /* intersectsViewport */, ChildProcessImportance.NORMAL);
+
+        assertRankingAndRemoveAll(ranking, new ChildProcessConnection[] {c2, c3, c1});
+    }
+
+    @Test
     public void testThrowExceptionWhenGoingOverLimit() {
         ChildProcessRanking ranking = new ChildProcessRanking(2);
 
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index ba9451b..ca52787 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -101,6 +101,7 @@
     "child_process_data.h",
     "child_process_launcher_utils.h",
     "child_process_security_policy.h",
+    "child_process_termination_info.cc",
     "child_process_termination_info.h",
     "clear_site_data_utils.h",
     "client_certificate_delegate.h",
diff --git a/content/public/browser/child_process_termination_info.cc b/content/public/browser/child_process_termination_info.cc
new file mode 100644
index 0000000..753880c
--- /dev/null
+++ b/content/public/browser/child_process_termination_info.cc
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/public/browser/child_process_termination_info.h"
+
+namespace content {
+
+ChildProcessTerminationInfo::ChildProcessTerminationInfo() = default;
+ChildProcessTerminationInfo::ChildProcessTerminationInfo(
+    const ChildProcessTerminationInfo& other) = default;
+ChildProcessTerminationInfo::~ChildProcessTerminationInfo() = default;
+
+}  // namespace content
diff --git a/content/public/browser/child_process_termination_info.h b/content/public/browser/child_process_termination_info.h
index 0331f00..5087bb4 100644
--- a/content/public/browser/child_process_termination_info.h
+++ b/content/public/browser/child_process_termination_info.h
@@ -8,6 +8,7 @@
 #include "base/process/kill.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "content/common/content_export.h"
 #include "content/public/common/result_codes.h"
 
 #if defined(OS_ANDROID)
@@ -16,7 +17,11 @@
 
 namespace content {
 
-struct ChildProcessTerminationInfo {
+struct CONTENT_EXPORT ChildProcessTerminationInfo {
+  ChildProcessTerminationInfo();
+  ChildProcessTerminationInfo(const ChildProcessTerminationInfo& other);
+  ~ChildProcessTerminationInfo();
+
   base::TerminationStatus status = base::TERMINATION_STATUS_NORMAL_TERMINATION;
 
   // If |status| is TERMINATION_STATUS_LAUNCH_FAILED then |exit_code| will
@@ -44,6 +49,12 @@
   int remaining_process_with_strong_binding = 0;
   int remaining_process_with_moderate_binding = 0;
   int remaining_process_with_waived_binding = 0;
+
+  // Eg lowest ranked process at time of death should have value 0.
+  // Valid values are non-negative.
+  // -1 means could not be obtained due to threading restrictions.
+  // -2 means not applicable because process is not ranked.
+  int best_effort_reverse_rank = -1;
 #endif
 };
 
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc
index 0d4450b..f4ccca7 100644
--- a/content/public/test/mock_render_process_host.cc
+++ b/content/public/test/mock_render_process_host.cc
@@ -93,7 +93,9 @@
     base::TerminationStatus status,
     int exit_code) {
   has_connection_ = false;
-  ChildProcessTerminationInfo termination_info{status, exit_code};
+  ChildProcessTerminationInfo termination_info;
+  termination_info.status = status;
+  termination_info.exit_code = exit_code;
   NotificationService::current()->Notify(
       NOTIFICATION_RENDERER_PROCESS_CLOSED, Source<RenderProcessHost>(this),
       Details<ChildProcessTerminationInfo>(&termination_info));
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate.cc b/content/renderer/media/renderer_webmediaplayer_delegate.cc
index d8cbaf1..4b8faa4 100644
--- a/content/renderer/media/renderer_webmediaplayer_delegate.cc
+++ b/content/renderer/media/renderer_webmediaplayer_delegate.cc
@@ -238,6 +238,8 @@
                         OnMediaDelegateVolumeMultiplierUpdate)
     IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_BecamePersistentVideo,
                         OnMediaDelegateBecamePersistentVideo)
+    IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_EndPictureInPictureMode,
+                        OnPictureInPictureModeEnded)
     IPC_MESSAGE_UNHANDLED(return false)
   IPC_END_MESSAGE_MAP()
   return true;
@@ -353,6 +355,13 @@
     observer->OnBecamePersistentVideo(value);
 }
 
+void RendererWebMediaPlayerDelegate::OnPictureInPictureModeEnded(
+    int player_id) {
+  Observer* observer = id_map_.Lookup(player_id);
+  if (observer)
+    observer->OnPictureInPictureModeEnded();
+}
+
 void RendererWebMediaPlayerDelegate::ScheduleUpdateTask() {
   if (!pending_update_task_) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate.h b/content/renderer/media/renderer_webmediaplayer_delegate.h
index fc39ff3..3e3921e 100644
--- a/content/renderer/media/renderer_webmediaplayer_delegate.h
+++ b/content/renderer/media/renderer_webmediaplayer_delegate.h
@@ -95,6 +95,7 @@
   void OnMediaDelegateSuspendAllMediaPlayers();
   void OnMediaDelegateVolumeMultiplierUpdate(int player_id, double multiplier);
   void OnMediaDelegateBecamePersistentVideo(int player_id, bool value);
+  void OnPictureInPictureModeEnded(int player_id);
 
   // Schedules UpdateTask() to run soon.
   void ScheduleUpdateTask();
diff --git a/content/renderer/media/stream/webmediaplayer_ms.cc b/content/renderer/media/stream/webmediaplayer_ms.cc
index 5aece72..a91b08c 100644
--- a/content/renderer/media/stream/webmediaplayer_ms.cc
+++ b/content/renderer/media/stream/webmediaplayer_ms.cc
@@ -921,6 +921,15 @@
   get_client()->OnBecamePersistentVideo(value);
 }
 
+void WebMediaPlayerMS::OnPictureInPictureModeEnded() {
+  // It is possible for this method to be called when the player is no longer in
+  // Picture-in-Picture mode.
+  if (!client_ || !IsInPictureInPicture())
+    return;
+
+  client_->PictureInPictureStopped();
+}
+
 bool WebMediaPlayerMS::CopyVideoTextureToPlatformTexture(
     gpu::gles2::GLES2Interface* gl,
     unsigned target,
diff --git a/content/renderer/media/stream/webmediaplayer_ms.h b/content/renderer/media/stream/webmediaplayer_ms.h
index 4e29246a..138c138 100644
--- a/content/renderer/media/stream/webmediaplayer_ms.h
+++ b/content/renderer/media/stream/webmediaplayer_ms.h
@@ -182,6 +182,7 @@
   void OnSeekBackward(double seconds) override;
   void OnVolumeMultiplierUpdate(double multiplier) override;
   void OnBecamePersistentVideo(bool value) override;
+  void OnPictureInPictureModeEnded() override;
 
   void OnFirstFrameReceived(media::VideoRotation video_rotation,
                             bool is_opaque);
diff --git a/content/renderer/media/stream/webmediaplayer_ms_unittest.cc b/content/renderer/media/stream/webmediaplayer_ms_unittest.cc
index 11aa5d4..6a58bcb7 100644
--- a/content/renderer/media/stream/webmediaplayer_ms_unittest.cc
+++ b/content/renderer/media/stream/webmediaplayer_ms_unittest.cc
@@ -578,6 +578,7 @@
       const blink::WebString& remote_device_friendly_name) override {}
   void MediaRemotingStopped(
       blink::WebLocalizedString::Name error_msg) override {}
+  void PictureInPictureStopped() override {}
   void RequestPlay() override {}
   void RequestPause() override {}
   void RequestMuted(bool muted) override {}
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index c4f8e8b..22e1e2a 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -1148,7 +1148,7 @@
 void ServiceWorkerContextClient::DispatchPeriodicSyncEvent(
     const std::string& tag,
     base::TimeDelta timeout,
-    DispatchSyncEventCallback callback) {
+    DispatchPeriodicSyncEventCallback callback) {
   DCHECK(worker_task_runner_->RunsTasksInCurrentSequence());
   // |context_| is valid because the Mojo binding is on |worker_task_runner_|.
   DCHECK(context_);
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index e777e18..7d88bb58 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -317,9 +317,10 @@
                          bool last_chance,
                          base::TimeDelta timeout,
                          DispatchSyncEventCallback callback) override;
-  void DispatchPeriodicSyncEvent(const std::string& tag,
-                                 base::TimeDelta timeout,
-                                 DispatchSyncEventCallback callback) override;
+  void DispatchPeriodicSyncEvent(
+      const std::string& tag,
+      base::TimeDelta timeout,
+      DispatchPeriodicSyncEventCallback callback) override;
   void DispatchAbortPaymentEvent(
       payments::mojom::PaymentHandlerResponseCallbackPtr response_callback,
       DispatchAbortPaymentEventCallback callback) override;
diff --git a/docs/gpu/gpu_testing_bot_details.md b/docs/gpu/gpu_testing_bot_details.md
index b4174e00..d90d3ed 100644
--- a/docs/gpu/gpu_testing_bot_details.md
+++ b/docs/gpu/gpu_testing_bot_details.md
@@ -227,8 +227,8 @@
 
 [infradata/config]:                https://chrome-internal.googlesource.com/infradata/config
 [bot_config.py]:                   https://chrome-internal.googlesource.com/infradata/config/+/master/configs/chromium-swarm/scripts/bot_config.py
-[gen.star]:                        https://chrome-internal.googlesource.com/infradata/config/+/master/configs/chromium-swarm/gen.star
 [gpu.star]:                        https://chrome-internal.googlesource.com/infradata/config/+/master/configs/chromium-swarm/starlark/bots/chromium/gpu.star
+[main.star]:                       https://chrome-internal.googlesource.com/infradata/config/+/master/main.star
 [vms.cfg]:                         https://chrome-internal.googlesource.com/infradata/config/+/master/configs/gce-provider/vms.cfg
 
 ## Walkthroughs of various maintenance scenarios
@@ -299,7 +299,7 @@
         [zones](https://cloud.google.com/compute/docs/regions-zones/) has
         available capacity.
 
-    1.  Run [gen.star] to regenerate `configs/chromium-swarm/bots.cfg`.
+    1.  Run [main.star] to regenerate `configs/chromium-swarm/bots.cfg`.
         Double-check your work there.
     1.  Get this reviewed and landed. This step associates the VM or pool of VMs
         with the bot's name on the waterfall.
@@ -340,7 +340,7 @@
         [second
         example](https://chrome-internal-review.googlesource.com/1111456).
 
-    1.  Run [gen.star] to regenerate `configs/chromium-swarm/bots.cfg`.
+    1.  Run [main.star] to regenerate `configs/chromium-swarm/bots.cfg`.
         Double-check your work there.
 
 1.  Allocate new virtual machines for the bots as described in [How to set up
diff --git a/extensions/browser/api/alarms/alarm_manager.cc b/extensions/browser/api/alarms/alarm_manager.cc
index 3de0227e..4129bb3 100644
--- a/extensions/browser/api/alarms/alarm_manager.cc
+++ b/extensions/browser/api/alarms/alarm_manager.cc
@@ -129,10 +129,9 @@
 void AlarmManager::AddAlarm(const std::string& extension_id,
                             std::unique_ptr<Alarm> alarm,
                             AddAlarmCallback callback) {
-  RunWhenReady(
-      extension_id,
-      base::BindOnce(&AlarmManager::AddAlarmWhenReady, AsWeakPtr(),
-                     base::Passed(std::move(alarm)), std::move(callback)));
+  RunWhenReady(extension_id,
+               base::BindOnce(&AlarmManager::AddAlarmWhenReady, AsWeakPtr(),
+                              std::move(alarm), std::move(callback)));
 }
 
 void AlarmManager::GetAlarm(const std::string& extension_id,
diff --git a/extensions/browser/api/execute_code_function.cc b/extensions/browser/api/execute_code_function.cc
index 0e77ada9..26854ab 100644
--- a/extensions/browser/api/execute_code_function.cc
+++ b/extensions/browser/api/execute_code_function.cc
@@ -250,9 +250,8 @@
         {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
         base::BindOnce(&ExecuteCodeFunction::
                            GetFileURLAndLocalizeComponentResourceInBackground,
-                       this, base::Passed(std::move(data)), extension_id,
-                       extension_path, extension_default_locale,
-                       might_require_localization),
+                       this, std::move(data), extension_id, extension_path,
+                       extension_default_locale, might_require_localization),
         base::BindOnce(&ExecuteCodeFunction::DidLoadAndLocalizeFile, this,
                        resource_.relative_path().AsUTF8Unsafe(),
                        true /* We assume this call always succeeds */));
diff --git a/gpu/vulkan/android/vulkan_implementation_android.cc b/gpu/vulkan/android/vulkan_implementation_android.cc
index 4a40c40..bbc80413 100644
--- a/gpu/vulkan/android/vulkan_implementation_android.cc
+++ b/gpu/vulkan/android/vulkan_implementation_android.cc
@@ -20,6 +20,55 @@
 
 namespace gpu {
 
+namespace {
+bool GetAhbProps(
+    const VkDevice& vk_device,
+    AHardwareBuffer* hardware_buffer,
+    VkAndroidHardwareBufferFormatPropertiesANDROID* ahb_format_props,
+    VkAndroidHardwareBufferPropertiesANDROID* ahb_props) {
+  DCHECK(ahb_format_props);
+  DCHECK(ahb_props);
+
+  // To obtain format properties of an Android hardware buffer, include an
+  // instance of VkAndroidHardwareBufferFormatPropertiesANDROID in the pNext
+  // chain of the VkAndroidHardwareBufferPropertiesANDROID instance passed to
+  // vkGetAndroidHardwareBufferPropertiesANDROID.
+  ahb_format_props->sType =
+      VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID;
+  ahb_format_props->pNext = nullptr;
+
+  ahb_props->sType =
+      VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID;
+  ahb_props->pNext = ahb_format_props;
+
+  bool result = vkGetAndroidHardwareBufferPropertiesANDROID(
+      vk_device, hardware_buffer, ahb_props);
+  if (result != VK_SUCCESS) {
+    LOG(ERROR)
+        << "GetAhbProps: vkGetAndroidHardwareBufferPropertiesANDROID failed : "
+        << result;
+    return false;
+  }
+  return true;
+}
+
+void PopulateYcbcrInfo(
+    const VkAndroidHardwareBufferFormatPropertiesANDROID& ahb_format_props,
+    VulkanYCbCrInfo* ycbcr_info) {
+  DCHECK(ycbcr_info);
+
+  ycbcr_info->suggested_ycbcr_model = ahb_format_props.suggestedYcbcrModel;
+  ycbcr_info->suggested_ycbcr_range = ahb_format_props.suggestedYcbcrRange;
+  ycbcr_info->suggested_xchroma_offset =
+      ahb_format_props.suggestedXChromaOffset;
+  ycbcr_info->suggested_ychroma_offset =
+      ahb_format_props.suggestedYChromaOffset;
+  ycbcr_info->external_format = ahb_format_props.externalFormat;
+  ycbcr_info->format_features = ahb_format_props.formatFeatures;
+}
+
+}  // namespace
+
 VulkanImplementationAndroid::VulkanImplementationAndroid() = default;
 
 VulkanImplementationAndroid::~VulkanImplementationAndroid() = default;
@@ -171,26 +220,11 @@
   DCHECK(vk_device_memory);
   DCHECK(mem_allocation_size);
 
-  // To obtain format properties of an Android hardware buffer, include an
-  // instance of VkAndroidHardwareBufferFormatPropertiesANDROID in the pNext
-  // chain of the VkAndroidHardwareBufferPropertiesANDROID instance passed to
-  // vkGetAndroidHardwareBufferPropertiesANDROID.
-  VkAndroidHardwareBufferFormatPropertiesANDROID ahb_format_props;
-  ahb_format_props.sType =
-      VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID;
-  ahb_format_props.pNext = nullptr;
-
-  VkAndroidHardwareBufferPropertiesANDROID ahb_props;
-  ahb_props.sType =
-      VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID;
-  ahb_props.pNext = &ahb_format_props;
-
-  bool result = vkGetAndroidHardwareBufferPropertiesANDROID(
-      vk_device, ahb_handle.get(), &ahb_props);
-  if (result != VK_SUCCESS) {
-    LOG(ERROR) << "GetAndroidHardwareBufferProperties failed : " << result;
+  // Get the image format properties of an Android hardware buffer.
+  VkAndroidHardwareBufferFormatPropertiesANDROID ahb_format_props = {};
+  VkAndroidHardwareBufferPropertiesANDROID ahb_props = {};
+  if (!GetAhbProps(vk_device, ahb_handle.get(), &ahb_format_props, &ahb_props))
     return false;
-  }
 
   // To create an image with an external format, include an instance of
   // VkExternalFormatANDROID in the pNext chain of VkImageCreateInfo.
@@ -284,7 +318,7 @@
   vk_image_info->initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
 
   // Create Vk Image.
-  result = vkCreateImage(vk_device, vk_image_info, nullptr, vk_image);
+  bool result = vkCreateImage(vk_device, vk_image_info, nullptr, vk_image);
   if (result != VK_SUCCESS) {
     LOG(ERROR) << "vkCreateImage failed : " << result;
     return false;
@@ -344,16 +378,24 @@
   }
 
   *mem_allocation_size = mem_alloc_info.allocationSize;
-  if (ycbcr_info) {
-    ycbcr_info->suggested_ycbcr_model = ahb_format_props.suggestedYcbcrModel;
-    ycbcr_info->suggested_ycbcr_range = ahb_format_props.suggestedYcbcrRange;
-    ycbcr_info->suggested_xchroma_offset =
-        ahb_format_props.suggestedXChromaOffset;
-    ycbcr_info->suggested_ychroma_offset =
-        ahb_format_props.suggestedYChromaOffset;
-    ycbcr_info->external_format = ahb_format_props.externalFormat;
-    ycbcr_info->format_features = ahb_format_props.formatFeatures;
-  }
+  if (ycbcr_info)
+    PopulateYcbcrInfo(ahb_format_props, ycbcr_info);
+  return true;
+}
+
+bool VulkanImplementationAndroid::GetSamplerYcbcrConversionInfo(
+    const VkDevice& vk_device,
+    base::android::ScopedHardwareBufferHandle ahb_handle,
+    VulkanYCbCrInfo* ycbcr_info) {
+  DCHECK(ycbcr_info);
+
+  // Get the image format properties of an Android hardware buffer.
+  VkAndroidHardwareBufferFormatPropertiesANDROID ahb_format_props = {};
+  VkAndroidHardwareBufferPropertiesANDROID ahb_props = {};
+  if (!GetAhbProps(vk_device, ahb_handle.get(), &ahb_format_props, &ahb_props))
+    return false;
+
+  PopulateYcbcrInfo(ahb_format_props, ycbcr_info);
   return true;
 }
 
diff --git a/gpu/vulkan/android/vulkan_implementation_android.h b/gpu/vulkan/android/vulkan_implementation_android.h
index 8d3c29a7..f1f316e 100644
--- a/gpu/vulkan/android/vulkan_implementation_android.h
+++ b/gpu/vulkan/android/vulkan_implementation_android.h
@@ -61,6 +61,10 @@
       VkDeviceMemory* vk_device_memory,
       VkDeviceSize* mem_allocation_size,
       VulkanYCbCrInfo* ycbcr_info) override;
+  bool GetSamplerYcbcrConversionInfo(
+      const VkDevice& vk_device,
+      base::android::ScopedHardwareBufferHandle ahb_handle,
+      VulkanYCbCrInfo* ycbcr_info) override;
 
  private:
   VulkanInstance vulkan_instance_;
diff --git a/gpu/vulkan/vulkan_implementation.h b/gpu/vulkan/vulkan_implementation.h
index 5e676e9..0420b31 100644
--- a/gpu/vulkan/vulkan_implementation.h
+++ b/gpu/vulkan/vulkan_implementation.h
@@ -122,6 +122,12 @@
       VkDeviceMemory* vk_device_memory,
       VkDeviceSize* mem_allocation_size,
       VulkanYCbCrInfo* ycbcr_info = nullptr) = 0;
+
+  // Get the sampler ycbcr conversion information from the AHB.
+  virtual bool GetSamplerYcbcrConversionInfo(
+      const VkDevice& vk_device,
+      base::android::ScopedHardwareBufferHandle ahb_handle,
+      VulkanYCbCrInfo* ycbcr_info) = 0;
 #endif
 
  private:
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index 763fd973..461c1a05 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -1343,12 +1343,14 @@
     builders {
       name: "Linux Builder"
       mixins: "linux-ci"
+      mixins: "linux-xenial"
       dimensions: "cores:32"
     }
 
     builders {
       name: "Linux Tests"
       mixins: "linux-ci"
+      mixins: "linux-xenial"
     }
 
     builders {
@@ -3830,6 +3832,7 @@
     }
     builders {
       mixins: "linux-try"
+      mixins: "linux-xenial"
       mixins: "goma-j150"
       name: "linux-rel"
     }
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index 511e558..86a1a2e 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -295,6 +295,7 @@
     "navigation/navigation_manager_impl_unittest.mm",
     "navigation/navigation_manager_util_unittest.mm",
     "navigation/nscoder_util_unittest.mm",
+    "navigation/wk_back_forward_list_item_holder_unittest.mm",
     "navigation/wk_based_navigation_manager_impl_unittest.mm",
     "navigation/wk_navigation_action_policy_util_unittest.mm",
     "navigation/wk_navigation_action_util_unittest.mm",
@@ -545,7 +546,6 @@
     "web_state/ui/favicon_util_unittest.mm",
     "web_state/ui/html_element_fetch_request_unittest.mm",
     "web_state/ui/web_view_js_utils_unittest.mm",
-    "web_state/ui/wk_back_forward_list_item_holder_unittest.mm",
     "web_state/ui/wk_web_view_configuration_provider_unittest.mm",
   ]
 }
diff --git a/ios/web/navigation/BUILD.gn b/ios/web/navigation/BUILD.gn
index 684ac74..816b5c8e 100644
--- a/ios/web/navigation/BUILD.gn
+++ b/ios/web/navigation/BUILD.gn
@@ -53,6 +53,8 @@
     "session_storage_builder.mm",
     "time_smoother.cc",
     "time_smoother.h",
+    "wk_back_forward_list_item_holder.h",
+    "wk_back_forward_list_item_holder.mm",
     "wk_based_navigation_manager_impl.h",
     "wk_based_navigation_manager_impl.mm",
     "wk_navigation_action_policy_util.h",
diff --git a/ios/web/web_state/ui/wk_back_forward_list_item_holder.h b/ios/web/navigation/wk_back_forward_list_item_holder.h
similarity index 92%
rename from ios/web/web_state/ui/wk_back_forward_list_item_holder.h
rename to ios/web/navigation/wk_back_forward_list_item_holder.h
index 390d781..4fc84ed 100644
--- a/ios/web/web_state/ui/wk_back_forward_list_item_holder.h
+++ b/ios/web/navigation/wk_back_forward_list_item_holder.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_WEB_STATE_UI_WK_BACK_FORWARD_LIST_ITEM_HOLDER_H_
-#define IOS_WEB_WEB_STATE_UI_WK_BACK_FORWARD_LIST_ITEM_HOLDER_H_
+#ifndef IOS_WEB_NAVIGATION_WK_BACK_FORWARD_LIST_ITEM_HOLDER_H_
+#define IOS_WEB_NAVIGATION_WK_BACK_FORWARD_LIST_ITEM_HOLDER_H_
 
 #import <WebKit/WebKit.h>
 
@@ -71,4 +71,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_WEB_STATE_UI_WK_BACK_FORWARD_LIST_ITEM_HOLDER_H_
+#endif  // IOS_WEB_NAVIGATION_WK_BACK_FORWARD_LIST_ITEM_HOLDER_H_
diff --git a/ios/web/web_state/ui/wk_back_forward_list_item_holder.mm b/ios/web/navigation/wk_back_forward_list_item_holder.mm
similarity index 94%
rename from ios/web/web_state/ui/wk_back_forward_list_item_holder.mm
rename to ios/web/navigation/wk_back_forward_list_item_holder.mm
index aaec35d..b072b665 100644
--- a/ios/web/web_state/ui/wk_back_forward_list_item_holder.mm
+++ b/ios/web/navigation/wk_back_forward_list_item_holder.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/web_state/ui/wk_back_forward_list_item_holder.h"
+#import "ios/web/navigation/wk_back_forward_list_item_holder.h"
 
 #include "base/memory/ptr_util.h"
 #import "ios/web/public/navigation_item.h"
diff --git a/ios/web/web_state/ui/wk_back_forward_list_item_holder_unittest.mm b/ios/web/navigation/wk_back_forward_list_item_holder_unittest.mm
similarity index 98%
rename from ios/web/web_state/ui/wk_back_forward_list_item_holder_unittest.mm
rename to ios/web/navigation/wk_back_forward_list_item_holder_unittest.mm
index 48c1465..848b088 100644
--- a/ios/web/web_state/ui/wk_back_forward_list_item_holder_unittest.mm
+++ b/ios/web/navigation/wk_back_forward_list_item_holder_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/web_state/ui/wk_back_forward_list_item_holder.h"
+#import "ios/web/navigation/wk_back_forward_list_item_holder.h"
 
 #import <WebKit/WebKit.h>
 
diff --git a/ios/web/web_state/ui/BUILD.gn b/ios/web/web_state/ui/BUILD.gn
index a384550..40adc19 100644
--- a/ios/web/web_state/ui/BUILD.gn
+++ b/ios/web/web_state/ui/BUILD.gn
@@ -54,8 +54,6 @@
     "crw_web_view_scroll_view_proxy.mm",
     "web_kit_constants.cc",
     "web_kit_constants.h",
-    "wk_back_forward_list_item_holder.h",
-    "wk_back_forward_list_item_holder.mm",
     "wk_security_origin_util.h",
     "wk_security_origin_util.mm",
   ]
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index e4c4494..82fb494 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -60,6 +60,7 @@
 #import "ios/web/navigation/navigation_item_impl.h"
 #import "ios/web/navigation/navigation_manager_impl.h"
 #include "ios/web/navigation/navigation_manager_util.h"
+#import "ios/web/navigation/wk_back_forward_list_item_holder.h"
 #import "ios/web/navigation/wk_navigation_action_policy_util.h"
 #import "ios/web/navigation/wk_navigation_action_util.h"
 #import "ios/web/navigation/wk_navigation_util.h"
@@ -104,7 +105,6 @@
 #import "ios/web/web_state/ui/crw_wk_script_message_router.h"
 #import "ios/web/web_state/ui/favicon_util.h"
 #include "ios/web/web_state/ui/web_kit_constants.h"
-#import "ios/web/web_state/ui/wk_back_forward_list_item_holder.h"
 #import "ios/web/web_state/ui/wk_security_origin_util.h"
 #import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
 #import "ios/web/web_state/web_frame_impl.h"
diff --git a/media/blink/webmediaplayer_delegate.h b/media/blink/webmediaplayer_delegate.h
index b592b21..021af49 100644
--- a/media/blink/webmediaplayer_delegate.h
+++ b/media/blink/webmediaplayer_delegate.h
@@ -69,6 +69,10 @@
     // Called to set as the persistent video. A persistent video should hide its
     // controls and go fullscreen.
     virtual void OnBecamePersistentVideo(bool value) = 0;
+
+    // Called when Picture-in-Picture mode is terminated from the
+    // Picture-in-Picture window.
+    virtual void OnPictureInPictureModeEnded() = 0;
   };
 
   // Returns true if the host frame is hidden or closed.
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index c8016a6..8caa8dac 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -2357,6 +2357,15 @@
   MaybeSendOverlayInfoToDecoder();
 }
 
+void WebMediaPlayerImpl::OnPictureInPictureModeEnded() {
+  // It is possible for this method to be called when the player is no longer in
+  // Picture-in-Picture mode.
+  if (!client_ || !IsInPictureInPicture())
+    return;
+
+  client_->PictureInPictureStopped();
+}
+
 void WebMediaPlayerImpl::SendBytesReceivedUpdate() {
   media_metrics_provider_->AddBytesReceived(bytes_received_since_last_update_);
   bytes_received_since_last_update_ = 0;
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 96723c8..f7ad9ce 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -235,6 +235,7 @@
   void OnSeekBackward(double seconds) override;
   void OnVolumeMultiplierUpdate(double multiplier) override;
   void OnBecamePersistentVideo(bool value) override;
+  void OnPictureInPictureModeEnded() override;
 
   // Callback for when bytes are received by |chunk_demuxer_| or the UrlData
   // being loaded.
diff --git a/media/gpu/libyuv_image_processor.cc b/media/gpu/libyuv_image_processor.cc
index 3be31ae..09c053f 100644
--- a/media/gpu/libyuv_image_processor.cc
+++ b/media/gpu/libyuv_image_processor.cc
@@ -205,7 +205,7 @@
   DVLOGF(4);
 #if defined(OS_LINUX)
   if (input_frame->storage_type() == VideoFrame::STORAGE_DMABUFS) {
-    DCHECK_NE(video_frame_mapper_, nullptr);
+    DCHECK_NE(video_frame_mapper_.get(), nullptr);
     input_frame = video_frame_mapper_->Map(std::move(input_frame));
     if (!input_frame) {
       VLOGF(1) << "Failed to map input VideoFrame";
diff --git a/media/gpu/linux/generic_dmabuf_video_frame_mapper.cc b/media/gpu/linux/generic_dmabuf_video_frame_mapper.cc
index 50028c6..def9dec 100644
--- a/media/gpu/linux/generic_dmabuf_video_frame_mapper.cc
+++ b/media/gpu/linux/generic_dmabuf_video_frame_mapper.cc
@@ -132,9 +132,9 @@
   std::vector<std::pair<uint8_t*, size_t>> chunks;
   const auto& buffer_sizes = layout.buffer_sizes();
   std::vector<uint8_t*> buffer_addrs(buffer_sizes.size(), nullptr);
-  DCHECK_EQ(buffer_addrs.size(), dmabuf_fds.size());
+  DCHECK_LE(buffer_addrs.size(), dmabuf_fds.size());
   DCHECK_LE(buffer_addrs.size(), VideoFrame::kMaxPlanes);
-  for (size_t i = 0; i < dmabuf_fds.size(); i++) {
+  for (size_t i = 0; i < buffer_sizes.size(); i++) {
     buffer_addrs[i] = Mmap(buffer_sizes[i], dmabuf_fds[i].get());
     if (!buffer_addrs[i]) {
       MunmapBuffers(chunks, std::move(video_frame));
@@ -149,14 +149,10 @@
   const auto& planes = layout.planes();
   const size_t num_of_planes = layout.num_planes();
   uint8_t* plane_addrs[VideoFrame::kMaxPlanes] = {};
-  if (dmabuf_fds.size() == 1) {
-    for (size_t i = 0; i < num_of_planes; i++) {
-      plane_addrs[i] = buffer_addrs[0] + planes[i].offset;
-    }
-  } else {
-    for (size_t i = 0; i < num_of_planes; i++) {
-      plane_addrs[i] = buffer_addrs[i] + planes[i].offset;
-    }
+  for (size_t i = 0; i < num_of_planes; i++) {
+    uint8_t* buffer =
+        i < buffer_addrs.size() ? buffer_addrs[i] : buffer_addrs.back();
+    plane_addrs[i] = buffer + planes[i].offset;
   }
   return CreateMappedVideoFrame(std::move(video_frame), plane_addrs, chunks);
 }
diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc
index 9da624e..b5690c5 100644
--- a/mojo/public/cpp/bindings/tests/connector_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -543,9 +543,9 @@
   base::RunLoop run_loop;
   another_thread.task_runner()->PostTaskAndReply(
       FROM_HERE,
-      base::Bind(
+      base::BindOnce(
           [](std::unique_ptr<Connector> connector) { connector.reset(); },
-          base::Passed(std::move(connector))),
+          std::move(connector)),
       run_loop.QuitClosure());
 
   run_loop.Run();
diff --git a/mojo/public/cpp/bindings/thread_safe_interface_ptr.h b/mojo/public/cpp/bindings/thread_safe_interface_ptr.h
index 17c29b09..e7da7b68 100644
--- a/mojo/public/cpp/bindings/thread_safe_interface_ptr.h
+++ b/mojo/public/cpp/bindings/thread_safe_interface_ptr.h
@@ -233,8 +233,7 @@
       caller_task_runner_->PostTask(
           FROM_HERE,
           base::BindOnce(&ForwardToCallingThread::CallAcceptAndDeleteResponder,
-                         base::Passed(std::move(responder_)),
-                         base::Passed(std::move(*message))));
+                         std::move(responder_), std::move(*message)));
       return true;
     }
 
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 5e93f8d..fa62002 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -958,6 +958,7 @@
       "proxy_resolution/proxy_resolver_winhttp.cc",
       "proxy_resolution/proxy_resolver_winhttp.h",
       "proxy_resolution/proxy_retry_info.h",
+      "quic/address_utils.h",
       "quic/bidirectional_stream_quic_impl.cc",
       "quic/bidirectional_stream_quic_impl.h",
       "quic/crypto/proof_source_chromium.cc",
@@ -2643,8 +2644,6 @@
     "data/ssl/certificates/subjectAltName_www_example_com.pem",
     "data/ssl/certificates/thawte.single.pem",
     "data/ssl/certificates/tls_feature_extension.pem",
-    "data/ssl/certificates/treadclimber.pem",
-    "data/ssl/certificates/treadclimber.sctlist",
     "data/ssl/certificates/unescaped.pem",
     "data/ssl/certificates/unittest.key.bin",
     "data/ssl/certificates/unittest.originbound.der",
diff --git a/net/base/ip_endpoint.cc b/net/base/ip_endpoint.cc
index b3866b9..e6115c4 100644
--- a/net/base/ip_endpoint.cc
+++ b/net/base/ip_endpoint.cc
@@ -181,4 +181,8 @@
   return address_ == other.address_ && port_ == other.port_;
 }
 
+bool IPEndPoint::operator!=(const IPEndPoint& that) const {
+  return !(*this == that);
+}
+
 }  // namespace net
diff --git a/net/base/ip_endpoint.h b/net/base/ip_endpoint.h
index f7bb248f..fd635b6 100644
--- a/net/base/ip_endpoint.h
+++ b/net/base/ip_endpoint.h
@@ -65,6 +65,7 @@
 
   bool operator<(const IPEndPoint& that) const;
   bool operator==(const IPEndPoint& that) const;
+  bool operator!=(const IPEndPoint& that) const;
 
  private:
   IPAddress address_;
diff --git a/net/cert/caching_cert_verifier_unittest.cc b/net/cert/caching_cert_verifier_unittest.cc
index ccdba07..4ad7e8d 100644
--- a/net/cert/caching_cert_verifier_unittest.cc
+++ b/net/cert/caching_cert_verifier_unittest.cc
@@ -52,19 +52,17 @@
 
   error = callback.GetResult(verifier_.Verify(
       CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
+                                  std::string()),
       &verify_result, callback.callback(), &request, NetLogWithSource()));
   ASSERT_TRUE(IsCertificateError(error));
   ASSERT_EQ(1u, verifier_.requests());
   ASSERT_EQ(0u, verifier_.cache_hits());
   ASSERT_EQ(1u, verifier_.GetCacheSize());
 
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
-      &verify_result, callback.callback(), &request, NetLogWithSource());
+  error = verifier_.Verify(CertVerifier::RequestParams(
+                               test_cert, "www.example.com", 0, std::string()),
+                           &verify_result, callback.callback(), &request,
+                           NetLogWithSource());
   // Synchronous completion.
   ASSERT_NE(ERR_IO_PENDING, error);
   ASSERT_TRUE(IsCertificateError(error));
@@ -113,8 +111,7 @@
 
   error = callback.GetResult(verifier_.Verify(
       CertVerifier::RequestParams(cert_chain1, "www.example.com", 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
+                                  std::string()),
       &verify_result, callback.callback(), &request, NetLogWithSource()));
   ASSERT_TRUE(IsCertificateError(error));
   ASSERT_EQ(1u, verifier_.requests());
@@ -123,8 +120,7 @@
 
   error = callback.GetResult(verifier_.Verify(
       CertVerifier::RequestParams(cert_chain2, "www.example.com", 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
+                                  std::string()),
       &verify_result, callback.callback(), &request, NetLogWithSource()));
   ASSERT_TRUE(IsCertificateError(error));
   ASSERT_EQ(2u, verifier_.requests());
diff --git a/net/cert/cert_verifier.cc b/net/cert/cert_verifier.cc
index d29b77d..97e6721 100644
--- a/net/cert/cert_verifier.cc
+++ b/net/cert/cert_verifier.cc
@@ -33,13 +33,11 @@
     scoped_refptr<X509Certificate> certificate,
     const std::string& hostname,
     int flags,
-    const std::string& ocsp_response,
-    const std::string& sct_list)
+    const std::string& ocsp_response)
     : certificate_(std::move(certificate)),
       hostname_(hostname),
       flags_(flags),
-      ocsp_response_(ocsp_response),
-      sct_list_(sct_list) {
+      ocsp_response_(ocsp_response) {
   // For efficiency sake, rather than compare all of the fields for each
   // comparison, compute a hash of their values. This is done directly in
   // this class, rather than as an overloaded hash operator, for efficiency's
@@ -55,7 +53,6 @@
   SHA256_Update(&ctx, hostname_.data(), hostname.size());
   SHA256_Update(&ctx, &flags, sizeof(flags));
   SHA256_Update(&ctx, ocsp_response.data(), ocsp_response.size());
-  SHA256_Update(&ctx, sct_list.data(), sct_list.size());
   SHA256_Final(reinterpret_cast<uint8_t*>(
                    base::WriteInto(&key_, SHA256_DIGEST_LENGTH + 1)),
                &ctx);
diff --git a/net/cert/cert_verifier.h b/net/cert/cert_verifier.h
index 8ea6fac14..517dd24 100644
--- a/net/cert/cert_verifier.h
+++ b/net/cert/cert_verifier.h
@@ -107,17 +107,12 @@
   // |ocsp_response| is optional, but if non-empty, should contain an OCSP
   // response obtained via OCSP stapling. It may be ignored by the
   // CertVerifier.
-  //
-  // |sct_list| is optional, but if non-empty, should contain a
-  // SignedCertificateTimestampList from the TLS extension as described in
-  // RFC6962 section 3.3.1. It may be ignored by the CertVerifier.
   class NET_EXPORT RequestParams {
    public:
     RequestParams(scoped_refptr<X509Certificate> certificate,
                   const std::string& hostname,
                   int flags,
-                  const std::string& ocsp_response,
-                  const std::string& sct_list);
+                  const std::string& ocsp_response);
     RequestParams(const RequestParams& other);
     ~RequestParams();
 
@@ -127,7 +122,6 @@
     const std::string& hostname() const { return hostname_; }
     int flags() const { return flags_; }
     const std::string& ocsp_response() const { return ocsp_response_; }
-    const std::string& sct_list() const { return sct_list_; }
 
     bool operator==(const RequestParams& other) const;
     bool operator<(const RequestParams& other) const;
@@ -137,7 +131,6 @@
     std::string hostname_;
     int flags_;
     std::string ocsp_response_;
-    std::string sct_list_;
 
     // Used to optimize sorting/indexing comparisons.
     std::string key_;
diff --git a/net/cert/cert_verifier_unittest.cc b/net/cert/cert_verifier_unittest.cc
index 9a996fb8..6bc31314 100644
--- a/net/cert/cert_verifier_unittest.cc
+++ b/net/cert/cert_verifier_unittest.cc
@@ -47,44 +47,36 @@
       {
           // Test for basic equivalence.
           CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      /*ocsp_response=*/std::string(),
-                                      /*sct_list=*/std::string()),
+                                      std::string()),
           CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      /*ocsp_response=*/std::string(),
-                                      /*sct_list=*/std::string()),
+                                      std::string()),
           true,
       },
       {
           // Test that different certificates but with the same CA and for
           // the same host are different validation keys.
           CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      /*ocsp_response=*/std::string(),
-                                      /*sct_list=*/std::string()),
+                                      std::string()),
           CertVerifier::RequestParams(expired_cert, "www.example.test", 0,
-                                      /*ocsp_response=*/std::string(),
-                                      /*sct_list=*/std::string()),
+                                      std::string()),
           false,
       },
       {
           // Test that the same EE certificate for the same host, but with
           // different chains are different validation keys.
           CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      /*ocsp_response=*/std::string(),
-                                      /*sct_list=*/std::string()),
+                                      std::string()),
           CertVerifier::RequestParams(combined_cert, "www.example.test", 0,
-                                      /*ocsp_response=*/std::string(),
-                                      /*sct_list=*/std::string()),
+                                      std::string()),
           false,
       },
       {
           // The same certificate, with the same chain, but for different
           // hosts are different validation keys.
           CertVerifier::RequestParams(ok_cert, "www1.example.test", 0,
-                                      /*ocsp_response=*/std::string(),
-                                      /*sct_list=*/std::string()),
+                                      std::string()),
           CertVerifier::RequestParams(ok_cert, "www2.example.test", 0,
-                                      /*ocsp_response=*/std::string(),
-                                      /*sct_list=*/std::string()),
+                                      std::string()),
           false,
       },
       {
@@ -92,32 +84,17 @@
           // are different validation keys.
           CertVerifier::RequestParams(
               ok_cert, "www.example.test",
-              CertVerifier::VERIFY_DISABLE_NETWORK_FETCHES,
-              /*ocsp_response=*/std::string(),
-              /*sct_list=*/std::string()),
+              CertVerifier::VERIFY_DISABLE_NETWORK_FETCHES, std::string()),
           CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      /*ocsp_response=*/std::string(),
-                                      /*sct_list=*/std::string()),
+                                      std::string()),
           false,
       },
       {
           // Different OCSP responses.
           CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      "ocsp response",
-                                      /*sct_list=*/std::string()),
+                                      "ocsp response"),
           CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      /*ocsp_response=*/std::string(),
-                                      /*sct_list=*/std::string()),
-          false,
-      },
-      {
-          // Different SignedCertificateTimestampList.
-          CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      /*ocsp_response=*/std::string(),
-                                      "sct list"),
-          CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      /*ocsp_response=*/std::string(),
-                                      /*sct_list=*/std::string()),
+                                      std::string()),
           false,
       },
   };
diff --git a/net/cert/cert_verify_proc.cc b/net/cert/cert_verify_proc.cc
index 1fb2c2c5..b8ae1e99 100644
--- a/net/cert/cert_verify_proc.cc
+++ b/net/cert/cert_verify_proc.cc
@@ -477,7 +477,6 @@
 int CertVerifyProc::Verify(X509Certificate* cert,
                            const std::string& hostname,
                            const std::string& ocsp_response,
-                           const std::string& sct_list,
                            int flags,
                            CRLSet* crl_set,
                            const CertificateList& additional_trust_anchors,
@@ -495,8 +494,8 @@
   verify_result->verified_cert = cert;
 
   DCHECK(crl_set);
-  int rv = VerifyInternal(cert, hostname, ocsp_response, sct_list, flags,
-                          crl_set, additional_trust_anchors, verify_result);
+  int rv = VerifyInternal(cert, hostname, ocsp_response, flags, crl_set,
+                          additional_trust_anchors, verify_result);
 
   // Check for mismatched signature algorithms and unknown signature algorithms
   // in the chain. Also fills in the has_* booleans for the digest algorithms
diff --git a/net/cert/cert_verify_proc.h b/net/cert/cert_verify_proc.h
index af2219d8..8ab3bf5 100644
--- a/net/cert/cert_verify_proc.h
+++ b/net/cert/cert_verify_proc.h
@@ -80,9 +80,6 @@
   //
   // |ocsp_response|, if non-empty, is a stapled OCSP response to use.
   //
-  // |sct_list|, if non-empty, is a SignedCertificateTimestampList from the TLS
-  // extension as described in RFC6962 section 3.3.1.
-  //
   // |flags| is bitwise OR'd of VerifyFlags:
   //
   // If VERIFY_REV_CHECKING_ENABLED is set in |flags|, online certificate
@@ -100,7 +97,6 @@
   int Verify(X509Certificate* cert,
              const std::string& hostname,
              const std::string& ocsp_response,
-             const std::string& sct_list,
              int flags,
              CRLSet* crl_set,
              const CertificateList& additional_trust_anchors,
@@ -157,7 +153,6 @@
   virtual int VerifyInternal(X509Certificate* cert,
                              const std::string& hostname,
                              const std::string& ocsp_response,
-                             const std::string& sct_list,
                              int flags,
                              CRLSet* crl_set,
                              const CertificateList& additional_trust_anchors,
diff --git a/net/cert/cert_verify_proc_android.cc b/net/cert/cert_verify_proc_android.cc
index 06b00ea..7bae0d2 100644
--- a/net/cert/cert_verify_proc_android.cc
+++ b/net/cert/cert_verify_proc_android.cc
@@ -354,7 +354,6 @@
     X509Certificate* cert,
     const std::string& hostname,
     const std::string& ocsp_response,
-    const std::string& sct_list,
     int flags,
     CRLSet* crl_set,
     const CertificateList& additional_trust_anchors,
diff --git a/net/cert/cert_verify_proc_android.h b/net/cert/cert_verify_proc_android.h
index 9868219..eab50a7b 100644
--- a/net/cert/cert_verify_proc_android.h
+++ b/net/cert/cert_verify_proc_android.h
@@ -28,7 +28,6 @@
   int VerifyInternal(X509Certificate* cert,
                      const std::string& hostname,
                      const std::string& ocsp_response,
-                     const std::string& sct_list,
                      int flags,
                      CRLSet* crl_set,
                      const CertificateList& additional_trust_anchors,
diff --git a/net/cert/cert_verify_proc_android_unittest.cc b/net/cert/cert_verify_proc_android_unittest.cc
index 19d79a5e..6f903b3 100644
--- a/net/cert/cert_verify_proc_android_unittest.cc
+++ b/net/cert/cert_verify_proc_android_unittest.cc
@@ -181,11 +181,9 @@
   ASSERT_TRUE(
       CreateCertificateChainFromFiles({"target_one_aia.pem", "i.pem"}, &leaf));
   CertVerifyResult verify_result;
-  EXPECT_EQ(
-      OK,
-      proc->Verify(leaf.get(), "target", /*ocsp_response=*/std::string(),
-                   /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-                   empty_cert_list_, &verify_result));
+  EXPECT_EQ(OK, proc->Verify(leaf.get(), "target", std::string(), 0,
+                             CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                             &verify_result));
 }
 
 // Tests that if the certificate does not contain an AIA URL, no AIA fetch
@@ -197,11 +195,10 @@
   scoped_refptr<X509Certificate> cert;
   ASSERT_TRUE(ReadTestCert("target_no_aia.pem", &cert));
   CertVerifyResult verify_result;
-  EXPECT_EQ(
-      ERR_CERT_AUTHORITY_INVALID,
-      proc->Verify(cert.get(), "target", /*ocsp_response=*/std::string(),
-                   /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-                   empty_cert_list_, &verify_result));
+  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
+            proc->Verify(cert.get(), "target", std::string(), 0,
+                         CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                         &verify_result));
 }
 
 // Tests that if a certificate contains one file:// URL and one http:// URL,
@@ -229,11 +226,9 @@
           ByMove(CreateMockRequestFromX509Certificate(OK, intermediate))));
 
   CertVerifyResult verify_result;
-  EXPECT_EQ(
-      OK,
-      proc->Verify(cert.get(), "target", /*ocsp_response=*/std::string(),
-                   /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-                   empty_cert_list_, &verify_result));
+  EXPECT_EQ(OK, proc->Verify(cert.get(), "target", std::string(), 0,
+                             CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                             &verify_result));
 }
 
 // Tests that if an AIA request returns the wrong intermediate, certificate
@@ -253,11 +248,10 @@
           ByMove(CreateMockRequestFromX509Certificate(OK, bad_intermediate))));
 
   CertVerifyResult verify_result;
-  EXPECT_EQ(
-      ERR_CERT_AUTHORITY_INVALID,
-      proc->Verify(cert.get(), "target", /*ocsp_response=*/std::string(),
-                   /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-                   empty_cert_list_, &verify_result));
+  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
+            proc->Verify(cert.get(), "target", std::string(), 0,
+                         CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                         &verify_result));
 }
 
 // Tests that if an AIA request returns an error, certificate verification
@@ -274,11 +268,10 @@
       .WillOnce(Return(ByMove(CreateMockRequestWithError(ERR_FAILED))));
 
   CertVerifyResult verify_result;
-  EXPECT_EQ(
-      ERR_CERT_AUTHORITY_INVALID,
-      proc->Verify(cert.get(), "target", /*ocsp_response=*/std::string(),
-                   /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-                   empty_cert_list_, &verify_result));
+  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
+            proc->Verify(cert.get(), "target", std::string(), 0,
+                         CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                         &verify_result));
 }
 
 // Tests that if an AIA request returns an unparseable cert, certificate
@@ -295,11 +288,10 @@
       .WillOnce(Return(ByMove(CreateMockRequestWithInvalidCertificate())));
 
   CertVerifyResult verify_result;
-  EXPECT_EQ(
-      ERR_CERT_AUTHORITY_INVALID,
-      proc->Verify(cert.get(), "target", /*ocsp_response=*/std::string(),
-                   /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-                   empty_cert_list_, &verify_result));
+  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
+            proc->Verify(cert.get(), "target", std::string(), 0,
+                         CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                         &verify_result));
 }
 
 // Tests that if a certificate has two HTTP AIA URLs, they are both fetched. If
@@ -330,11 +322,9 @@
           ByMove(CreateMockRequestFromX509Certificate(OK, intermediate))));
 
   CertVerifyResult verify_result;
-  EXPECT_EQ(
-      OK,
-      proc->Verify(cert.get(), "target", /*ocsp_response=*/std::string(),
-                   /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-                   empty_cert_list_, &verify_result));
+  EXPECT_EQ(OK, proc->Verify(cert.get(), "target", std::string(), 0,
+                             CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                             &verify_result));
 }
 
 // Tests that if an intermediate is fetched via AIA, and the intermediate itself
@@ -365,11 +355,10 @@
   CertVerifyResult verify_result;
   // This chain results in an AUTHORITY_INVALID root because |root_| is not
   // trusted.
-  EXPECT_EQ(
-      ERR_CERT_AUTHORITY_INVALID,
-      proc->Verify(cert.get(), "target", /*ocsp_response=*/std::string(),
-                   /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-                   empty_cert_list_, &verify_result));
+  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
+            proc->Verify(cert.get(), "target", std::string(), 0,
+                         CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                         &verify_result));
 }
 
 // Tests that if a certificate contains six AIA URLs, only the first five are
@@ -389,11 +378,10 @@
       .WillOnce(Return(ByMove(CreateMockRequestWithError(ERR_FAILED))));
 
   CertVerifyResult verify_result;
-  EXPECT_EQ(
-      ERR_CERT_AUTHORITY_INVALID,
-      proc->Verify(cert.get(), "target", /*ocsp_response=*/std::string(),
-                   /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-                   empty_cert_list_, &verify_result));
+  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
+            proc->Verify(cert.get(), "target", std::string(), 0,
+                         CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                         &verify_result));
 }
 
 // Tests that if the supplied chain contains an intermediate with an AIA URL,
@@ -417,11 +405,10 @@
   CertVerifyResult verify_result;
   // This chain results in an AUTHORITY_INVALID root because |root_| is not
   // trusted.
-  EXPECT_EQ(
-      ERR_CERT_AUTHORITY_INVALID,
-      proc->Verify(leaf.get(), "target", /*ocsp_response=*/std::string(),
-                   /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-                   empty_cert_list_, &verify_result));
+  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
+            proc->Verify(leaf.get(), "target", std::string(), 0,
+                         CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                         &verify_result));
 }
 
 }  // namespace net
diff --git a/net/cert/cert_verify_proc_builtin.cc b/net/cert/cert_verify_proc_builtin.cc
index b9f29ba..8f2fec34 100644
--- a/net/cert/cert_verify_proc_builtin.cc
+++ b/net/cert/cert_verify_proc_builtin.cc
@@ -269,7 +269,6 @@
   int VerifyInternal(X509Certificate* cert,
                      const std::string& hostname,
                      const std::string& ocsp_response,
-                     const std::string& sct_list,
                      int flags,
                      CRLSet* crl_set,
                      const CertificateList& additional_trust_anchors,
@@ -563,7 +562,6 @@
     X509Certificate* input_cert,
     const std::string& hostname,
     const std::string& ocsp_response,
-    const std::string& sct_list,
     int flags,
     CRLSet* crl_set,
     const CertificateList& additional_trust_anchors,
diff --git a/net/cert/cert_verify_proc_ios.cc b/net/cert/cert_verify_proc_ios.cc
index 1ff9410..88b6ca71 100644
--- a/net/cert/cert_verify_proc_ios.cc
+++ b/net/cert/cert_verify_proc_ios.cc
@@ -12,7 +12,6 @@
 #include "net/base/net_errors.h"
 #include "net/cert/asn1_util.h"
 #include "net/cert/cert_verify_result.h"
-#include "net/cert/ct_serialization.h"
 #include "net/cert/known_roots.h"
 #include "net/cert/test_root_certs.h"
 #include "net/cert/x509_certificate.h"
@@ -21,14 +20,6 @@
 
 using base::ScopedCFTypeRef;
 
-extern "C" {
-// Declared in <Security/SecTrust.h>, available in iOS 12.1.1+
-// TODO(mattm): Remove this weak_import once chromium requires a new enough
-// iOS SDK.
-OSStatus SecTrustSetSignedCertificateTimestamps(SecTrustRef, CFArrayRef)
-    __attribute__((weak_import));
-}  // extern "C"
-
 namespace net {
 
 namespace {
@@ -78,7 +69,6 @@
 int BuildAndEvaluateSecTrustRef(CFArrayRef cert_array,
                                 CFArrayRef trust_policies,
                                 CFDataRef ocsp_response_ref,
-                                CFArrayRef sct_array_ref,
                                 ScopedCFTypeRef<SecTrustRef>* trust_ref,
                                 ScopedCFTypeRef<CFArrayRef>* verified_chain,
                                 SecTrustResultType* trust_result) {
@@ -101,14 +91,6 @@
       return NetErrorFromOSStatus(status);
   }
 
-  if (sct_array_ref) {
-    if (__builtin_available(iOS 12.1.1, *)) {
-      status = SecTrustSetSignedCertificateTimestamps(tmp_trust, sct_array_ref);
-      if (status)
-        return NetErrorFromOSStatus(status);
-    }
-  }
-
   SecTrustResultType tmp_trust_result;
   status = SecTrustEvaluate(tmp_trust, &tmp_trust_result);
   if (status)
@@ -279,7 +261,6 @@
     X509Certificate* cert,
     const std::string& hostname,
     const std::string& ocsp_response,
-    const std::string& sct_list,
     int flags,
     CRLSet* crl_set,
     const CertificateList& additional_trust_anchors,
@@ -307,35 +288,13 @@
       return ERR_OUT_OF_MEMORY;
   }
 
-  ScopedCFTypeRef<CFMutableArrayRef> sct_array_ref;
-  if (!sct_list.empty()) {
-    if (__builtin_available(iOS 12.1.1, *)) {
-      std::vector<base::StringPiece> decoded_sct_list;
-      if (ct::DecodeSCTList(sct_list, &decoded_sct_list)) {
-        sct_array_ref.reset(CFArrayCreateMutable(kCFAllocatorDefault,
-                                                 decoded_sct_list.size(),
-                                                 &kCFTypeArrayCallBacks));
-        if (!sct_array_ref)
-          return ERR_OUT_OF_MEMORY;
-        for (const auto& sct : decoded_sct_list) {
-          ScopedCFTypeRef<CFDataRef> sct_ref(CFDataCreate(
-              kCFAllocatorDefault, reinterpret_cast<const UInt8*>(sct.data()),
-              base::checked_cast<CFIndex>(sct.size())));
-          if (!sct_ref)
-            return ERR_OUT_OF_MEMORY;
-          CFArrayAppendValue(sct_array_ref.get(), sct_ref.get());
-        }
-      }
-    }
-  }
-
   ScopedCFTypeRef<SecTrustRef> trust_ref;
   SecTrustResultType trust_result = kSecTrustResultDeny;
   ScopedCFTypeRef<CFArrayRef> final_chain;
 
-  status = BuildAndEvaluateSecTrustRef(
-      cert_array, trust_policies, ocsp_response_ref.get(), sct_array_ref.get(),
-      &trust_ref, &final_chain, &trust_result);
+  status = BuildAndEvaluateSecTrustRef(cert_array, trust_policies,
+                                       ocsp_response_ref.get(), &trust_ref,
+                                       &final_chain, &trust_result);
   if (status)
     return NetErrorFromOSStatus(status);
 
diff --git a/net/cert/cert_verify_proc_ios.h b/net/cert/cert_verify_proc_ios.h
index ef889be0..b2048c81 100644
--- a/net/cert/cert_verify_proc_ios.h
+++ b/net/cert/cert_verify_proc_ios.h
@@ -30,7 +30,6 @@
   int VerifyInternal(X509Certificate* cert,
                      const std::string& hostname,
                      const std::string& ocsp_response,
-                     const std::string& sct_list,
                      int flags,
                      CRLSet* crl_set,
                      const CertificateList& additional_trust_anchors,
diff --git a/net/cert/cert_verify_proc_mac.cc b/net/cert/cert_verify_proc_mac.cc
index fe275ec..548ce4f 100644
--- a/net/cert/cert_verify_proc_mac.cc
+++ b/net/cert/cert_verify_proc_mac.cc
@@ -27,7 +27,6 @@
 #include "net/cert/cert_verifier.h"
 #include "net/cert/cert_verify_result.h"
 #include "net/cert/crl_set.h"
-#include "net/cert/ct_serialization.h"
 #include "net/cert/ev_root_ca_metadata.h"
 #include "net/cert/internal/certificate_policies.h"
 #include "net/cert/internal/parsed_certificate.h"
@@ -47,14 +46,6 @@
 
 using base::ScopedCFTypeRef;
 
-extern "C" {
-// Declared in <Security/SecTrust.h>, available in 10.14.2+
-// TODO(mattm): Remove this weak_import once chromium compiles against the
-// macOS 10.14 SDK.
-OSStatus SecTrustSetSignedCertificateTimestamps(SecTrustRef, CFArrayRef)
-    __attribute__((weak_import));
-}  // extern "C"
-
 namespace net {
 
 namespace {
@@ -521,7 +512,6 @@
 int BuildAndEvaluateSecTrustRef(CFArrayRef cert_array,
                                 CFArrayRef trust_policies,
                                 CFDataRef ocsp_response_ref,
-                                CFArrayRef sct_array_ref,
                                 int flags,
                                 CFArrayRef keychain_search_list,
                                 ScopedCFTypeRef<SecTrustRef>* trust_ref,
@@ -553,14 +543,6 @@
       return NetErrorFromOSStatus(status);
   }
 
-  if (sct_array_ref && SecTrustSetSignedCertificateTimestamps) {
-    if (__builtin_available(macOS 10.14.2, *)) {
-      status = SecTrustSetSignedCertificateTimestamps(tmp_trust, sct_array_ref);
-      if (status)
-        return NetErrorFromOSStatus(status);
-    }
-  }
-
   CSSM_APPLE_TP_ACTION_DATA tp_action_data;
   memset(&tp_action_data, 0, sizeof(tp_action_data));
   tp_action_data.Version = CSSM_APPLE_TP_ACTION_VERSION;
@@ -633,7 +615,6 @@
 int VerifyWithGivenFlags(X509Certificate* cert,
                          const std::string& hostname,
                          const std::string& ocsp_response,
-                         const std::string& sct_list,
                          const int flags,
                          CRLSet* crl_set,
                          CertVerifyResult* verify_result,
@@ -655,28 +636,6 @@
       return ERR_OUT_OF_MEMORY;
   }
 
-  ScopedCFTypeRef<CFMutableArrayRef> sct_array_ref;
-  if (!sct_list.empty()) {
-    if (__builtin_available(macOS 10.14.2, *)) {
-      std::vector<base::StringPiece> decoded_sct_list;
-      if (ct::DecodeSCTList(sct_list, &decoded_sct_list)) {
-        sct_array_ref.reset(CFArrayCreateMutable(kCFAllocatorDefault,
-                                                 decoded_sct_list.size(),
-                                                 &kCFTypeArrayCallBacks));
-        if (!sct_array_ref)
-          return ERR_OUT_OF_MEMORY;
-        for (const auto& sct : decoded_sct_list) {
-          ScopedCFTypeRef<CFDataRef> sct_ref(CFDataCreate(
-              kCFAllocatorDefault, reinterpret_cast<const UInt8*>(sct.data()),
-              base::checked_cast<CFIndex>(sct.size())));
-          if (!sct_ref)
-            return ERR_OUT_OF_MEMORY;
-          CFArrayAppendValue(sct_array_ref.get(), sct_ref.get());
-        }
-      }
-    }
-  }
-
   // Serialize all calls that may use the Keychain, to work around various
   // issues in OS X 10.6+ with multi-threaded access to Security.framework.
   base::AutoLock lock(crypto::GetMacSecurityServicesLock());
@@ -813,8 +772,7 @@
       CSSM_TP_APPLE_EVIDENCE_INFO* temp_chain_info = NULL;
 
       int rv = BuildAndEvaluateSecTrustRef(
-          cert_array, trust_policies, ocsp_response_ref.get(),
-          sct_array_ref.get(), flags,
+          cert_array, trust_policies, ocsp_response_ref.get(), flags,
           scoped_alternate_keychain_search_list.get(), &temp_ref,
           &temp_trust_result, &temp_chain, &temp_chain_info);
       if (rv != OK)
@@ -1045,7 +1003,6 @@
     X509Certificate* cert,
     const std::string& hostname,
     const std::string& ocsp_response,
-    const std::string& sct_list,
     int flags,
     CRLSet* crl_set,
     const CertificateList& additional_trust_anchors,
@@ -1059,9 +1016,8 @@
   GetCandidateEVPolicy(cert, &candidate_ev_policy_oid);
 
   CRLSetResult completed_chain_crl_result;
-  int rv =
-      VerifyWithGivenFlags(cert, hostname, ocsp_response, sct_list, flags,
-                           crl_set, verify_result, &completed_chain_crl_result);
+  int rv = VerifyWithGivenFlags(cert, hostname, ocsp_response, flags, crl_set,
+                                verify_result, &completed_chain_crl_result);
   if (rv != OK)
     return rv;
 
@@ -1079,7 +1035,7 @@
       // re-verification starts with a clean slate.
       *verify_result = input_verify_result;
       int tmp_rv = VerifyWithGivenFlags(
-          verify_result->verified_cert.get(), hostname, ocsp_response, sct_list,
+          verify_result->verified_cert.get(), hostname, ocsp_response,
           flags | VERIFY_REV_CHECKING_ENABLED, crl_set, verify_result,
           &completed_chain_crl_result);
       // If re-verification failed, return those results without setting EV
diff --git a/net/cert/cert_verify_proc_mac.h b/net/cert/cert_verify_proc_mac.h
index ee4a64a6..b9bce00 100644
--- a/net/cert/cert_verify_proc_mac.h
+++ b/net/cert/cert_verify_proc_mac.h
@@ -25,7 +25,6 @@
   int VerifyInternal(X509Certificate* cert,
                      const std::string& hostname,
                      const std::string& ocsp_response,
-                     const std::string& sct_list,
                      int flags,
                      CRLSet* crl_set,
                      const CertificateList& additional_trust_anchors,
diff --git a/net/cert/cert_verify_proc_mac_unittest.cc b/net/cert/cert_verify_proc_mac_unittest.cc
index 6b2572a51..5a6044e 100644
--- a/net/cert/cert_verify_proc_mac_unittest.cc
+++ b/net/cert/cert_verify_proc_mac_unittest.cc
@@ -107,10 +107,9 @@
   CertVerifyResult verify_result;
 
   scoped_refptr<CertVerifyProc> verify_proc = new CertVerifyProcMac;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, crl_set.get(), CertificateList(),
-      &verify_result);
+  int error =
+      verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                          crl_set.get(), CertificateList(), &verify_result);
 
   ASSERT_EQ(OK, error);
   ASSERT_EQ(0U, verify_result.cert_status);
@@ -167,9 +166,8 @@
   CertVerifyResult verify_result;
   scoped_refptr<CertVerifyProc> verify_proc = new CertVerifyProcMac;
   int error = verify_proc->Verify(
-      cert.get(), "gms.hongleong.com.my", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+      cert.get(), "gms.hongleong.com.my", std::string(), flags,
+      CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
 
   ASSERT_EQ(OK, error);
   EXPECT_FALSE(verify_result.has_sha1);
@@ -214,10 +212,9 @@
   int flags = 0;
   CertVerifyResult verify_result;
   scoped_refptr<CertVerifyProc> verify_proc = new CertVerifyProcMac;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
 }
diff --git a/net/cert/cert_verify_proc_nss.cc b/net/cert/cert_verify_proc_nss.cc
index 9b430bf..7bb58da 100644
--- a/net/cert/cert_verify_proc_nss.cc
+++ b/net/cert/cert_verify_proc_nss.cc
@@ -1006,7 +1006,6 @@
     X509Certificate* cert,
     const std::string& hostname,
     const std::string& ocsp_response,
-    const std::string& sct_list,
     int flags,
     CRLSet* crl_set,
     const CertificateList& additional_trust_anchors,
diff --git a/net/cert/cert_verify_proc_nss.h b/net/cert/cert_verify_proc_nss.h
index 022d675..417c4cd6 100644
--- a/net/cert/cert_verify_proc_nss.h
+++ b/net/cert/cert_verify_proc_nss.h
@@ -38,7 +38,6 @@
   int VerifyInternal(X509Certificate* cert,
                      const std::string& hostname,
                      const std::string& ocsp_response,
-                     const std::string& sct_list,
                      int flags,
                      CRLSet* crl_set,
                      const CertificateList& additional_trust_anchors,
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index 8d07f0d..463073d 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -110,7 +110,6 @@
   int VerifyInternal(X509Certificate* cert,
                      const std::string& hostname,
                      const std::string& ocsp_response,
-                     const std::string& sct_list,
                      int flags,
                      CRLSet* crl_set,
                      const CertificateList& additional_trust_anchors,
@@ -125,7 +124,6 @@
     X509Certificate* cert,
     const std::string& hostname,
     const std::string& ocsp_response,
-    const std::string& sct_list,
     int flags,
     CRLSet* crl_set,
     const CertificateList& additional_trust_anchors,
@@ -716,8 +714,7 @@
              CRLSet* crl_set,
              const CertificateList& additional_trust_anchors,
              CertVerifyResult* verify_result) {
-    return verify_proc_->Verify(cert, hostname, /*ocsp_response=*/std::string(),
-                                /*sct_list=*/std::string(), flags, crl_set,
+    return verify_proc_->Verify(cert, hostname, std::string(), flags, crl_set,
                                 additional_trust_anchors, verify_result);
   }
 
@@ -1356,10 +1353,9 @@
     scoped_refptr<CertVerifyProc> verify_proc =
         new MockCertVerifyProc(dummy_result);
 
-    return verify_proc->Verify(
-        chain.get(), "test.example.com", /*ocsp_response=*/std::string(),
-        /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-        CertificateList(), &verify_result);
+    return verify_proc->Verify(chain.get(), "test.example.com", std::string(),
+                               flags, CRLSet::BuiltinCRLSet().get(),
+                               CertificateList(), &verify_result);
   }
 
  private:
@@ -1913,10 +1909,9 @@
   dummy_result.is_issued_by_known_root = true;
   scoped_refptr<CertVerifyProc> verify_proc =
       new MockCertVerifyProc(dummy_result);
-  error = verify_proc->Verify(
-      cert.get(), "webmail", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  error = verify_proc->Verify(cert.get(), "webmail", std::string(), 0,
+                              CRLSet::BuiltinCRLSet().get(), CertificateList(),
+                              &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_NON_UNIQUE_NAME);
 
@@ -1924,10 +1919,9 @@
   dummy_result.Reset();
   dummy_result.is_issued_by_known_root = false;
   verify_proc = base::MakeRefCounted<MockCertVerifyProc>(dummy_result);
-  error = verify_proc->Verify(
-      cert.get(), "webmail", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  error = verify_proc->Verify(cert.get(), "webmail", std::string(), 0,
+                              CRLSet::BuiltinCRLSet().get(), CertificateList(),
+                              &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_NON_UNIQUE_NAME);
 }
@@ -1968,10 +1962,9 @@
     verify_proc = base::MakeRefCounted<MockCertVerifyProc>(symantec_result);
 
     CertVerifyResult test_result_1;
-    error = verify_proc->Verify(
-        cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-        /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-        CertificateList(), &test_result_1);
+    error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), 0,
+                                CRLSet::BuiltinCRLSet().get(),
+                                CertificateList(), &test_result_1);
     EXPECT_THAT(error, IsError(ERR_CERT_SYMANTEC_LEGACY));
     EXPECT_TRUE(test_result_1.cert_status & CERT_STATUS_SYMANTEC_LEGACY);
 
@@ -1985,18 +1978,16 @@
     verify_proc = base::MakeRefCounted<MockCertVerifyProc>(whitelisted_result);
 
     CertVerifyResult test_result_2;
-    error = verify_proc->Verify(
-        cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-        /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-        CertificateList(), &test_result_2);
+    error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), 0,
+                                CRLSet::BuiltinCRLSet().get(),
+                                CertificateList(), &test_result_2);
     EXPECT_THAT(error, IsOk());
     EXPECT_FALSE(test_result_2.cert_status & CERT_STATUS_AUTHORITY_INVALID);
 
     // ... Or the caller disabled enforcement of Symantec policies.
     CertVerifyResult test_result_3;
     error = verify_proc->Verify(
-        cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-        /*sct_list=*/std::string(),
+        cert.get(), "127.0.0.1", std::string(),
         CertVerifyProc::VERIFY_DISABLE_SYMANTEC_ENFORCEMENT,
         CRLSet::BuiltinCRLSet().get(), CertificateList(), &test_result_3);
     EXPECT_THAT(error, IsOk());
@@ -2028,10 +2019,9 @@
     verify_proc = base::MakeRefCounted<MockCertVerifyProc>(symantec_result);
 
     CertVerifyResult test_result_1;
-    error = verify_proc->Verify(
-        cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-        /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-        CertificateList(), &test_result_1);
+    error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), 0,
+                                CRLSet::BuiltinCRLSet().get(),
+                                CertificateList(), &test_result_1);
     if (feature_flag_enabled) {
       EXPECT_THAT(error, IsError(ERR_CERT_SYMANTEC_LEGACY));
       EXPECT_TRUE(test_result_1.cert_status & CERT_STATUS_SYMANTEC_LEGACY);
@@ -2050,18 +2040,16 @@
     verify_proc = base::MakeRefCounted<MockCertVerifyProc>(whitelisted_result);
 
     CertVerifyResult test_result_2;
-    error = verify_proc->Verify(
-        cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-        /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-        CertificateList(), &test_result_2);
+    error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), 0,
+                                CRLSet::BuiltinCRLSet().get(),
+                                CertificateList(), &test_result_2);
     EXPECT_THAT(error, IsOk());
     EXPECT_FALSE(test_result_2.cert_status & CERT_STATUS_AUTHORITY_INVALID);
 
     // ... Or the caller disabled enforcement of Symantec policies.
     CertVerifyResult test_result_3;
     error = verify_proc->Verify(
-        cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-        /*sct_list=*/std::string(),
+        cert.get(), "127.0.0.1", std::string(),
         CertVerifyProc::VERIFY_DISABLE_SYMANTEC_ENFORCEMENT,
         CRLSet::BuiltinCRLSet().get(), CertificateList(), &test_result_3);
     EXPECT_THAT(error, IsOk());
@@ -2246,93 +2234,6 @@
   EXPECT_FALSE(verify_result.is_issued_by_known_root);
 }
 
-// Test verification with a leaf that does not contain embedded SCTs, and which
-// has a notBefore date after 2018/10/15, and with no |sct_list|.
-// On recent macOS and iOS versions this should fail to verify.
-// The iOS simulator has different verifier behavior than a real device, and
-// verification succeeds on all currently available versions. If this test
-// fails on iossim in the future, disable the test on TARGET_IPHONE_SIMULATOR
-// and file a bug against mattm.
-TEST_P(CertVerifyProcInternalTest, LeafNewerThan20181015NoScts) {
-  scoped_refptr<X509Certificate> chain = CreateCertificateChainFromFile(
-      GetTestCertsDirectory(), "treadclimber.pem",
-      X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
-  ASSERT_TRUE(chain);
-  if (base::Time::Now() > chain->valid_expiry()) {
-    FAIL() << "This test uses a certificate chain which is now expired. Please "
-              "disable and file a bug against mattm.";
-    return;
-  }
-
-  // Verification should pass, except on recent macOS / iOS systems.
-  int flags = 0;
-  CertVerifyResult verify_result;
-  int error = verify_proc()->Verify(
-      chain.get(), "treadclimber.com", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
-
-#if defined(OS_IOS) && !TARGET_IPHONE_SIMULATOR
-  if (verify_proc_type() == CERT_VERIFY_PROC_IOS) {
-    if (__builtin_available(iOS 12.1.1, *)) {
-      // TODO(mattm): Check if this can this be mapped to some better error.
-      EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
-      EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
-      return;
-    }
-  }
-#elif defined(OS_MACOSX)
-  if (verify_proc_type() == CERT_VERIFY_PROC_MAC) {
-    if (__builtin_available(macOS 10.14.2, *)) {
-      // TODO(mattm): SecTrustEvaluate just gives a generic
-      // CSSMERR_TP_VERIFY_ACTION_FAILED error. Not sure there's much that
-      // could be done about that.
-      EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
-      EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
-      return;
-    }
-  }
-#endif
-  // On older macOS and iOS versions, and all other verifiers, verification
-  // should succeed:
-  EXPECT_THAT(error, IsOk());
-  EXPECT_EQ(0U, verify_result.cert_status);
-  EXPECT_TRUE(verify_result.is_issued_by_known_root);
-}
-
-// Test verification with a leaf that does not contain embedded SCTs, and which
-// has a notBefore date after 2018/10/15, and passing a valid |sct_list| to
-// Verify(). Verification should succeed on all platforms. (Assuming the
-// verifier trusts the SCT Logs used in |sct_list|.)
-TEST_P(CertVerifyProcInternalTest, LeafNewerThan20181015WithTlsSctList) {
-  scoped_refptr<X509Certificate> chain = CreateCertificateChainFromFile(
-      GetTestCertsDirectory(), "treadclimber.pem",
-      X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
-  ASSERT_TRUE(chain);
-  if (base::Time::Now() > chain->valid_expiry()) {
-    FAIL() << "This test uses a certificate chain which is now expired. Please "
-              "disable and file a bug against mattm.";
-    return;
-  }
-
-  std::string sct_list;
-  ASSERT_TRUE(base::ReadFileToString(
-      GetTestCertsDirectory().AppendASCII("treadclimber.sctlist"), &sct_list));
-
-  int flags = 0;
-  CertVerifyResult verify_result;
-  int error = verify_proc()->Verify(chain.get(), "treadclimber.com",
-                                    /*ocsp_response=*/std::string(), sct_list,
-                                    flags, CRLSet::BuiltinCRLSet().get(),
-                                    CertificateList(), &verify_result);
-
-  // Since the valid |sct_list| was passed to Verify, verification should
-  // succeed on all verifiers and OS versions.
-  EXPECT_THAT(error, IsOk());
-  EXPECT_EQ(0U, verify_result.cert_status);
-  EXPECT_TRUE(verify_result.is_issued_by_known_root);
-}
-
 // Test that CRLSets are effective in making a certificate appear to be
 // revoked.
 TEST_P(CertVerifyProcInternalTest, CRLSet) {
@@ -3255,10 +3156,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
 }
@@ -3274,10 +3174,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
 }
@@ -3293,10 +3192,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_WEAK_SIGNATURE_ALGORITHM));
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_WEAK_SIGNATURE_ALGORITHM);
 }
@@ -3314,10 +3212,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_WEAK_SIGNATURE_ALGORITHM));
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_WEAK_SIGNATURE_ALGORITHM);
 }
@@ -3335,10 +3232,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   if (AreSHA1IntermediatesAllowed()) {
     EXPECT_THAT(error, IsOk());
     EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_SHA1_SIGNATURE_PRESENT);
@@ -3363,20 +3259,18 @@
   // SHA-1 should be rejected by default for private roots...
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_WEAK_SIGNATURE_ALGORITHM));
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_SHA1_SIGNATURE_PRESENT);
 
   // ... unless VERIFY_ENABLE_SHA1_LOCAL_ANCHORS was supplied.
   flags = CertVerifyProc::VERIFY_ENABLE_SHA1_LOCAL_ANCHORS;
   verify_result.Reset();
-  error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                              CRLSet::BuiltinCRLSet().get(), CertificateList(),
+                              &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_SHA1_SIGNATURE_PRESENT);
 }
@@ -3461,9 +3355,9 @@
   // hash algorithms is done by CertVerifyProc::Verify().
   scoped_refptr<CertVerifyProc> proc =
       new MockCertVerifyProc(CertVerifyResult());
-  proc->Verify(ee_chain.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-               /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-               CertificateList(), &verify_result);
+  proc->Verify(ee_chain.get(), "127.0.0.1", std::string(), flags,
+               CRLSet::BuiltinCRLSet().get(), CertificateList(),
+               &verify_result);
   EXPECT_EQ(!!(data.expected_algorithms & EXPECT_MD2), verify_result.has_md2);
   EXPECT_EQ(!!(data.expected_algorithms & EXPECT_MD4), verify_result.has_md4);
   EXPECT_EQ(!!(data.expected_algorithms & EXPECT_MD5), verify_result.has_md5);
@@ -3590,10 +3484,9 @@
     scoped_refptr<CertVerifyProc> verify_proc = new MockCertVerifyProc(result);
 
     CertVerifyResult verify_result;
-    int error = verify_proc->Verify(
-        cert.get(), hostname, /*ocsp_response=*/std::string(),
-        /*sct_list=*/std::string(), 0, CRLSet::BuiltinCRLSet().get(),
-        CertificateList(), &verify_result);
+    int error = verify_proc->Verify(cert.get(), hostname, std::string(), 0,
+                                    CRLSet::BuiltinCRLSet().get(),
+                                    CertificateList(), &verify_result);
     if (valid) {
       EXPECT_THAT(error, IsOk());
       EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_COMMON_NAME_INVALID);
@@ -3686,10 +3579,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_EQ(OK, error);
   histograms.ExpectTotalCount(kTLSFeatureExtensionHistogram, 1);
   histograms.ExpectBucketCount(kTLSFeatureExtensionHistogram, true, 1);
@@ -3714,9 +3606,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", "dummy response", /*sct_list=*/std::string(),
-      flags, CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", "dummy response",
+                                  flags, CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_EQ(OK, error);
   histograms.ExpectTotalCount(kTLSFeatureExtensionHistogram, 1);
   histograms.ExpectBucketCount(kTLSFeatureExtensionHistogram, true, 1);
@@ -3741,10 +3633,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_EQ(OK, error);
   histograms.ExpectTotalCount(kTLSFeatureExtensionHistogram, 1);
   histograms.ExpectBucketCount(kTLSFeatureExtensionHistogram, false, 1);
@@ -3767,10 +3658,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_EQ(OK, error);
   histograms.ExpectTotalCount(kTLSFeatureExtensionHistogram, 0);
   histograms.ExpectTotalCount(kTLSFeatureExtensionOCSPHistogram, 0);
@@ -3809,10 +3699,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_EQ(OK, error);
   histograms.ExpectUniqueSample(kTrustAnchorVerifyHistogram,
                                 kGTSRootR4HistogramID, 1);
@@ -3857,10 +3746,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_EQ(OK, error);
 
   // Only GTS Root R3 should be recorded.
@@ -3896,10 +3784,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_EQ(OK, error);
   const base::HistogramBase::Sample kUnknownRootHistogramID = 0;
   histograms.ExpectUniqueSample(kTrustAnchorVerifyHistogram,
diff --git a/net/cert/cert_verify_proc_win.cc b/net/cert/cert_verify_proc_win.cc
index 2847990c..9fdb3b4 100644
--- a/net/cert/cert_verify_proc_win.cc
+++ b/net/cert/cert_verify_proc_win.cc
@@ -847,7 +847,6 @@
     X509Certificate* cert,
     const std::string& hostname,
     const std::string& ocsp_response,
-    const std::string& sct_list,
     int flags,
     CRLSet* crl_set,
     const CertificateList& additional_trust_anchors,
diff --git a/net/cert/cert_verify_proc_win.h b/net/cert/cert_verify_proc_win.h
index e584906..666fa95 100644
--- a/net/cert/cert_verify_proc_win.h
+++ b/net/cert/cert_verify_proc_win.h
@@ -24,7 +24,6 @@
   int VerifyInternal(X509Certificate* cert,
                      const std::string& hostname,
                      const std::string& ocsp_response,
-                     const std::string& sct_list,
                      int flags,
                      CRLSet* crl_set,
                      const CertificateList& additional_trust_anchors,
diff --git a/net/cert/multi_threaded_cert_verifier.cc b/net/cert/multi_threaded_cert_verifier.cc
index 531aa0e..c578a473 100644
--- a/net/cert/multi_threaded_cert_verifier.cc
+++ b/net/cert/multi_threaded_cert_verifier.cc
@@ -196,7 +196,6 @@
     const scoped_refptr<X509Certificate>& cert,
     const std::string& hostname,
     const std::string& ocsp_response,
-    const std::string& sct_list,
     int flags,
     const scoped_refptr<CRLSet>& crl_set,
     const CertificateList& additional_trust_anchors) {
@@ -205,7 +204,7 @@
   MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives
       allow_base_sync_primitives;
   verify_result->error = verify_proc->Verify(
-      cert.get(), hostname, ocsp_response, sct_list, flags, crl_set.get(),
+      cert.get(), hostname, ocsp_response, flags, crl_set.get(),
       additional_trust_anchors, &verify_result->result);
   return verify_result;
 }
@@ -249,8 +248,8 @@
         FROM_HERE,
         {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
         base::BindOnce(&DoVerifyOnWorkerThread, verify_proc, key_.certificate(),
-                       key_.hostname(), key_.ocsp_response(), key_.sct_list(),
-                       flags, config.crl_set, config.additional_trust_anchors),
+                       key_.hostname(), key_.ocsp_response(), flags,
+                       config.crl_set, config.additional_trust_anchors),
         base::BindOnce(&CertVerifierJob::OnJobCompleted,
                        weak_ptr_factory_.GetWeakPtr(), config_id));
   }
diff --git a/net/cert/multi_threaded_cert_verifier_unittest.cc b/net/cert/multi_threaded_cert_verifier_unittest.cc
index fac1994..5506984 100644
--- a/net/cert/multi_threaded_cert_verifier_unittest.cc
+++ b/net/cert/multi_threaded_cert_verifier_unittest.cc
@@ -39,11 +39,10 @@
 
 class MockCertVerifyProc : public CertVerifyProc {
  public:
-  MOCK_METHOD8(VerifyInternal,
+  MOCK_METHOD7(VerifyInternal,
                int(X509Certificate*,
                    const std::string&,
                    const std::string&,
-                   const std::string&,
                    int,
                    CRLSet*,
                    const CertificateList&,
@@ -56,7 +55,7 @@
 
 ACTION(SetCertVerifyResult) {
   X509Certificate* cert = arg0;
-  CertVerifyResult* result = arg7;
+  CertVerifyResult* result = arg6;
   result->Reset();
   result->verified_cert = cert;
   result->cert_status = CERT_STATUS_COMMON_NAME_INVALID;
@@ -64,7 +63,7 @@
 
 ACTION(SetCertVerifyRevokedResult) {
   X509Certificate* cert = arg0;
-  CertVerifyResult* result = arg7;
+  CertVerifyResult* result = arg6;
   result->Reset();
   result->verified_cert = cert;
   result->cert_status = CERT_STATUS_REVOKED;
@@ -79,7 +78,7 @@
         verifier_(mock_verify_proc_) {
     EXPECT_CALL(*mock_verify_proc_, SupportsAdditionalTrustAnchors())
         .WillRepeatedly(Return(true));
-    EXPECT_CALL(*mock_verify_proc_, VerifyInternal(_, _, _, _, _, _, _, _))
+    EXPECT_CALL(*mock_verify_proc_, VerifyInternal(_, _, _, _, _, _, _))
         .WillRepeatedly(
             DoAll(SetCertVerifyResult(), Return(ERR_CERT_COMMON_NAME_INVALID)));
   }
@@ -105,18 +104,16 @@
   TestCompletionCallback callback2;
   std::unique_ptr<CertVerifier::Request> request2;
 
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
-      &verify_result, callback.callback(), &request, NetLogWithSource());
+  error = verifier_.Verify(CertVerifier::RequestParams(
+                               test_cert, "www.example.com", 0, std::string()),
+                           &verify_result, callback.callback(), &request,
+                           NetLogWithSource());
   ASSERT_THAT(error, IsError(ERR_IO_PENDING));
   EXPECT_TRUE(request);
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
-      &verify_result2, callback2.callback(), &request2, NetLogWithSource());
+  error = verifier_.Verify(CertVerifier::RequestParams(
+                               test_cert, "www.example.com", 0, std::string()),
+                           &verify_result2, callback2.callback(), &request2,
+                           NetLogWithSource());
   EXPECT_THAT(error, IsError(ERR_IO_PENDING));
   EXPECT_TRUE(request2);
   error = callback.WaitForResult();
@@ -138,11 +135,10 @@
   CertVerifyResult verify_result;
   std::unique_ptr<CertVerifier::Request> request;
 
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
-      &verify_result, base::BindOnce(&FailTest), &request, NetLogWithSource());
+  error = verifier_.Verify(CertVerifier::RequestParams(
+                               test_cert, "www.example.com", 0, std::string()),
+                           &verify_result, base::BindOnce(&FailTest), &request,
+                           NetLogWithSource());
   ASSERT_THAT(error, IsError(ERR_IO_PENDING));
   ASSERT_TRUE(request);
   request.reset();
@@ -154,8 +150,7 @@
   for (int i = 0; i < 5; ++i) {
     error = verifier_.Verify(
         CertVerifier::RequestParams(test_cert, "www2.example.com", 0,
-                                    /*ocsp_response=*/std::string(),
-                                    /*sct_list=*/std::string()),
+                                    std::string()),
         &verify_result, callback.callback(), &request, NetLogWithSource());
     ASSERT_THAT(error, IsError(ERR_IO_PENDING));
     EXPECT_TRUE(request);
@@ -185,8 +180,7 @@
     ANNOTATE_SCOPED_MEMORY_LEAK;
     error = verifier_.Verify(
         CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                    /*ocsp_response=*/std::string(),
-                                    /*sct_list=*/std::string()),
+                                    std::string()),
         &verify_result, callback.callback(), &request, NetLogWithSource());
   }
   ASSERT_THAT(error, IsError(ERR_IO_PENDING));
@@ -226,42 +220,32 @@
 
   // Start 3 unique requests.
   error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain2, 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
+      CertVerifier::RequestParams(test_cert, domain2, 0, std::string()),
       &verify_result1, callback1.callback(), &request1, NetLogWithSource());
   ASSERT_THAT(error, IsError(ERR_IO_PENDING));
   EXPECT_TRUE(request1);
 
   error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain2, 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
+      CertVerifier::RequestParams(test_cert, domain2, 0, std::string()),
       &verify_result2, callback2.callback(), &request2, NetLogWithSource());
   EXPECT_THAT(error, IsError(ERR_IO_PENDING));
   EXPECT_TRUE(request2);
 
   error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain3, 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
+      CertVerifier::RequestParams(test_cert, domain3, 0, std::string()),
       &verify_result3, callback3.callback(), &request3, NetLogWithSource());
   EXPECT_THAT(error, IsError(ERR_IO_PENDING));
   EXPECT_TRUE(request3);
 
   // Start duplicate requests (which should join to existing jobs).
   error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain1, 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
+      CertVerifier::RequestParams(test_cert, domain1, 0, std::string()),
       &verify_result4, callback4.callback(), &request4, NetLogWithSource());
   EXPECT_THAT(error, IsError(ERR_IO_PENDING));
   EXPECT_TRUE(request4);
 
   error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain2, 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
+      CertVerifier::RequestParams(test_cert, domain2, 0, std::string()),
       &verify_result5, callback5.callback(), &request5, NetLogWithSource());
   EXPECT_THAT(error, IsError(ERR_IO_PENDING));
   EXPECT_TRUE(request5);
@@ -305,7 +289,7 @@
     verifier_.SetConfig(config);
 
     EXPECT_CALL(*mock_verify_proc_,
-                VerifyInternal(_, _, _, _, test_config.expected_flag, _, _, _))
+                VerifyInternal(_, _, _, test_config.expected_flag, _, _, _))
         .WillRepeatedly(
             DoAll(SetCertVerifyRevokedResult(), Return(ERR_CERT_REVOKED)));
 
@@ -314,8 +298,7 @@
     std::unique_ptr<CertVerifier::Request> request;
     int error = verifier_.Verify(
         CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                    /*ocsp_response=*/std::string(),
-                                    /*sct_list=*/std::string()),
+                                    std::string()),
         &verify_result, callback.callback(), &request, NetLogWithSource());
     ASSERT_THAT(error, IsError(ERR_IO_PENDING));
     EXPECT_TRUE(request);
diff --git a/net/cert/nss_cert_database_unittest.cc b/net/cert/nss_cert_database_unittest.cc
index d2480dd..367ca2574 100644
--- a/net/cert/nss_cert_database_unittest.cc
+++ b/net/cert/nss_cert_database_unittest.cc
@@ -573,10 +573,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      x509_found_server_cert.get(), "127.0.0.1",
-      /*ocsp_response=*/std::string(), /*sct_list=*/std::string(), flags,
-      crl_set_.get(), empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_found_server_cert.get(), "127.0.0.1",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
   EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result.cert_status);
 }
@@ -605,10 +604,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      x509_puny_cert.get(), "xn--wgv71a119e.com",
-      /*ocsp_response=*/std::string(), /*sct_list=*/std::string(), flags,
-      crl_set_.get(), empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_puny_cert.get(), "xn--wgv71a119e.com",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
   EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result.cert_status);
 }
@@ -638,10 +636,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      x509_puny_cert.get(), "xn--wgv71a119e.com",
-      /*ocsp_response=*/std::string(), /*sct_list=*/std::string(), flags,
-      crl_set_.get(), empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_puny_cert.get(), "xn--wgv71a119e.com",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
 }
@@ -674,10 +671,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      x509_server_cert.get(), "127.0.0.1",
-      /*ocsp_response=*/std::string(), /*sct_list=*/std::string(), flags,
-      crl_set_.get(), empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
 }
@@ -715,10 +711,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      x509_server_cert.get(), "127.0.0.1",
-      /*ocsp_response=*/std::string(), /*sct_list=*/std::string(), flags,
-      crl_set_.get(), empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
   EXPECT_EQ(CERT_STATUS_REVOKED, verify_result.cert_status);
 }
@@ -765,10 +760,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      x509_server_cert.get(), "127.0.0.1",
-      /*ocsp_response=*/std::string(), /*sct_list=*/std::string(), flags,
-      crl_set_.get(), empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
 
@@ -792,8 +786,7 @@
   // Server cert should fail to verify.
   CertVerifyResult verify_result2;
   error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
-                              /*ocsp_response=*/std::string(),
-                              /*sct_list=*/std::string(), flags, crl_set_.get(),
+                              std::string(), flags, crl_set_.get(),
                               empty_cert_list_, &verify_result2);
   EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
   EXPECT_EQ(CERT_STATUS_REVOKED, verify_result2.cert_status);
@@ -832,10 +825,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      x509_server_cert.get(), "127.0.0.1",
-      /*ocsp_response=*/std::string(), /*sct_list=*/std::string(), flags,
-      crl_set_.get(), empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
 
@@ -846,8 +838,7 @@
   // Server cert should fail to verify.
   CertVerifyResult verify_result2;
   error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
-                              /*ocsp_response=*/std::string(),
-                              /*sct_list=*/std::string(), flags, crl_set_.get(),
+                              std::string(), flags, crl_set_.get(),
                               empty_cert_list_, &verify_result2);
   EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
   EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result2.cert_status);
@@ -896,10 +887,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      x509_server_cert.get(), "127.0.0.1",
-      /*ocsp_response=*/std::string(), /*sct_list=*/std::string(), flags,
-      crl_set_.get(), empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
 
@@ -910,8 +900,7 @@
   // Server cert should fail to verify.
   CertVerifyResult verify_result2;
   error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
-                              /*ocsp_response=*/std::string(),
-                              /*sct_list=*/std::string(), flags, crl_set_.get(),
+                              std::string(), flags, crl_set_.get(),
                               empty_cert_list_, &verify_result2);
   EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
   EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result2.cert_status);
@@ -960,10 +949,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      x509_server_cert.get(), "127.0.0.1",
-      /*ocsp_response=*/std::string(), /*sct_list=*/std::string(), flags,
-      crl_set_.get(), empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
   EXPECT_EQ(CERT_STATUS_REVOKED, verify_result.cert_status);
 
@@ -974,8 +962,7 @@
   // Server cert should verify.
   CertVerifyResult verify_result2;
   error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
-                              /*ocsp_response=*/std::string(),
-                              /*sct_list=*/std::string(), flags, crl_set_.get(),
+                              std::string(), flags, crl_set_.get(),
                               empty_cert_list_, &verify_result2);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result2.cert_status);
diff --git a/net/cert/test_root_certs_unittest.cc b/net/cert/test_root_certs_unittest.cc
index 62e2365..8c2a5c0 100644
--- a/net/cert/test_root_certs_unittest.cc
+++ b/net/cert/test_root_certs_unittest.cc
@@ -91,10 +91,10 @@
   CertVerifyResult bad_verify_result;
   scoped_refptr<CertVerifyProc> verify_proc(
       CertVerifyProc::CreateDefault(/*cert_net_fetcher=*/nullptr));
-  int bad_status = verify_proc->Verify(
-      test_cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, net::CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &bad_verify_result);
+  int bad_status =
+      verify_proc->Verify(test_cert.get(), "127.0.0.1", std::string(), flags,
+                          net::CRLSet::BuiltinCRLSet().get(), CertificateList(),
+                          &bad_verify_result);
   EXPECT_NE(OK, bad_status);
   EXPECT_NE(0u, bad_verify_result.cert_status & CERT_STATUS_AUTHORITY_INVALID);
 
@@ -107,9 +107,8 @@
   // TestRootCerts is successfully imbuing trust.
   CertVerifyResult good_verify_result;
   int good_status = verify_proc->Verify(
-      test_cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &good_verify_result);
+      test_cert.get(), "127.0.0.1", std::string(), flags,
+      CRLSet::BuiltinCRLSet().get(), CertificateList(), &good_verify_result);
   EXPECT_THAT(good_status, IsOk());
   EXPECT_EQ(0u, good_verify_result.cert_status);
 
@@ -120,10 +119,10 @@
   // revert to their original state, and don't linger. If trust status
   // lingers, it will likely break other tests in net_unittests.
   CertVerifyResult restored_verify_result;
-  int restored_status = verify_proc->Verify(
-      test_cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
-      CertificateList(), &restored_verify_result);
+  int restored_status =
+      verify_proc->Verify(test_cert.get(), "127.0.0.1", std::string(), flags,
+                          CRLSet::BuiltinCRLSet().get(), CertificateList(),
+                          &restored_verify_result);
   EXPECT_NE(OK, restored_status);
   EXPECT_NE(0u,
             restored_verify_result.cert_status & CERT_STATUS_AUTHORITY_INVALID);
diff --git a/net/cert/trial_comparison_cert_verifier.cc b/net/cert/trial_comparison_cert_verifier.cc
index 7f96f29..c3e71d2 100644
--- a/net/cert/trial_comparison_cert_verifier.cc
+++ b/net/cert/trial_comparison_cert_verifier.cc
@@ -262,9 +262,9 @@
       }
       // Chains were different, reverify the trial_result_.verified_cert chain
       // using the platform verifier and compare results again.
-      RequestParams reverification_params(
-          trial_result_.verified_cert, params_.hostname(), params_.flags(),
-          params_.ocsp_response(), params_.sct_list());
+      RequestParams reverification_params(trial_result_.verified_cert,
+                                          params_.hostname(), params_.flags(),
+                                          params_.ocsp_response());
 
       int rv = cert_verifier_->primary_reverifier()->Verify(
           reverification_params, &reverification_result_,
diff --git a/net/cert/trial_comparison_cert_verifier_unittest.cc b/net/cert/trial_comparison_cert_verifier_unittest.cc
index 4d2c3a4..946523b 100644
--- a/net/cert/trial_comparison_cert_verifier_unittest.cc
+++ b/net/cert/trial_comparison_cert_verifier_unittest.cc
@@ -120,7 +120,6 @@
   int VerifyInternal(X509Certificate* cert,
                      const std::string& hostname,
                      const std::string& ocsp_response,
-                     const std::string& sct_list,
                      int flags,
                      CRLSet* crl_set,
                      const CertificateList& additional_trust_anchors,
@@ -138,7 +137,6 @@
     X509Certificate* cert,
     const std::string& hostname,
     const std::string& ocsp_response,
-    const std::string& sct_list,
     int flags,
     CRLSet* crl_set,
     const CertificateList& additional_trust_anchors,
@@ -163,7 +161,6 @@
   int VerifyInternal(X509Certificate* cert,
                      const std::string& hostname,
                      const std::string& ocsp_response,
-                     const std::string& sct_list,
                      int flags,
                      CRLSet* crl_set,
                      const CertificateList& additional_trust_anchors,
@@ -176,7 +173,6 @@
     X509Certificate* cert,
     const std::string& hostname,
     const std::string& ocsp_response,
-    const std::string& sct_list,
     int flags,
     CRLSet* crl_set,
     const CertificateList& additional_trust_anchors,
@@ -194,11 +190,10 @@
   MockCertVerifyProc() = default;
   // CertVerifyProc implementation:
   bool SupportsAdditionalTrustAnchors() const override { return false; }
-  MOCK_METHOD8(VerifyInternal,
+  MOCK_METHOD7(VerifyInternal,
                int(X509Certificate* cert,
                    const std::string& hostname,
                    const std::string& ocsp_response,
-                   const std::string& sct_list,
                    int flags,
                    CRLSet* crl_set,
                    const CertificateList& additional_trust_anchors,
@@ -288,9 +283,8 @@
       base::MakeRefCounted<FakeCertVerifyProc>(OK, dummy_result),
       base::MakeRefCounted<NotCalledCertVerifyProc>(),
       base::BindRepeating(&RecordTrialReport, &reports));
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -350,9 +344,8 @@
       false /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf, "t0.test", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf, "t0.test", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -365,9 +358,8 @@
 
   // Enable the trial and do another verification.
   verifier.set_trial_allowed(true);
-  CertVerifier::RequestParams params2(leaf, "t1.test", /*flags=*/0,
-                                      /*ocsp_response=*/std::string(),
-                                      /*sct_list=*/std::string());
+  CertVerifier::RequestParams params2(leaf, "t1.test", 0 /* flags */,
+                                      std::string() /* ocsp_response */);
   CertVerifyResult result2;
   TestCompletionCallback callback2;
   std::unique_ptr<CertVerifier::Request> request2;
@@ -432,9 +424,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf, "t0.test", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf, "t0.test", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -447,9 +438,8 @@
 
   // Disable the trial and do another verification.
   verifier.set_trial_allowed(false);
-  CertVerifier::RequestParams params2(leaf, "t1.test", /*flags=*/0,
-                                      /*ocsp_response=*/std::string(),
-                                      /*sct_list=*/std::string());
+  CertVerifier::RequestParams params2(leaf, "t1.test", 0 /* flags */,
+                                      std::string() /* ocsp_response */);
   CertVerifyResult result2;
   TestCompletionCallback callback2;
   std::unique_ptr<CertVerifier::Request> request2;
@@ -492,9 +482,8 @@
       base::MakeRefCounted<NotCalledCertVerifyProc>(),
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -543,9 +532,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -598,9 +586,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -645,9 +632,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -708,9 +694,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -769,9 +754,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -827,9 +811,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -881,12 +864,12 @@
       base::MakeRefCounted<MockCertVerifyProc>();
   // Primary verifier returns ok status and chain1 if verifying the leaf alone.
   EXPECT_CALL(*verify_proc1,
-              VerifyInternal(leaf_cert_1_.get(), _, _, _, _, _, _, _))
-      .WillRepeatedly(DoAll(SetArgPointee<7>(chain1_result), Return(OK)));
+              VerifyInternal(leaf_cert_1_.get(), _, _, _, _, _, _))
+      .WillRepeatedly(DoAll(SetArgPointee<6>(chain1_result), Return(OK)));
   // Primary verifier returns ok status and chain2 if verifying chain2.
   EXPECT_CALL(*verify_proc1,
-              VerifyInternal(cert_chain_2_.get(), _, _, _, _, _, _, _))
-      .WillRepeatedly(DoAll(SetArgPointee<7>(chain2_result), Return(OK)));
+              VerifyInternal(cert_chain_2_.get(), _, _, _, _, _, _))
+      .WillRepeatedly(DoAll(SetArgPointee<6>(chain2_result), Return(OK)));
 
   // Trial verifier returns ok status and chain2.
   scoped_refptr<FakeCertVerifyProc> verify_proc2 =
@@ -897,9 +880,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -983,14 +965,13 @@
       base::MakeRefCounted<MockCertVerifyProc>();
   // Primary verifier returns ok status and different_chain if verifying leaf
   // alone.
-  EXPECT_CALL(*verify_proc1, VerifyInternal(leaf.get(), _, _, _, _, _, _, _))
+  EXPECT_CALL(*verify_proc1, VerifyInternal(leaf.get(), _, _, _, _, _, _))
       .WillRepeatedly(
-          DoAll(SetArgPointee<7>(different_chain_result), Return(OK)));
+          DoAll(SetArgPointee<6>(different_chain_result), Return(OK)));
   // Primary verifier returns ok status and nonev_chain_result if verifying
   // cert_chain.
-  EXPECT_CALL(*verify_proc1,
-              VerifyInternal(cert_chain.get(), _, _, _, _, _, _, _))
-      .WillRepeatedly(DoAll(SetArgPointee<7>(nonev_chain_result), Return(OK)));
+  EXPECT_CALL(*verify_proc1, VerifyInternal(cert_chain.get(), _, _, _, _, _, _))
+      .WillRepeatedly(DoAll(SetArgPointee<6>(nonev_chain_result), Return(OK)));
 
   // Trial verifier returns ok status and ev_chain_result.
   scoped_refptr<FakeCertVerifyProc> verify_proc2 =
@@ -1001,9 +982,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf, "test.example", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf, "test.example", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -1060,8 +1040,7 @@
   verifier.SetConfig(config);
 
   CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -1124,9 +1103,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
 
   // Start first verification request.
   CertVerifyResult result_1;
@@ -1200,9 +1178,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   std::unique_ptr<CertVerifier::Request> request;
   int error =
@@ -1258,9 +1235,8 @@
       base::MakeRefCounted<NotCalledCertVerifyProc>(),
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   std::unique_ptr<CertVerifier::Request> request;
   int error =
@@ -1310,9 +1286,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -1364,24 +1339,23 @@
   scoped_refptr<MockCertVerifyProc> verify_proc2 =
       base::MakeRefCounted<MockCertVerifyProc>();
   // Secondary verifier returns ok status...
-  EXPECT_CALL(*verify_proc2, VerifyInternal(_, _, _, _, _, _, _, _))
-      .WillRepeatedly(DoAll(SetArgPointee<7>(ok_result), Return(OK)));
+  EXPECT_CALL(*verify_proc2, VerifyInternal(_, _, _, _, _, _, _))
+      .WillRepeatedly(DoAll(SetArgPointee<6>(ok_result), Return(OK)));
   // ...unless it was called with REV_CHECKING_ENABLED.
   EXPECT_CALL(
       *verify_proc2,
-      VerifyInternal(_, _, _, _, CertVerifyProc::VERIFY_REV_CHECKING_ENABLED, _,
-                     _, _))
+      VerifyInternal(_, _, _, CertVerifyProc::VERIFY_REV_CHECKING_ENABLED, _, _,
+                     _))
       .WillRepeatedly(
-          DoAll(SetArgPointee<7>(revoked_result), Return(ERR_CERT_REVOKED)));
+          DoAll(SetArgPointee<6>(revoked_result), Return(ERR_CERT_REVOKED)));
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -1436,17 +1410,16 @@
   // REV_CHECKING_ENABLED was passed.
   scoped_refptr<MockCertVerifyProc> verify_proc2 =
       base::MakeRefCounted<MockCertVerifyProc>();
-  EXPECT_CALL(*verify_proc2, VerifyInternal(_, _, _, _, _, _, _, _))
-      .WillRepeatedly(DoAll(SetArgPointee<7>(ok_result), Return(OK)));
+  EXPECT_CALL(*verify_proc2, VerifyInternal(_, _, _, _, _, _, _))
+      .WillRepeatedly(DoAll(SetArgPointee<6>(ok_result), Return(OK)));
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -1521,9 +1494,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -1585,9 +1557,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -1652,9 +1623,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
@@ -1701,9 +1671,8 @@
       true /* initial_allowed */, verify_proc1, verify_proc2,
       base::BindRepeating(&RecordTrialReport, &reports));
 
-  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
   CertVerifyResult result;
   TestCompletionCallback callback;
   std::unique_ptr<CertVerifier::Request> request;
diff --git a/net/cert_net/nss_ocsp_unittest.cc b/net/cert_net/nss_ocsp_unittest.cc
index 79984fff..9c42f2f 100644
--- a/net/cert_net/nss_ocsp_unittest.cc
+++ b/net/cert_net/nss_ocsp_unittest.cc
@@ -150,8 +150,7 @@
   int flags = 0;
   int error = verifier()->Verify(
       CertVerifier::RequestParams(test_cert, "aia-host.invalid", flags,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
+                                  std::string()),
       &verify_result, test_callback.callback(), &request, NetLogWithSource());
   ASSERT_THAT(error, IsError(ERR_IO_PENDING));
 
diff --git a/net/data/ssl/certificates/README b/net/data/ssl/certificates/README
index 32c7d287..a33a5bf 100644
--- a/net/data/ssl/certificates/README
+++ b/net/data/ssl/certificates/README
@@ -69,11 +69,6 @@
 - www.ahrn.com.pem: A certificate issued by the Legacy Symantec PKI in 2014,
   expires on 2019-10-27.
 
-- treadclimber.pem: A chain where the leaf does not contain embedded SCTs,
-  and which has a notBefore date after 2018/10/15. Expires 2020/02/07.
-- treadclimber.sctlist: The TLS encoded SignedCertificateTimestampList for the
-  treadclimber.pem leaf certificate.
-
 ===== Manually generated certificates
 - client.p12 : A PKCS #12 file containing a client certificate and a private
      key created for testing.  The password is "12345".
diff --git a/net/data/ssl/certificates/treadclimber.pem b/net/data/ssl/certificates/treadclimber.pem
deleted file mode 100644
index bfbf8594..0000000
--- a/net/data/ssl/certificates/treadclimber.pem
+++ /dev/null
@@ -1,227 +0,0 @@
-===========================================
-Certificate0: 2f3d743c3627324a11549a9e5cfd1845e6dfdd63771509f0903456411c60bda8
-===========================================
-Certificate:
-    Data:
-        Version: 3 (0x2)
-        Serial Number:
-            8c:7f:32:24:e1:a7:ab:96:00:00:00:00:50:ed:50:f3
-        Signature Algorithm: sha256WithRSAEncryption
-        Issuer: C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2012 Entrust, Inc. - for authorized use only", CN = Entrust Certification Authority - L1K
-        Validity
-            Not Before: Feb  7 17:50:22 2019 GMT
-            Not After : Feb  7 18:20:21 2020 GMT
-        Subject: C = US, ST = Washington, L = Vancouver, O = "Nautilus, Inc.", CN = Treadclimber.com
-        Subject Public Key Info:
-            Public Key Algorithm: rsaEncryption
-                RSA Public-Key: (2048 bit)
-                Modulus:
-                    00:c5:4a:d9:1b:cb:70:54:d7:7d:57:83:53:8e:22:
-                    ad:aa:d2:41:4a:b7:39:4e:7f:99:66:a9:7e:4f:2b:
-                    a7:ed:17:4c:da:3b:8d:07:bd:f1:90:0b:8d:05:4d:
-                    e0:dc:3c:19:ef:2e:fe:a3:38:30:c4:9e:8d:56:34:
-                    91:fe:25:65:8d:ad:46:a2:f1:fb:d0:5f:63:73:8d:
-                    29:f8:09:f6:9c:4d:10:5e:63:5e:bf:66:24:0c:70:
-                    3b:88:8f:f4:c6:ee:8f:31:68:05:0e:89:7a:7a:96:
-                    3a:2b:50:71:97:d3:a0:f1:95:49:11:e2:f9:c8:78:
-                    54:63:e8:6a:db:96:87:2d:de:18:ea:f2:43:4c:8d:
-                    3f:a5:76:27:fa:d9:e3:25:ba:96:60:9c:9a:9a:a9:
-                    d3:37:3c:9c:49:28:f4:54:ce:c0:2a:e1:ce:9f:de:
-                    f8:b5:3f:d9:1c:aa:9c:3c:9c:d0:f7:67:84:3a:3a:
-                    bf:19:bf:e2:44:b3:13:3a:d5:94:f3:db:eb:67:7e:
-                    da:50:9a:4a:40:e9:46:f6:5f:65:b5:21:ae:e2:4e:
-                    4d:02:fe:33:7f:b3:aa:0c:9c:cf:97:44:fe:28:15:
-                    64:26:ca:fb:07:8c:4f:40:b9:df:f5:fb:7b:e4:81:
-                    0e:85:16:4b:7e:80:74:df:62:3a:dd:1e:30:e8:11:
-                    3a:bf
-                Exponent: 65537 (0x10001)
-        X509v3 extensions:
-            X509v3 Key Usage: critical
-                Digital Signature, Key Encipherment
-            X509v3 Extended Key Usage: 
-                TLS Web Server Authentication, TLS Web Client Authentication
-            X509v3 CRL Distribution Points: 
-
-                Full Name:
-                  URI:http://crl.entrust.net/level1k.crl
-
-            X509v3 Certificate Policies: 
-                Policy: 2.16.840.1.114028.10.1.5
-                  CPS: http://www.entrust.net/rpa
-                Policy: 2.23.140.1.2.2
-
-            Authority Information Access: 
-                OCSP - URI:http://ocsp.entrust.net
-                CA Issuers - URI:http://aia.entrust.net/l1k-chain256.cer
-
-            X509v3 Subject Alternative Name: 
-                DNS:Treadclimber.com, DNS:www.Treadclimber.com, DNS:bowflexcatalog.com, DNS:www.bowflexcatalog.com, DNS:bowflexhomegyms.com, DNS:www.bowflexhomegyms.com, DNS:bowflexmaxtrainer.com, DNS:www.bowflexmaxtrainer.com, DNS:bowflexselecttech.com, DNS:www.bowflexselecttech.com, DNS:bowflexhvt.com, DNS:www.bowflexhvt.com, DNS:meethvt.com, DNS:www.meethvt.com, DNS:universalhomefitness.com, DNS:www.universalhomefitness.com, DNS:bowflexmaxtrainer.ca, DNS:www.bowflexmaxtrainer.ca, DNS:treadclimber.ca, DNS:www.treadclimber.ca
-            X509v3 Authority Key Identifier: 
-                keyid:82:A2:70:74:DD:BC:53:3F:CF:7B:D4:F7:CD:7F:A7:60:C6:0A:4C:BF
-
-            X509v3 Subject Key Identifier: 
-                DB:F8:04:79:71:73:4C:9D:47:E0:52:C2:8A:24:7E:C4:A6:14:D2:90
-            X509v3 Basic Constraints: 
-                CA:FALSE
-    Signature Algorithm: sha256WithRSAEncryption
-         29:86:2a:a0:6f:bf:b7:3b:7e:9e:c3:e1:bb:28:9f:45:9f:0d:
-         97:fc:24:a0:9c:28:6e:7c:50:3d:c2:ee:24:50:d1:c3:55:79:
-         7b:10:94:50:4c:47:91:f5:f7:b3:d9:05:62:73:26:6f:8d:8e:
-         18:b5:44:ae:55:32:0d:9d:12:36:a5:3b:df:d0:c1:07:7b:f4:
-         a5:8b:7a:64:b6:8b:d1:de:4f:fb:6e:64:a1:e8:95:f4:69:d7:
-         d3:f3:5c:75:ff:24:da:4e:e5:94:4d:e2:06:dc:87:de:2f:48:
-         fe:69:8e:77:ac:7f:3c:6b:cc:57:08:a7:9a:c7:9d:b3:37:9b:
-         0a:a6:54:0c:a4:08:e8:95:63:73:95:c5:d5:5a:7e:41:57:d2:
-         a7:ca:45:ba:2c:4d:46:00:2d:3d:95:02:e2:34:08:91:43:7b:
-         c5:aa:e1:af:cd:f1:f4:f4:b6:e2:43:f6:8d:ff:2d:7a:26:13:
-         f8:99:be:77:92:90:b9:86:5f:93:2c:c6:61:37:56:0f:2a:1b:
-         ed:ad:68:fc:16:aa:ef:b6:3e:3e:3c:97:b6:8e:e2:72:ef:cc:
-         47:9a:4c:e6:55:ea:b7:81:60:bf:d7:c4:23:a8:06:6e:02:0f:
-         85:2b:70:8c:0a:3d:fc:5c:be:21:46:e8:33:0f:dd:18:d3:ad:
-         8b:d0:b6:6a
-
------BEGIN CERTIFICATE-----
-MIIG4zCCBcugAwIBAgIRAIx/MiThp6uWAAAAAFDtUPMwDQYJKoZIhvcNAQELBQAw
-gboxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL
-Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg
-MjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxLjAs
-BgNVBAMTJUVudHJ1c3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBMMUswHhcN
-MTkwMjA3MTc1MDIyWhcNMjAwMjA3MTgyMDIxWjBqMQswCQYDVQQGEwJVUzETMBEG
-A1UECBMKV2FzaGluZ3RvbjESMBAGA1UEBxMJVmFuY291dmVyMRcwFQYDVQQKEw5O
-YXV0aWx1cywgSW5jLjEZMBcGA1UEAxMQVHJlYWRjbGltYmVyLmNvbTCCASIwDQYJ
-KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVK2RvLcFTXfVeDU44irarSQUq3OU5/
-mWapfk8rp+0XTNo7jQe98ZALjQVN4Nw8Ge8u/qM4MMSejVY0kf4lZY2tRqLx+9Bf
-Y3ONKfgJ9pxNEF5jXr9mJAxwO4iP9MbujzFoBQ6JenqWOitQcZfToPGVSRHi+ch4
-VGPoatuWhy3eGOryQ0yNP6V2J/rZ4yW6lmCcmpqp0zc8nEko9FTOwCrhzp/e+LU/
-2RyqnDyc0PdnhDo6vxm/4kSzEzrVlPPb62d+2lCaSkDpRvZfZbUhruJOTQL+M3+z
-qgycz5dE/igVZCbK+weMT0C53/X7e+SBDoUWS36AdN9iOt0eMOgROr8CAwEAAaOC
-AzEwggMtMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
-BQUHAwIwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5lbnRydXN0Lm5ldC9s
-ZXZlbDFrLmNybDBLBgNVHSAERDBCMDYGCmCGSAGG+mwKAQUwKDAmBggrBgEFBQcC
-ARYaaHR0cDovL3d3dy5lbnRydXN0Lm5ldC9ycGEwCAYGZ4EMAQICMGgGCCsGAQUF
-BwEBBFwwWjAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZW50cnVzdC5uZXQwMwYI
-KwYBBQUHMAKGJ2h0dHA6Ly9haWEuZW50cnVzdC5uZXQvbDFrLWNoYWluMjU2LmNl
-cjCCAcMGA1UdEQSCAbowggG2ghBUcmVhZGNsaW1iZXIuY29tghR3d3cuVHJlYWRj
-bGltYmVyLmNvbYISYm93ZmxleGNhdGFsb2cuY29tghZ3d3cuYm93ZmxleGNhdGFs
-b2cuY29tghNib3dmbGV4aG9tZWd5bXMuY29tghd3d3cuYm93ZmxleGhvbWVneW1z
-LmNvbYIVYm93ZmxleG1heHRyYWluZXIuY29tghl3d3cuYm93ZmxleG1heHRyYWlu
-ZXIuY29tghVib3dmbGV4c2VsZWN0dGVjaC5jb22CGXd3dy5ib3dmbGV4c2VsZWN0
-dGVjaC5jb22CDmJvd2ZsZXhodnQuY29tghJ3d3cuYm93ZmxleGh2dC5jb22CC21l
-ZXRodnQuY29tgg93d3cubWVldGh2dC5jb22CGHVuaXZlcnNhbGhvbWVmaXRuZXNz
-LmNvbYIcd3d3LnVuaXZlcnNhbGhvbWVmaXRuZXNzLmNvbYIUYm93ZmxleG1heHRy
-YWluZXIuY2GCGHd3dy5ib3dmbGV4bWF4dHJhaW5lci5jYYIPdHJlYWRjbGltYmVy
-LmNhghN3d3cudHJlYWRjbGltYmVyLmNhMB8GA1UdIwQYMBaAFIKicHTdvFM/z3vU
-981/p2DGCky/MB0GA1UdDgQWBBTb+AR5cXNMnUfgUsKKJH7EphTSkDAJBgNVHRME
-AjAAMA0GCSqGSIb3DQEBCwUAA4IBAQAphiqgb7+3O36ew+G7KJ9Fnw2X/CSgnChu
-fFA9wu4kUNHDVXl7EJRQTEeR9fez2QVicyZvjY4YtUSuVTINnRI2pTvf0MEHe/Sl
-i3pktovR3k/7bmSh6JX0adfT81x1/yTaTuWUTeIG3IfeL0j+aY53rH88a8xXCKea
-x52zN5sKplQMpAjolWNzlcXVWn5BV9KnykW6LE1GAC09lQLiNAiRQ3vFquGvzfH0
-9LbiQ/aN/y16JhP4mb53kpC5hl+TLMZhN1YPKhvtrWj8Fqrvtj4+PJe2juJy78xH
-mkzmVeq3gWC/18QjqAZuAg+FK3CMCj38XL4hRugzD90Y062L0LZq
------END CERTIFICATE-----
-
-===========================================
-Certificate1: d6c3fc493bacd1df8a1ba30f4ae26254b2a4528e4876081eacc6a16a090aa36a
-===========================================
-Certificate:
-    Data:
-        Version: 3 (0x2)
-        Serial Number: 1372455166 (0x51ce00fe)
-        Signature Algorithm: sha256WithRSAEncryption
-        Issuer: O = Entrust.net, OU = www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU = (c) 1999 Entrust.net Limited, CN = Entrust.net Certification Authority (2048)
-        Validity
-            Not Before: Oct 10 15:23:17 2014 GMT
-            Not After : Oct 11 06:22:47 2024 GMT
-        Subject: C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2012 Entrust, Inc. - for authorized use only", CN = Entrust Certification Authority - L1K
-        Subject Public Key Info:
-            Public Key Algorithm: rsaEncryption
-                RSA Public-Key: (2048 bit)
-                Modulus:
-                    00:da:3f:96:d0:4d:b9:2f:44:e7:db:39:5e:9b:50:
-                    ee:5c:a5:61:da:41:67:53:09:aa:00:9a:8e:57:7f:
-                    29:6b:db:c7:e1:21:24:aa:3a:d0:8d:47:23:d2:ed:
-                    72:16:f0:91:21:d2:5d:b7:b8:4b:a8:83:8f:b7:91:
-                    32:68:cf:ce:25:93:2c:b2:7d:97:c8:fe:c1:b4:17:
-                    ba:09:9e:03:90:93:7b:7c:49:83:22:68:8a:9b:de:
-                    47:c3:31:98:7a:2e:7d:40:0b:d2:ef:3e:d3:b2:8c:
-                    aa:8f:48:a9:ff:00:e8:29:58:06:f7:b6:93:5a:94:
-                    73:26:26:ad:58:0e:e5:42:b8:d5:ea:73:79:64:68:
-                    53:25:b8:84:cf:94:7a:ae:06:45:0c:a3:6b:4d:d0:
-                    c6:be:ea:18:a4:36:f0:92:b2:ba:1c:88:8f:3a:52:
-                    7f:f7:5e:6d:83:1c:9d:f0:1f:e5:c3:d6:dd:a5:78:
-                    92:3d:b0:6d:2c:ea:c9:cf:94:41:19:71:44:68:ba:
-                    47:3c:04:e9:5d:ba:3e:f0:35:f7:15:b6:9e:f2:2e:
-                    15:1e:3f:47:c8:c8:38:a7:73:45:5d:4d:b0:3b:b1:
-                    8e:17:29:37:ea:dd:05:01:22:bb:94:36:2a:8d:5b:
-                    35:fe:53:19:2f:08:46:c1:2a:b3:1a:62:1d:4e:2b:
-                    d9:1b
-                Exponent: 65537 (0x10001)
-        X509v3 extensions:
-            X509v3 Key Usage: critical
-                Certificate Sign, CRL Sign
-            X509v3 Basic Constraints: critical
-                CA:TRUE, pathlen:0
-            Authority Information Access: 
-                OCSP - URI:http://ocsp.entrust.net
-
-            X509v3 CRL Distribution Points: 
-
-                Full Name:
-                  URI:http://crl.entrust.net/2048ca.crl
-
-            X509v3 Certificate Policies: 
-                Policy: X509v3 Any Policy
-                  CPS: http://www.entrust.net/rpa
-
-            X509v3 Subject Key Identifier: 
-                82:A2:70:74:DD:BC:53:3F:CF:7B:D4:F7:CD:7F:A7:60:C6:0A:4C:BF
-            X509v3 Authority Key Identifier: 
-                keyid:55:E4:81:D1:11:80:BE:D8:89:B9:08:A3:31:F9:A1:24:09:16:B9:70
-
-    Signature Algorithm: sha256WithRSAEncryption
-         58:42:71:c1:3e:29:16:6e:75:58:77:b5:65:4d:9e:29:f5:ae:
-         0b:1c:18:f9:a3:08:43:75:ef:7d:6d:f1:92:ea:fe:ce:68:5c:
-         e2:1f:be:a5:af:1a:4c:aa:83:3b:5f:a2:47:46:f7:7c:9e:c1:
-         83:c4:7a:24:b0:e9:cc:e9:a2:9a:07:09:e8:1e:1d:77:56:49:
-         fc:53:73:a8:47:cc:c9:2d:5a:60:34:a7:1a:0b:e5:2b:b8:df:
-         ef:82:4a:dd:70:5e:10:18:08:3b:5d:dc:8a:84:3d:68:d8:00:
-         b4:c4:9e:43:78:4b:5e:f0:62:6a:8c:90:66:53:8a:ac:c5:7d:
-         58:ff:4e:a9:ad:d7:a4:ca:12:47:29:e5:f3:22:21:40:32:60:
-         da:3a:fe:92:54:1e:43:a1:0d:a9:52:37:60:bf:87:c4:a1:c7:
-         78:d5:87:1e:e5:77:e3:5f:5b:dc:71:6d:ba:44:87:31:05:80:
-         58:0b:c5:de:74:28:81:83:08:84:d0:c8:46:5a:fe:8a:c6:bd:
-         a9:0e:3b:64:78:6d:26:dc:3c:4c:f7:81:5c:3c:11:7f:25:3a:
-         93:62:a5:a3:91:05:25:23:73:b4:cd:ce:cc:39:a4:03:78:30:
-         66:46:5e:a9:75:b0:b4:67:03:a9:b1:9f:57:f0:d3:76:cf:e1:
-         93:e8:80:a2
-
------BEGIN CERTIFICATE-----
-MIIE/jCCA+agAwIBAgIEUc4A/jANBgkqhkiG9w0BAQsFADCBtDEUMBIGA1UEChML
-RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
-bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
-IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
-ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw0xNDEwMTAxNTIzMTdaFw0yNDEw
-MTEwNjIyNDdaMIG6MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5j
-LjEoMCYGA1UECxMfU2VlIHd3dy5lbnRydXN0Lm5ldC9sZWdhbC10ZXJtczE5MDcG
-A1UECxMwKGMpIDIwMTIgRW50cnVzdCwgSW5jLiAtIGZvciBhdXRob3JpemVkIHVz
-ZSBvbmx5MS4wLAYDVQQDEyVFbnRydXN0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
-IC0gTDFLMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2j+W0E25L0Tn
-2zlem1DuXKVh2kFnUwmqAJqOV38pa9vH4SEkqjrQjUcj0u1yFvCRIdJdt7hLqIOP
-t5EyaM/OJZMssn2XyP7BtBe6CZ4DkJN7fEmDImiKm95HwzGYei59QAvS7z7Tsoyq
-j0ip/wDoKVgG97aTWpRzJiatWA7lQrjV6nN5ZGhTJbiEz5R6rgZFDKNrTdDGvuoY
-pDbwkrK6HIiPOlJ/915tgxyd8B/lw9bdpXiSPbBtLOrJz5RBGXFEaLpHPATpXbo+
-8DX3Fbae8i4VHj9HyMg4p3NFXU2wO7GOFyk36t0FASK7lDYqjVs1/lMZLwhGwSqz
-GmIdTivZGwIDAQABo4IBDjCCAQowDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI
-MAYBAf8CAQAwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdodHRwOi8vb2Nz
-cC5lbnRydXN0Lm5ldDAyBgNVHR8EKzApMCegJaAjhiFodHRwOi8vY3JsLmVudHJ1
-c3QubmV0LzIwNDhjYS5jcmwwOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUH
-AgEWGmh0dHA6Ly93d3cuZW50cnVzdC5uZXQvcnBhMB0GA1UdDgQWBBSConB03bxT
-P8971PfNf6dgxgpMvzAfBgNVHSMEGDAWgBRV5IHREYC+2Im5CKMx+aEkCRa5cDAN
-BgkqhkiG9w0BAQsFAAOCAQEAWEJxwT4pFm51WHe1ZU2eKfWuCxwY+aMIQ3XvfW3x
-kur+zmhc4h++pa8aTKqDO1+iR0b3fJ7Bg8R6JLDpzOmimgcJ6B4dd1ZJ/FNzqEfM
-yS1aYDSnGgvlK7jf74JK3XBeEBgIO13cioQ9aNgAtMSeQ3hLXvBiaoyQZlOKrMV9
-WP9Oqa3XpMoSRynl8yIhQDJg2jr+klQeQ6ENqVI3YL+HxKHHeNWHHuV3419b3HFt
-ukSHMQWAWAvF3nQogYMIhNDIRlr+isa9qQ47ZHhtJtw8TPeBXDwRfyU6k2Klo5EF
-JSNztM3OzDmkA3gwZkZeqXWwtGcDqbGfV/DTds/hk+iAog==
------END CERTIFICATE-----
-
diff --git a/net/data/ssl/certificates/treadclimber.sctlist b/net/data/ssl/certificates/treadclimber.sctlist
deleted file mode 100644
index 268a0ac..0000000
--- a/net/data/ssl/certificates/treadclimber.sctlist
+++ /dev/null
Binary files differ
diff --git a/net/quic/address_utils.h b/net/quic/address_utils.h
new file mode 100644
index 0000000..f067db7
--- /dev/null
+++ b/net/quic/address_utils.h
@@ -0,0 +1,48 @@
+#ifndef NET_QUIC_ADDRESS_UTILS_H_
+#define NET_QUIC_ADDRESS_UTILS_H_
+
+#include "net/base/ip_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address_family.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+
+namespace net {
+
+inline IPEndPoint ToIPEndPoint(quic::QuicSocketAddress address) {
+  if (!address.IsInitialized()) {
+    return IPEndPoint();
+  }
+
+  IPEndPoint result;
+  sockaddr_storage storage = address.generic_address();
+  const bool success = result.FromSockAddr(
+      reinterpret_cast<const sockaddr*>(&storage), sizeof(storage));
+  DCHECK(success);
+  return result;
+}
+
+inline IPAddress ToIPAddress(quic::QuicIpAddress address) {
+  if (!address.IsInitialized()) {
+    return IPAddress();
+  }
+
+  switch (address.address_family()) {
+    case quic::IpAddressFamily::IP_V4: {
+      in_addr raw_address = address.GetIPv4();
+      return IPAddress(reinterpret_cast<const uint8_t*>(&raw_address),
+                       sizeof(raw_address));
+    }
+    case quic::IpAddressFamily::IP_V6: {
+      in6_addr raw_address = address.GetIPv6();
+      return IPAddress(reinterpret_cast<const uint8_t*>(&raw_address),
+                       sizeof(raw_address));
+    }
+    default:
+      DCHECK_EQ(address.address_family(), quic::IpAddressFamily::IP_UNSPEC);
+      return IPAddress();
+  }
+}
+
+}  // namespace net
+
+#endif  // NET_QUIC_ADDRESS_UTILS_H_
diff --git a/net/quic/crypto/proof_verifier_chromium.cc b/net/quic/crypto/proof_verifier_chromium.cc
index e222a7e..366894c 100644
--- a/net/quic/crypto/proof_verifier_chromium.cc
+++ b/net/quic/crypto/proof_verifier_chromium.cc
@@ -80,8 +80,6 @@
   quic::QuicAsyncStatus VerifyCertChain(
       const std::string& hostname,
       const std::vector<std::string>& certs,
-      const std::string& ocsp_response,
-      const std::string& cert_sct,
       std::string* error_details,
       std::unique_ptr<quic::ProofVerifyDetails>* verify_details,
       std::unique_ptr<quic::ProofVerifierCallback> callback);
@@ -103,8 +101,6 @@
   quic::QuicAsyncStatus VerifyCert(
       const string& hostname,
       const uint16_t port,
-      const std::string& ocsp_response,
-      const std::string& cert_sct,
       std::string* error_details,
       std::unique_ptr<quic::ProofVerifyDetails>* verify_details,
       std::unique_ptr<quic::ProofVerifierCallback> callback);
@@ -137,10 +133,6 @@
   std::string hostname_;
   // |port| specifies the target port for the connection.
   uint16_t port_;
-  // Encoded stapled OCSP response for |certs|.
-  std::string ocsp_response_;
-  // Encoded SignedCertificateTimestampList for |certs|.
-  std::string cert_sct_;
 
   std::unique_ptr<quic::ProofVerifierCallback> callback_;
   std::unique_ptr<ProofVerifyDetailsChromium> verify_details_;
@@ -250,15 +242,13 @@
   }
 
   DCHECK(enforce_policy_checking_);
-  return VerifyCert(hostname, port, /*ocsp_response=*/std::string(), cert_sct,
-                    error_details, verify_details, std::move(callback));
+  return VerifyCert(hostname, port, error_details, verify_details,
+                    std::move(callback));
 }
 
 quic::QuicAsyncStatus ProofVerifierChromium::Job::VerifyCertChain(
     const string& hostname,
     const std::vector<string>& certs,
-    const std::string& ocsp_response,
-    const std::string& cert_sct,
     std::string* error_details,
     std::unique_ptr<quic::ProofVerifyDetails>* verify_details,
     std::unique_ptr<quic::ProofVerifierCallback> callback) {
@@ -282,8 +272,8 @@
 
   enforce_policy_checking_ = false;
   // |port| is not needed because |enforce_policy_checking_| is false.
-  return VerifyCert(hostname, /*port=*/0, ocsp_response, cert_sct,
-                    error_details, verify_details, std::move(callback));
+  return VerifyCert(hostname, /*port=*/0, error_details, verify_details,
+                    std::move(callback));
 }
 
 bool ProofVerifierChromium::Job::GetX509Certificate(
@@ -317,15 +307,11 @@
 quic::QuicAsyncStatus ProofVerifierChromium::Job::VerifyCert(
     const string& hostname,
     const uint16_t port,
-    const std::string& ocsp_response,
-    const std::string& cert_sct,
     std::string* error_details,
     std::unique_ptr<quic::ProofVerifyDetails>* verify_details,
     std::unique_ptr<quic::ProofVerifierCallback> callback) {
   hostname_ = hostname;
   port_ = port;
-  ocsp_response_ = ocsp_response;
-  cert_sct_ = cert_sct;
 
   next_state_ = STATE_VERIFY_CERT;
   switch (DoLoop(OK)) {
@@ -383,7 +369,7 @@
 
   return verifier_->Verify(
       CertVerifier::RequestParams(cert_, hostname_, cert_verify_flags_,
-                                  ocsp_response_, cert_sct_),
+                                  std::string()),
       &verify_details_->cert_verify_result,
       base::Bind(&ProofVerifierChromium::Job::OnIOComplete,
                  base::Unretained(this)),
@@ -630,15 +616,15 @@
     *error_details = "Missing context";
     return quic::QUIC_FAILURE;
   }
+  // TODO(mattm): use |ocsp_response| and |cert_sct|.
   const ProofVerifyContextChromium* chromium_context =
       reinterpret_cast<const ProofVerifyContextChromium*>(verify_context);
   std::unique_ptr<Job> job = std::make_unique<Job>(
       this, cert_verifier_, ct_policy_enforcer_, transport_security_state_,
       cert_transparency_verifier_, chromium_context->cert_verify_flags,
       chromium_context->net_log);
-  quic::QuicAsyncStatus status =
-      job->VerifyCertChain(hostname, certs, ocsp_response, cert_sct,
-                           error_details, verify_details, std::move(callback));
+  quic::QuicAsyncStatus status = job->VerifyCertChain(
+      hostname, certs, error_details, verify_details, std::move(callback));
   if (status == quic::QUIC_PENDING) {
     Job* job_ptr = job.get();
     active_jobs_[job_ptr] = std::move(job);
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index 8afe739..fdeda72 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -27,6 +27,7 @@
 #include "net/http/transport_security_state.h"
 #include "net/log/net_log_event_type.h"
 #include "net/log/net_log_source_type.h"
+#include "net/quic/address_utils.h"
 #include "net/quic/crypto/proof_verifier_chromium.h"
 #include "net/quic/quic_chromium_connection_helper.h"
 #include "net/quic/quic_chromium_packet_writer.h"
@@ -491,7 +492,7 @@
   if (!session_)
     return ERR_CONNECTION_CLOSED;
 
-  *address = session_->peer_address().impl().socket_address();
+  *address = ToIPEndPoint(session_->peer_address());
   return OK;
 }
 
@@ -500,7 +501,7 @@
   if (!session_)
     return ERR_CONNECTION_CLOSED;
 
-  *address = session_->self_address().impl().socket_address();
+  *address = ToIPEndPoint(session_->self_address());
   return OK;
 }
 
@@ -829,17 +830,17 @@
 
   net_log_.EndEvent(NetLogEventType::QUIC_SESSION);
   DCHECK(waiting_for_confirmation_callbacks_.empty());
-  if (!dynamic_streams().empty())
+  if (HasActiveRequestStreams())
     RecordUnexpectedOpenStreams(DESTRUCTOR);
   if (!handles_.empty())
     RecordUnexpectedObservers(DESTRUCTOR);
   if (!going_away_)
     RecordUnexpectedNotGoingAway(DESTRUCTOR);
 
-  while (!dynamic_streams().empty() || !handles_.empty() ||
+  while (HasActiveRequestStreams() || !handles_.empty() ||
          !stream_requests_.empty()) {
     // The session must be closed before it is destroyed.
-    DCHECK(dynamic_streams().empty());
+    DCHECK(!HasActiveRequestStreams());
     CloseAllStreams(ERR_UNEXPECTED);
     DCHECK(handles_.empty());
     CloseAllHandles(ERR_UNEXPECTED);
@@ -1173,7 +1174,7 @@
 }
 
 bool QuicChromiumClientSession::GetRemoteEndpoint(IPEndPoint* endpoint) {
-  *endpoint = peer_address().impl().socket_address();
+  *endpoint = ToIPEndPoint(peer_address());
   return true;
 }
 
@@ -1472,7 +1473,7 @@
 
   // Server has sent an alternate address to connect to.
   IPEndPoint new_address =
-      config()->ReceivedAlternateServerAddress().impl().socket_address();
+      ToIPEndPoint(config()->ReceivedAlternateServerAddress());
   IPEndPoint old_address;
   GetDefaultSocket()->GetPeerAddress(&old_address);
 
@@ -1715,7 +1716,7 @@
   for (auto& socket : sockets_) {
     socket->Close();
   }
-  DCHECK(dynamic_streams().empty());
+  DCHECK(!HasActiveRequestStreams());
   CloseAllStreams(ERR_UNEXPECTED);
   CloseAllHandles(ERR_UNEXPECTED);
   CancelAllRequests(ERR_CONNECTION_CLOSED);
@@ -1864,7 +1865,7 @@
       NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED,
       NetLogQuicConnectionMigrationTriggerCallback("WriteError"));
   MigrationResult result =
-      Migrate(new_network, connection()->peer_address().impl().socket_address(),
+      Migrate(new_network, ToIPEndPoint(connection()->peer_address()),
               /*close_session_on_error=*/false, migration_net_log);
   migration_net_log.EndEvent(
       NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED);
@@ -2203,7 +2204,7 @@
   probing_manager_.CancelProbing(network, peer_address());
 
   MigrationResult result =
-      Migrate(network, connection()->peer_address().impl().socket_address(),
+      Migrate(network, ToIPEndPoint(connection()->peer_address()),
               /*close_session_on_error=*/true, net_log_);
   if (result == MigrationResult::FAILURE)
     return;
@@ -2415,11 +2416,26 @@
 }
 
 void QuicChromiumClientSession::CloseAllStreams(int net_error) {
-  while (!dynamic_streams().empty()) {
-    quic::QuicStream* stream = dynamic_streams().begin()->second.get();
-    quic::QuicStreamId id = stream->id();
-    static_cast<QuicChromiumClientStream*>(stream)->OnError(net_error);
-    CloseStream(id);
+  if (!eliminate_static_stream_map()) {
+    while (!dynamic_streams().empty()) {
+      quic::QuicStream* stream = dynamic_streams().begin()->second.get();
+      quic::QuicStreamId id = stream->id();
+      static_cast<QuicChromiumClientStream*>(stream)->OnError(net_error);
+      CloseStream(id);
+    }
+  } else {
+    quic::QuicSmallMap<quic::QuicStreamId, quic::QuicStream*, 10>
+        non_static_streams;
+    for (const auto& stream : dynamic_streams()) {
+      if (!stream.second->is_static()) {
+        non_static_streams[stream.first] = stream.second.get();
+      }
+    }
+    for (const auto& stream : non_static_streams) {
+      quic::QuicStreamId id = stream.first;
+      static_cast<QuicChromiumClientStream*>(stream.second)->OnError(net_error);
+      CloseStream(id);
+    }
   }
 }
 
@@ -2494,9 +2510,9 @@
   // Create and configure socket on |network|.
   std::unique_ptr<DatagramClientSocket> probing_socket =
       stream_factory_->CreateSocket(net_log_.net_log(), net_log_.source());
-  if (stream_factory_->ConfigureSocket(
-          probing_socket.get(), peer_address.impl().socket_address(), network,
-          session_key_.socket_tag()) != OK) {
+  if (stream_factory_->ConfigureSocket(probing_socket.get(),
+                                       ToIPEndPoint(peer_address), network,
+                                       session_key_.socket_tag()) != OK) {
     HistogramAndLogMigrationFailure(
         migration_net_log, MIGRATION_STATUS_INTERNAL_ERROR, connection_id(),
         "Socket configuration failed");
@@ -2628,6 +2644,10 @@
   auto it = dynamic_streams().begin();
   // Stream may be deleted when iterating through the map.
   while (it != dynamic_streams().end()) {
+    if (eliminate_static_stream_map() && it->second->is_static()) {
+      it++;
+      continue;
+    }
     QuicChromiumClientStream* stream =
         static_cast<QuicChromiumClientStream*>(it->second.get());
     if (!stream->can_migrate_to_cellular_network()) {
@@ -2749,6 +2769,8 @@
   std::unique_ptr<base::ListValue> stream_list(new base::ListValue());
   for (DynamicStreamMap::const_iterator it = dynamic_streams().begin();
        it != dynamic_streams().end(); ++it) {
+    if (eliminate_static_stream_map() && it->second->is_static())
+      continue;
     stream_list->AppendString(base::NumberToString(it->second->id()));
   }
   dict.Set("active_streams", std::move(stream_list));
@@ -2833,7 +2855,7 @@
 }
 
 void QuicChromiumClientSession::NotifyFactoryOfSessionClosedLater() {
-  if (!dynamic_streams().empty())
+  if (HasActiveRequestStreams())
     RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER);
 
   if (!going_away_)
@@ -2849,7 +2871,7 @@
 }
 
 void QuicChromiumClientSession::NotifyFactoryOfSessionClosed() {
-  if (!dynamic_streams().empty())
+  if (HasActiveRequestStreams())
     RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED);
 
   if (!going_away_)
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc
index 833a3b9..79ae9d9 100644
--- a/net/quic/quic_connection_logger.cc
+++ b/net/quic/quic_connection_logger.cc
@@ -22,6 +22,7 @@
 #include "net/log/net_log.h"
 #include "net/log/net_log_capture_mode.h"
 #include "net/log/net_log_event_type.h"
+#include "net/quic/address_utils.h"
 #include "net/quic/quic_address_mismatch.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
@@ -38,10 +39,11 @@
 
 namespace {
 
-base::Value NetLogQuicPacketCallback(const IPEndPoint* self_address,
-                                     const IPEndPoint* peer_address,
-                                     size_t packet_size,
-                                     NetLogCaptureMode /* capture_mode */) {
+base::Value NetLogQuicPacketCallback(
+    const quic::QuicSocketAddress* self_address,
+    const quic::QuicSocketAddress* peer_address,
+    size_t packet_size,
+    NetLogCaptureMode /* capture_mode */) {
   base::DictionaryValue dict;
   dict.SetString("self_address", self_address->ToString());
   dict.SetString("peer_address", peer_address->ToString());
@@ -223,7 +225,7 @@
 
 base::Value NetLogQuicPublicResetPacketCallback(
     const IPEndPoint* server_hello_address,
-    const IPEndPoint* public_reset_address,
+    const quic::QuicSocketAddress* public_reset_address,
     NetLogCaptureMode /* capture_mode */) {
   base::DictionaryValue dict;
   dict.SetString("server_hello_address", server_hello_address->ToString());
@@ -540,10 +542,10 @@
     const quic::QuicSocketAddress& peer_address,
     const quic::QuicEncryptedPacket& packet) {
   if (local_address_from_self_.GetFamily() == ADDRESS_FAMILY_UNSPECIFIED) {
-    local_address_from_self_ = self_address.impl().socket_address();
+    local_address_from_self_ = ToIPEndPoint(self_address);
     UMA_HISTOGRAM_ENUMERATION(
         "Net.QuicSession.ConnectionTypeFromSelf",
-        GetRealAddressFamily(self_address.impl().socket_address().address()),
+        GetRealAddressFamily(ToIPEndPoint(self_address).address()),
         ADDRESS_FAMILY_LAST);
   }
 
@@ -551,11 +553,9 @@
   last_received_packet_size_ = packet.length();
   if (!net_log_is_capturing_)
     return;
-  net_log_.AddEvent(
-      NetLogEventType::QUIC_SESSION_PACKET_RECEIVED,
-      base::Bind(&NetLogQuicPacketCallback,
-                 &self_address.impl().socket_address(),
-                 &peer_address.impl().socket_address(), packet.length()));
+  net_log_.AddEvent(NetLogEventType::QUIC_SESSION_PACKET_RECEIVED,
+                    base::Bind(&NetLogQuicPacketCallback, &self_address,
+                               &peer_address, packet.length()));
 }
 
 void QuicConnectionLogger::OnUnauthenticatedHeader(
@@ -737,13 +737,13 @@
 void QuicConnectionLogger::OnPublicResetPacket(
     const quic::QuicPublicResetPacket& packet) {
   UpdatePublicResetAddressMismatchHistogram(
-      local_address_from_shlo_, packet.client_address.impl().socket_address());
+      local_address_from_shlo_, ToIPEndPoint(packet.client_address));
   if (!net_log_is_capturing_)
     return;
-  net_log_.AddEvent(NetLogEventType::QUIC_SESSION_PUBLIC_RESET_PACKET_RECEIVED,
-                    base::Bind(&NetLogQuicPublicResetPacketCallback,
-                               &local_address_from_shlo_,
-                               &packet.client_address.impl().socket_address()));
+  net_log_.AddEvent(
+      NetLogEventType::QUIC_SESSION_PUBLIC_RESET_PACKET_RECEIVED,
+      base::Bind(&NetLogQuicPublicResetPacketCallback,
+                 &local_address_from_shlo_, &packet.client_address));
 }
 
 void QuicConnectionLogger::OnVersionNegotiationPacket(
@@ -763,7 +763,7 @@
     if (message.GetStringPiece(quic::kCADR, &address) &&
         decoder.Decode(address.data(), address.size())) {
       local_address_from_shlo_ =
-          IPEndPoint(decoder.ip().impl().ip_address(), decoder.port());
+          IPEndPoint(ToIPAddress(decoder.ip()), decoder.port());
       UMA_HISTOGRAM_ENUMERATION(
           "Net.QuicSession.ConnectionTypeFromPeer",
           GetRealAddressFamily(local_address_from_shlo_.address()),
diff --git a/net/quic/quic_connectivity_probing_manager.cc b/net/quic/quic_connectivity_probing_manager.cc
index 5ce272a..7f33513 100644
--- a/net/quic/quic_connectivity_probing_manager.cc
+++ b/net/quic/quic_connectivity_probing_manager.cc
@@ -9,6 +9,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
 #include "net/log/net_log.h"
+#include "net/quic/address_utils.h"
 
 namespace net {
 
@@ -173,7 +174,7 @@
            << local_address.ToString() << ", to peer ip:port "
            << peer_address_.ToString();
 
-  if (quic::QuicSocketAddressImpl(local_address) != self_address.impl() ||
+  if (local_address != ToIPEndPoint(self_address) ||
       peer_address_ != peer_address) {
     DVLOG(1) << "Received probing response from peer ip:port "
              << peer_address.ToString() << ", to self ip:port "
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index b5c361f..d4df7d3e 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -36,6 +36,7 @@
 #include "net/log/net_log_capture_mode.h"
 #include "net/log/net_log_event_type.h"
 #include "net/log/net_log_source_type.h"
+#include "net/quic/address_utils.h"
 #include "net/quic/crypto/proof_verifier_chromium.h"
 #include "net/quic/properties_based_quic_server_info.h"
 #include "net/quic/quic_chromium_alarm_factory.h"
@@ -264,6 +265,7 @@
       verify_callback_->Cancel();
   }
 
+  // TODO(mattm): pass |ocsp_response| and |cert_sct|.
   // Starts verification of certs cached in the |crypto_config|.
   quic::QuicAsyncStatus Run(quic::QuicCryptoClientConfig* crypto_config,
                             CompletionOnceCallback callback) {
@@ -274,8 +276,9 @@
     quic::QuicAsyncStatus status =
         crypto_config->proof_verifier()->VerifyCertChain(
             server_id_.host(), cached->certs(),
-            /*ocsp_response=*/std::string(), cached->cert_sct(),
-            verify_context_.get(), &verify_error_details_, &verify_details_,
+            /*ocsp_response=*/std::string(),
+            /*cert_sct=*/std::string(), verify_context_.get(),
+            &verify_error_details_, &verify_details_,
             std::move(verify_callback));
     if (status == quic::QUIC_PENDING) {
       verify_callback_ = verify_callback_ptr;
@@ -412,7 +415,7 @@
   bool DoesPeerAddressMatchWithFreshAddressList() {
     std::vector<net::IPEndPoint> endpoints =
         fresh_resolve_host_request_->GetAddressResults().value().endpoints();
-    IPEndPoint stale_address = session_->peer_address().impl().socket_address();
+    IPEndPoint stale_address = ToIPEndPoint(session_->peer_address());
 
     if (std::find(endpoints.begin(), endpoints.end(), stale_address) !=
         endpoints.end()) {
@@ -889,8 +892,7 @@
   DCHECK(!factory_->HasActiveSession(key_.session_key()));
   // There may well now be an active session for this IP.  If so, use the
   // existing session instead.
-  AddressList address(
-      session_->connection()->peer_address().impl().socket_address());
+  AddressList address(ToIPEndPoint(session_->connection()->peer_address()));
   if (factory_->HasMatchingIpSession(key_, address)) {
     LogConnectionIpPooling(true);
     session_->connection()->CloseConnection(
@@ -1898,7 +1900,7 @@
   active_sessions_[key.session_key()] = session;
   session_aliases_[session].insert(key);
   const IPEndPoint peer_address =
-      session->connection()->peer_address().impl().socket_address();
+      ToIPEndPoint(session->connection()->peer_address());
   DCHECK(!base::ContainsKey(ip_aliases_[peer_address], session));
   ip_aliases_[peer_address].insert(session);
   DCHECK(!base::ContainsKey(session_peer_ip_, session));
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 00821bda..6de7e444 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -27,6 +27,7 @@
 #include "net/http/http_util.h"
 #include "net/http/transport_security_state.h"
 #include "net/http/transport_security_state_test_util.h"
+#include "net/quic/address_utils.h"
 #include "net/quic/crypto/proof_verifier_chromium.h"
 #include "net/quic/mock_crypto_client_stream_factory.h"
 #include "net/quic/mock_quic_data.h"
@@ -9324,9 +9325,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
-  EXPECT_EQ(
-      session->peer_address().impl().socket_address().ToStringWithoutPort(),
-      kNonCachedIPAddress);
+  EXPECT_EQ(session->peer_address().host().ToString(), kNonCachedIPAddress);
 
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
   EXPECT_TRUE(quic_data.AllWriteDataConsumed());
@@ -9373,9 +9372,7 @@
   EXPECT_TRUE(stream.get());
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
 
-  EXPECT_EQ(
-      session->peer_address().impl().socket_address().ToStringWithoutPort(),
-      kNonCachedIPAddress);
+  EXPECT_EQ(session->peer_address().host().ToString(), kNonCachedIPAddress);
 
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
   EXPECT_TRUE(quic_data.AllWriteDataConsumed());
@@ -9431,9 +9428,8 @@
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
 
-  EXPECT_EQ(
-      session->peer_address().impl().socket_address().ToStringWithoutPort(),
-      kCachedIPAddress.ToString());
+  EXPECT_EQ(session->peer_address().host().ToString(),
+            kCachedIPAddress.ToString());
 
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
   EXPECT_TRUE(quic_data.AllWriteDataConsumed());
@@ -9499,9 +9495,8 @@
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
 
-  EXPECT_EQ(
-      session->peer_address().impl().socket_address().ToStringWithoutPort(),
-      kCachedIPAddress.ToString());
+  EXPECT_EQ(session->peer_address().host().ToString(),
+            kCachedIPAddress.ToString());
 
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
   EXPECT_TRUE(quic_data.AllWriteDataConsumed());
@@ -9563,9 +9558,8 @@
   EXPECT_TRUE(stream.get());
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
-  EXPECT_EQ(
-      session->peer_address().impl().socket_address().ToStringWithoutPort(),
-      kCachedIPAddress.ToString());
+  EXPECT_EQ(session->peer_address().host().ToString(),
+            kCachedIPAddress.ToString());
 
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
   EXPECT_TRUE(quic_data.AllWriteDataConsumed());
@@ -9634,9 +9628,7 @@
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
 
-  EXPECT_EQ(
-      session->peer_address().impl().socket_address().ToStringWithoutPort(),
-      kNonCachedIPAddress);
+  EXPECT_EQ(session->peer_address().host().ToString(), kNonCachedIPAddress);
 
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
   EXPECT_TRUE(quic_data.AllWriteDataConsumed());
@@ -9708,9 +9700,7 @@
   EXPECT_TRUE(stream.get());
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
-  EXPECT_EQ(
-      session->peer_address().impl().socket_address().ToStringWithoutPort(),
-      kNonCachedIPAddress);
+  EXPECT_EQ(session->peer_address().host().ToString(), kNonCachedIPAddress);
 
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
   EXPECT_TRUE(quic_data.AllWriteDataConsumed());
@@ -9779,9 +9769,7 @@
   EXPECT_TRUE(stream.get());
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
-  EXPECT_EQ(
-      session->peer_address().impl().socket_address().ToStringWithoutPort(),
-      kNonCachedIPAddress);
+  EXPECT_EQ(session->peer_address().host().ToString(), kNonCachedIPAddress);
 
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
   EXPECT_TRUE(quic_data.AllWriteDataConsumed());
@@ -10002,9 +9990,7 @@
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
 
-  EXPECT_EQ(
-      session->peer_address().impl().socket_address().ToStringWithoutPort(),
-      kNonCachedIPAddress);
+  EXPECT_EQ(session->peer_address().host().ToString(), kNonCachedIPAddress);
 
   EXPECT_TRUE(quic_data2.AllReadDataConsumed());
   EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index 857dc8a0..71fdb600 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -1138,16 +1138,10 @@
   base::StringPiece ocsp_response(
       reinterpret_cast<const char*>(ocsp_response_raw), ocsp_response_len);
 
-  const uint8_t* sct_list_raw;
-  size_t sct_list_len;
-  SSL_get0_signed_cert_timestamp_list(ssl_.get(), &sct_list_raw, &sct_list_len);
-  base::StringPiece sct_list(reinterpret_cast<const char*>(sct_list_raw),
-                             sct_list_len);
-
   cert_verification_result_ = cert_verifier_->Verify(
-      CertVerifier::RequestParams(
-          server_cert_, host_and_port_.host(), ssl_config_.GetCertVerifyFlags(),
-          ocsp_response.as_string(), sct_list.as_string()),
+      CertVerifier::RequestParams(server_cert_, host_and_port_.host(),
+                                  ssl_config_.GetCertVerifyFlags(),
+                                  ocsp_response.as_string()),
       &server_cert_verify_result_,
       base::BindOnce(&SSLClientSocketImpl::OnVerifyComplete,
                      base::Unretained(this)),
diff --git a/net/tools/cert_verify_tool/verify_using_cert_verify_proc.cc b/net/tools/cert_verify_tool/verify_using_cert_verify_proc.cc
index 787f123..11e9e9a 100644
--- a/net/tools/cert_verify_tool/verify_using_cert_verify_proc.cc
+++ b/net/tools/cert_verify_tool/verify_using_cert_verify_proc.cc
@@ -173,9 +173,8 @@
   net::CertVerifyResult result;
   int rv =
       cert_verify_proc->Verify(x509_target_and_intermediates.get(), hostname,
-                               /*ocsp_response=*/std::string(),
-                               /*sct_list=*/std::string(), flags, crl_set,
-                               x509_additional_trust_anchors, &result);
+                               std::string() /* ocsp_response */, flags,
+                               crl_set, x509_additional_trust_anchors, &result);
 
   // Remove any temporary trust anchors.
   test_root_certs->Clear();
diff --git a/net/tools/quic/quic_client_message_loop_network_helper.cc b/net/tools/quic/quic_client_message_loop_network_helper.cc
index 72cf547e..486aab9 100644
--- a/net/tools/quic/quic_client_message_loop_network_helper.cc
+++ b/net/tools/quic/quic_client_message_loop_network_helper.cc
@@ -14,6 +14,7 @@
 #include "net/http/http_response_info.h"
 #include "net/log/net_log_source.h"
 #include "net/log/net_log_with_source.h"
+#include "net/quic/address_utils.h"
 #include "net/quic/quic_chromium_alarm_factory.h"
 #include "net/quic/quic_chromium_connection_helper.h"
 #include "net/quic/quic_chromium_packet_reader.h"
@@ -60,7 +61,7 @@
         quic::QuicSocketAddress(quic::QuicIpAddress::Any6(), bind_to_port);
   }
 
-  int rc = socket->Connect(server_address.impl().socket_address());
+  int rc = socket->Connect(ToIPEndPoint(server_address));
   if (rc != OK) {
     LOG(ERROR) << "Connect failed: " << ErrorToShortString(rc);
     return false;
diff --git a/net/tools/quic/quic_simple_server_packet_writer.cc b/net/tools/quic/quic_simple_server_packet_writer.cc
index ac952d7..6e71a9d 100644
--- a/net/tools/quic/quic_simple_server_packet_writer.cc
+++ b/net/tools/quic/quic_simple_server_packet_writer.cc
@@ -12,6 +12,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
+#include "net/quic/address_utils.h"
 #include "net/socket/udp_server_socket.h"
 #include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
 
@@ -58,8 +59,7 @@
   int rv;
   if (buf_len <= static_cast<size_t>(std::numeric_limits<int>::max())) {
     rv = socket_->SendTo(
-        buf.get(), static_cast<int>(buf_len),
-        peer_address.impl().socket_address(),
+        buf.get(), static_cast<int>(buf_len), ToIPEndPoint(peer_address),
         base::Bind(&QuicSimpleServerPacketWriter::OnWriteComplete,
                    weak_factory_.GetWeakPtr()));
   } else {
diff --git a/services/network/cert_verifier_with_trust_anchors_unittest.cc b/services/network/cert_verifier_with_trust_anchors_unittest.cc
index e7e1d6f1..c808a8e 100644
--- a/services/network/cert_verifier_with_trust_anchors_unittest.cc
+++ b/services/network/cert_verifier_with_trust_anchors_unittest.cc
@@ -81,12 +81,11 @@
       net::CompletionOnceCallback test_callback,
       net::CertVerifyResult* verify_result,
       std::unique_ptr<net::CertVerifier::Request>* request) {
-    return cert_verifier_->Verify(net::CertVerifier::RequestParams(
-                                      test_server_cert_.get(), "127.0.0.1", 0,
-                                      /*ocsp_response=*/std::string(),
-                                      /*sct_list=*/std::string()),
-                                  verify_result, std::move(test_callback),
-                                  request, net::NetLogWithSource());
+    return cert_verifier_->Verify(
+        net::CertVerifier::RequestParams(test_server_cert_.get(), "127.0.0.1",
+                                         0, std::string()),
+        verify_result, std::move(test_callback), request,
+        net::NetLogWithSource());
   }
 
   bool SupportsAdditionalTrustAnchors() {
diff --git a/services/network/cert_verify_proc_chromeos.cc b/services/network/cert_verify_proc_chromeos.cc
index d559a2a..5eac37c 100644
--- a/services/network/cert_verify_proc_chromeos.cc
+++ b/services/network/cert_verify_proc_chromeos.cc
@@ -44,7 +44,6 @@
     net::X509Certificate* cert,
     const std::string& hostname,
     const std::string& ocsp_response,
-    const std::string& sct_list,
     int flags,
     net::CRLSet* crl_set,
     const net::CertificateList& additional_trust_anchors,
diff --git a/services/network/cert_verify_proc_chromeos.h b/services/network/cert_verify_proc_chromeos.h
index dcbfac5..45ee9fb 100644
--- a/services/network/cert_verify_proc_chromeos.h
+++ b/services/network/cert_verify_proc_chromeos.h
@@ -37,7 +37,6 @@
   int VerifyInternal(net::X509Certificate* cert,
                      const std::string& hostname,
                      const std::string& ocsp_response,
-                     const std::string& sct_list,
                      int flags,
                      net::CRLSet* crl_set,
                      const net::CertificateList& additional_trust_anchors,
diff --git a/services/network/cert_verify_proc_chromeos_unittest.cc b/services/network/cert_verify_proc_chromeos_unittest.cc
index fef1da5a..6c5934c 100644
--- a/services/network/cert_verify_proc_chromeos_unittest.cc
+++ b/services/network/cert_verify_proc_chromeos_unittest.cc
@@ -107,9 +107,7 @@
       std::string* root_subject_name) {
     int flags = 0;
     net::CertVerifyResult verify_result;
-    int error = verify_proc->Verify(cert, "127.0.0.1",
-                                    /*ocsp_response=*/std::string(),
-                                    /*sct_list=*/std::string(), flags,
+    int error = verify_proc->Verify(cert, "127.0.0.1", std::string(), flags,
                                     net::CRLSet::BuiltinCRLSet().get(),
                                     additional_trust_anchors, &verify_result);
     if (!verify_result.verified_cert->intermediate_buffers().empty()) {
diff --git a/services/network/ignore_errors_cert_verifier_unittest.cc b/services/network/ignore_errors_cert_verifier_unittest.cc
index afd4c3d01..d29015f 100644
--- a/services/network/ignore_errors_cert_verifier_unittest.cc
+++ b/services/network/ignore_errors_cert_verifier_unittest.cc
@@ -94,9 +94,7 @@
 
 static CertVerifier::RequestParams MakeRequestParams(
     const scoped_refptr<X509Certificate>& cert) {
-  return CertVerifier::RequestParams(cert, "example.com", /*flags=*/0,
-                                     /*ocsp_response=*/std::string(),
-                                     /*sct_list=*/std::string());
+  return CertVerifier::RequestParams(cert, "example.com", 0, "");
 }
 
 static void GetWhitelistedTestCert(scoped_refptr<X509Certificate>* out) {
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index f19e147..b5771b7e 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -1357,8 +1357,7 @@
                                   : url_request_context_->cert_verifier();
   int result = cert_verifier->Verify(
       net::CertVerifier::RequestParams(certificate, url.host(),
-                                       0 /* cert_verify_flags */, ocsp_result,
-                                       sct_list),
+                                       0 /* cert_verify_flags */, ocsp_result),
       pending_cert_verify->result.get(),
       base::BindOnce(&NetworkContext::OnCertVerifyForSignedExchangeComplete,
                      base::Unretained(this), cert_verify_id),
@@ -1547,7 +1546,6 @@
     const scoped_refptr<net::X509Certificate>& certificate,
     const std::string& hostname,
     const std::string& ocsp_response,
-    const std::string& sct_list,
     VerifyCertificateForTestingCallback callback) {
   net::CertVerifier* cert_verifier = url_request_context_->cert_verifier();
 
@@ -1555,13 +1553,12 @@
   auto* request = &state->request;
   auto* result = &state->result;
 
-  cert_verifier->Verify(
-      net::CertVerifier::RequestParams(certificate.get(), hostname, 0,
-                                       ocsp_response, sct_list),
-      result,
-      base::BindOnce(TestVerifyCertCallback, std::move(state),
-                     std::move(callback)),
-      request, net::NetLogWithSource());
+  cert_verifier->Verify(net::CertVerifier::RequestParams(
+                            certificate.get(), hostname, 0, ocsp_response),
+                        result,
+                        base::BindOnce(TestVerifyCertCallback, std::move(state),
+                                       std::move(callback)),
+                        request, net::NetLogWithSource());
 }
 
 void NetworkContext::PreconnectSockets(uint32_t num_streams,
diff --git a/services/network/network_context.h b/services/network/network_context.h
index 99d81077..315ce8a 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -318,7 +318,6 @@
       const scoped_refptr<net::X509Certificate>& certificate,
       const std::string& hostname,
       const std::string& ocsp_response,
-      const std::string& sct_list,
       VerifyCertificateForTestingCallback callback) override;
   void PreconnectSockets(uint32_t num_streams,
                          const GURL& url,
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 60966ed..aa628f12 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -910,8 +910,7 @@
   // Verifies the given certificate using the context's CertVerifier.
   VerifyCertificateForTesting(X509Certificate certificate,
                               string hostname,
-                              string ocsp_response,
-                              string sct_list) => (int32 error_code);
+                              string ocsp_response) => (int32 error_code);
 
   [Sync]
   // Adds a Domain Reliability Context.
diff --git a/services/network/ssl_config_service_mojo_unittest.cc b/services/network/ssl_config_service_mojo_unittest.cc
index f8e0686..6af9826 100644
--- a/services/network/ssl_config_service_mojo_unittest.cc
+++ b/services/network/ssl_config_service_mojo_unittest.cc
@@ -501,10 +501,7 @@
   net::CertVerifyResult cert_verify_result1;
   std::unique_ptr<net::CertVerifier::Request> request1;
   int result = network_context_->url_request_context()->cert_verifier()->Verify(
-      net::CertVerifier::RequestParams(cert, "127.0.0.1",
-                                       /*flags=*/0,
-                                       /*ocsp_response=*/std::string(),
-                                       /*sct_list=*/std::string()),
+      net::CertVerifier::RequestParams(cert, "127.0.0.1", 0, std::string()),
       &cert_verify_result1, callback1.callback(), &request1,
       net::NetLogWithSource());
   ASSERT_THAT(callback1.GetResult(result), net::test::IsOk());
@@ -525,10 +522,7 @@
   net::CertVerifyResult cert_verify_result2;
   std::unique_ptr<net::CertVerifier::Request> request2;
   result = network_context_->url_request_context()->cert_verifier()->Verify(
-      net::CertVerifier::RequestParams(cert, "127.0.0.1",
-                                       /*flags=*/0,
-                                       /*ocsp_response=*/std::string(),
-                                       /*sct_list=*/std::string()),
+      net::CertVerifier::RequestParams(cert, "127.0.0.1", 0, std::string()),
       &cert_verify_result2, callback2.callback(), &request2,
       net::NetLogWithSource());
   ASSERT_THAT(callback2.GetResult(result),
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h
index fa8bb72..bf688427 100644
--- a/services/network/test/test_network_context.h
+++ b/services/network/test/test_network_context.h
@@ -182,7 +182,6 @@
       const scoped_refptr<net::X509Certificate>& certificate,
       const std::string& hostname,
       const std::string& ocsp_response,
-      const std::string& sct_list,
       VerifyCertificateForTestingCallback callback) override {}
   void PreconnectSockets(uint32_t num_streams,
                          const GURL& url,
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 5304f488..55f29f41 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -2129,7 +2129,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2144,7 +2144,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2159,7 +2159,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2174,7 +2174,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2189,7 +2189,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2204,7 +2204,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2219,7 +2219,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2234,7 +2234,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2249,7 +2249,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2264,7 +2264,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2280,7 +2280,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2295,7 +2295,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2310,7 +2310,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2325,7 +2325,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 10
@@ -2346,7 +2346,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2361,7 +2361,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2379,7 +2379,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2394,7 +2394,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2409,7 +2409,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2424,7 +2424,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2439,7 +2439,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2454,7 +2454,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2469,7 +2469,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2484,7 +2484,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2499,7 +2499,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 6
@@ -2520,7 +2520,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2540,7 +2540,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2555,7 +2555,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2570,7 +2570,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2585,7 +2585,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2600,7 +2600,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2615,7 +2615,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2630,7 +2630,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2645,7 +2645,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2660,7 +2660,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2675,7 +2675,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2690,7 +2690,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2705,7 +2705,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2720,7 +2720,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2735,7 +2735,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2750,7 +2750,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2765,7 +2765,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2780,7 +2780,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2795,7 +2795,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2810,7 +2810,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2825,7 +2825,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 3
@@ -2846,7 +2846,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2861,7 +2861,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2876,7 +2876,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2891,7 +2891,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2906,7 +2906,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2921,7 +2921,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2936,7 +2936,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2951,7 +2951,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2966,7 +2966,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2981,7 +2981,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2996,7 +2996,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3011,7 +3011,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3026,7 +3026,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3041,7 +3041,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3056,7 +3056,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3071,7 +3071,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3086,7 +3086,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3101,7 +3101,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3116,7 +3116,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3131,7 +3131,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3146,7 +3146,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3161,7 +3161,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3176,7 +3176,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3191,7 +3191,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3206,7 +3206,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3221,7 +3221,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3236,7 +3236,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3251,7 +3251,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3266,7 +3266,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3281,7 +3281,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3296,7 +3296,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3311,7 +3311,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3326,7 +3326,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3341,7 +3341,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3356,7 +3356,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3371,7 +3371,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3386,7 +3386,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3401,7 +3401,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3416,7 +3416,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3431,7 +3431,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3446,7 +3446,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3461,7 +3461,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3477,7 +3477,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3496,7 +3496,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -3515,7 +3515,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -3531,7 +3531,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -3547,7 +3547,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -3563,7 +3563,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -3579,7 +3579,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -3595,7 +3595,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -3611,7 +3611,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -3637,7 +3637,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 10
@@ -3654,7 +3654,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "idempotent": false
@@ -3675,7 +3675,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "idempotent": false,
@@ -3697,7 +3697,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "idempotent": false,
@@ -3720,7 +3720,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -3742,7 +3742,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 6
diff --git a/testing/buildbot/filters/webui_html_imports_polyfill_browser_tests.filter b/testing/buildbot/filters/webui_html_imports_polyfill_browser_tests.filter
index 7a5753b..0d113ecc8 100644
--- a/testing/buildbot/filters/webui_html_imports_polyfill_browser_tests.filter
+++ b/testing/buildbot/filters/webui_html_imports_polyfill_browser_tests.filter
@@ -32,6 +32,11 @@
 CrExtensionsRuntimeHostPermissionsTest.*
 CrExtensionsHostPermissionsToggleListTest.*
 
+DownloadsItemTest.*
+DownloadsManagerTest.*
+DownloadsToolbarTest.*
+DownloadsUrlTest.*
+
 PDFAnnotationsTest.*
 PDFExtensionClipboardTest.*
 PDFExtensionHitTestTest.*
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 1767969f..de68f03 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -239,6 +239,13 @@
       },
     },
   },
+  'linux-xenial': {
+    'swarming': {
+      'dimensions': {
+        'os': 'Ubuntu-16.04',
+      },
+    },
+  },
   'linux_amd_r7_240': {
     'swarming': {
       'dimensions': {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 9eab929..3e16ceb 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -3423,7 +3423,7 @@
       },
       'Linux Tests': {
         'mixins': [
-          'linux-trusty',
+          'linux-xenial',
         ],
         'test_suites': {
           'gtest_tests': 'chromium_linux_gtests',
diff --git a/third_party/blink/public/platform/web_media_player_client.h b/third_party/blink/public/platform/web_media_player_client.h
index 9811f0f5..74227c82 100644
--- a/third_party/blink/public/platform/web_media_player_client.h
+++ b/third_party/blink/public/platform/web_media_player_client.h
@@ -124,6 +124,9 @@
   // to a localized string that explains the reason as user-readable text.
   virtual void MediaRemotingStopped(WebLocalizedString::Name error_msg) = 0;
 
+  // Informs that Picture-in-Picture mode has stopped for the media element.
+  virtual void PictureInPictureStopped() = 0;
+
   // Returns whether the media element has native controls. It does not mean
   // that the controls are currently visible.
   virtual bool HasNativeControls() = 0;
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index 8dc52ea..35c2034d 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -103,7 +103,6 @@
   BLINK_PLATFORM_EXPORT static void EnableFirstContentfulPaintPlusPlus(bool);
   BLINK_PLATFORM_EXPORT static void EnableForceSynchronousHTMLParsing(bool);
   BLINK_PLATFORM_EXPORT static void EnableForceTallerSelectPopup(bool);
-  BLINK_PLATFORM_EXPORT static void EnableFormControlsRefresh(bool);
   BLINK_PLATFORM_EXPORT static void EnableGenericSensor(bool);
   BLINK_PLATFORM_EXPORT static void EnableGenericSensorExtraClasses(bool);
   BLINK_PLATFORM_EXPORT static void EnableHeapCompaction(bool);
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
index 762889f7..ce95d11 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
@@ -111,9 +111,9 @@
         loading_task_runner_(platform_->test_task_runner()) {
     auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
     FetchContext* context = MakeGarbageCollected<MockFetchContext>();
-    auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
-        ResourceFetcherInit(*properties, context, loading_task_runner_,
-                            MakeGarbageCollected<NoopLoaderFactory>()));
+    auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+        properties->MakeDetachable(), context, loading_task_runner_,
+        MakeGarbageCollected<NoopLoaderFactory>()));
 
     ResourceRequest request(url_);
     request.SetRequestContext(mojom::RequestContextType::SCRIPT);
diff --git a/third_party/blink/renderer/core/animation/compositor_animations.cc b/third_party/blink/renderer/core/animation/compositor_animations.cc
index 223d1cb..234a29c 100644
--- a/third_party/blink/renderer/core/animation/compositor_animations.cc
+++ b/third_party/blink/renderer/core/animation/compositor_animations.cc
@@ -45,6 +45,7 @@
 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
+#include "third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h"
 #include "third_party/blink/renderer/core/paint/filter_effect_builder.h"
 #include "third_party/blink/renderer/core/paint/object_paint_properties.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
@@ -192,6 +193,16 @@
     if (!layout_object || !layout_object->UniqueId()) {
       reasons |= kTargetHasInvalidCompositingState;
     }
+
+    // Elements with subtrees containing will-change: contents are not
+    // composited for animations as if the contents change the tiles
+    // would need to be rerastered anyways.
+    // TODO(crbug.com/961686): Remove Style() check once unit tests create
+    // fully styled Elements.
+    if (layout_object && layout_object->Style() &&
+        layout_object->Style()->SubtreeWillChangeContents()) {
+      reasons |= kTargetHasInvalidCompositingState;
+    }
   }
 
   PropertyHandleSet properties = keyframe_effect.Properties();
diff --git a/third_party/blink/renderer/core/clipboard/system_clipboard.cc b/third_party/blink/renderer/core/clipboard/system_clipboard.cc
index c643434..c8f57fa 100644
--- a/third_party/blink/renderer/core/clipboard/system_clipboard.cc
+++ b/third_party/blink/renderer/core/clipboard/system_clipboard.cc
@@ -199,6 +199,7 @@
 }
 
 void SystemClipboard::WriteDataObject(DataObject* data_object) {
+  DCHECK(data_object);
   // This plagiarizes the logic in DropDataBuilder::Build, but only extracts the
   // data needed for the implementation of WriteDataObject.
   //
diff --git a/third_party/blink/renderer/core/clipboard/system_clipboard.h b/third_party/blink/renderer/core/clipboard/system_clipboard.h
index bb48891..038ec50 100644
--- a/third_party/blink/renderer/core/clipboard/system_clipboard.h
+++ b/third_party/blink/renderer/core/clipboard/system_clipboard.h
@@ -63,7 +63,7 @@
   String ReadCustomData(const String& type);
   void WriteDataObject(DataObject*);
 
-  // Clipboard write functions that must use CommitWrite for changes to reach
+  // Clipboard write functions must use CommitWrite for changes to reach
   // the OS clipboard.
   void CommitWrite();
 
@@ -72,6 +72,8 @@
   bool IsValidBufferType(mojom::ClipboardBuffer);
 
   mojom::blink::ClipboardHostPtr clipboard_;
+  // In X11, |buffer_| may equal ClipboardBuffer::kStandard or kSelection.
+  // Outside X11, |buffer_| always equals ClipboardBuffer::kStandard.
   mojom::ClipboardBuffer buffer_ = mojom::ClipboardBuffer::kStandard;
 
   DISALLOW_COPY_AND_ASSIGN(SystemClipboard);
diff --git a/third_party/blink/renderer/core/css/css_property_value_set.cc b/third_party/blink/renderer/core/css/css_property_value_set.cc
index a28a3fc..26163af 100644
--- a/third_party/blink/renderer/core/css/css_property_value_set.cc
+++ b/third_party/blink/renderer/core/css/css_property_value_set.cc
@@ -41,11 +41,11 @@
 
 namespace blink {
 
-static wtf_size_t SizeForImmutableCSSPropertyValueSetWithPropertyCount(
+static AdditionalBytes
+AdditionalBytesForImmutableCSSPropertyValueSetWithPropertyCount(
     unsigned count) {
-  return sizeof(ImmutableCSSPropertyValueSet) - sizeof(void*) +
-         sizeof(Member<CSSValue>) * count +
-         sizeof(CSSPropertyValueMetadata) * count;
+  return AdditionalBytes(sizeof(Member<CSSValue>) * count +
+                         sizeof(CSSPropertyValueMetadata) * count);
 }
 
 ImmutableCSSPropertyValueSet* ImmutableCSSPropertyValueSet::Create(
@@ -53,10 +53,9 @@
     unsigned count,
     CSSParserMode css_parser_mode) {
   DCHECK_LE(count, static_cast<unsigned>(kMaxArraySize));
-  void* slot = ThreadHeap::Allocate<CSSPropertyValueSet>(
-      SizeForImmutableCSSPropertyValueSetWithPropertyCount(count));
-  return new (slot)
-      ImmutableCSSPropertyValueSet(properties, count, css_parser_mode);
+  return MakeGarbageCollected<ImmutableCSSPropertyValueSet>(
+      AdditionalBytesForImmutableCSSPropertyValueSetWithPropertyCount(count),
+      properties, count, css_parser_mode);
 }
 
 CSSPropertyName CSSPropertyValueSet::PropertyReference::Name() const {
@@ -645,7 +644,9 @@
 unsigned CSSPropertyValueSet::AverageSizeInBytes() {
   // Please update this if the storage scheme changes so that this longer
   // reflects the actual size.
-  return SizeForImmutableCSSPropertyValueSetWithPropertyCount(4);
+  return sizeof(ImmutableCSSPropertyValueSet) +
+         AdditionalBytesForImmutableCSSPropertyValueSetWithPropertyCount(4)
+             .value;
 }
 
 // See the function above if you need to update this.
diff --git a/third_party/blink/renderer/core/css/css_property_value_set.h b/third_party/blink/renderer/core/css/css_property_value_set.h
index 47d39c8..02326d58 100644
--- a/third_party/blink/renderer/core/css/css_property_value_set.h
+++ b/third_party/blink/renderer/core/css/css_property_value_set.h
@@ -195,23 +195,17 @@
   int FindPropertyIndex(T property) const;
 
   void TraceAfterDispatch(blink::Visitor*);
-
-  void* operator new(std::size_t, void* location) { return location; }
-
-  void* storage_;
 };
 
 inline const Member<const CSSValue>* ImmutableCSSPropertyValueSet::ValueArray()
     const {
-  return reinterpret_cast<const Member<const CSSValue>*>(
-      const_cast<const void**>(&(this->storage_)));
+  return reinterpret_cast<const Member<const CSSValue>*>(this + 1);
 }
 
 inline const CSSPropertyValueMetadata*
 ImmutableCSSPropertyValueSet::MetadataArray() const {
-  return reinterpret_cast<const CSSPropertyValueMetadata*>(
-      &reinterpret_cast<const char*>(
-          &(this->storage_))[array_size_ * sizeof(Member<CSSValue>)]);
+  return reinterpret_cast<const CSSPropertyValueMetadata*>(ValueArray() +
+                                                           array_size_);
 }
 
 template <>
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.h b/third_party/blink/renderer/core/display_lock/display_lock_context.h
index 30e996a..98dc295 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.h
@@ -115,6 +115,12 @@
   bool ShouldPaint() const;
   void DidPaint();
 
+  // Returns true if the last style recalc traversal was blocked at this
+  // element, either for itself, its children or its descendants.
+  bool StyleTraversalWasBlocked() {
+    return blocked_style_traversal_type_ != kStyleUpdateNotRequired;
+  }
+
   // Returns true if the contents of the associated element should be visible
   // from and activatable by find-in-page, tab order, anchor links, etc.
   bool IsActivatable() const;
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc b/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc
index 0e2dbe5..be64fb79 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc
@@ -82,13 +82,13 @@
 
 const Element* DisplayLockUtilities::NearestLockedInclusiveAncestor(
     const Node& node) {
-  if (!RuntimeEnabledFeatures::DisplayLockingEnabled() ||
+  if (!node.IsElementNode())
+    return NearestLockedExclusiveAncestor(node);
+  if (!RuntimeEnabledFeatures::DisplayLockingEnabled() || !node.isConnected() ||
       node.GetDocument().LockedDisplayLockCount() == 0 ||
       !node.CanParticipateInFlatTree()) {
     return nullptr;
   }
-  if (!node.IsElementNode())
-    return NearestLockedExclusiveAncestor(node);
   if (auto* context = ToElement(node).GetDisplayLockContext()) {
     if (context->IsLocked())
       return &ToElement(node);
@@ -103,7 +103,7 @@
 
 Element* DisplayLockUtilities::NearestLockedExclusiveAncestor(
     const Node& node) {
-  if (!RuntimeEnabledFeatures::DisplayLockingEnabled() ||
+  if (!RuntimeEnabledFeatures::DisplayLockingEnabled() || !node.isConnected() ||
       node.GetDocument().LockedDisplayLockCount() == 0 ||
       !node.CanParticipateInFlatTree()) {
     return nullptr;
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 1a78710..d48b02a 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -717,10 +717,12 @@
   } else if (imports_controller_) {
     fetcher_ = FrameFetchContext::CreateFetcherForImportedDocument(this);
   } else {
+    auto& properties =
+        *MakeGarbageCollected<DetachableResourceFetcherProperties>(
+            *MakeGarbageCollected<NullResourceFetcherProperties>());
     fetcher_ = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
-        *MakeGarbageCollected<NullResourceFetcherProperties>(),
-        &FetchContext::NullInstance(), GetTaskRunner(TaskType::kNetworking),
-        nullptr /* loader_factory */));
+        properties, &FetchContext::NullInstance(),
+        GetTaskRunner(TaskType::kNetworking), nullptr /* loader_factory */));
   }
   DCHECK(fetcher_);
 
@@ -2420,9 +2422,22 @@
 
 bool Document::NeedsLayoutTreeUpdateForNode(const Node& node,
                                             bool ignore_adjacent_style) const {
+  // TODO(rakina): Switch some callers that may need to call
+  // NeedsLayoutTreeUpdateForNodeIncludingDisplayLocked instead of this.
+  if (DisplayLockUtilities::NearestLockedExclusiveAncestor(node)) {
+    // |node| is in a locked-subtree, so we don't need to update it.
+    return false;
+  }
+  return NeedsLayoutTreeUpdateForNodeIncludingDisplayLocked(
+      node, ignore_adjacent_style);
+}
+
+bool Document::NeedsLayoutTreeUpdateForNodeIncludingDisplayLocked(
+    const Node& node,
+    bool ignore_adjacent_style) const {
   if (!node.CanParticipateInFlatTree())
     return false;
-  if (!NeedsLayoutTreeUpdate())
+  if (locked_display_lock_count_ == 0 && !NeedsLayoutTreeUpdate())
     return false;
   if (!node.isConnected())
     return false;
@@ -2442,13 +2457,34 @@
         (ancestor->NeedsAdjacentStyleRecalc() && !ignore_adjacent_style)) {
       return true;
     }
+    if (!ancestor->IsElementNode())
+      continue;
+    if (auto* context = ToElement(ancestor)->GetDisplayLockContext()) {
+      // Even if the ancestor is style-clean, we might've previously
+      // blocked a style traversal going to the ancestor or its descendants.
+      if (context->StyleTraversalWasBlocked())
+        return true;
+    }
   }
   return false;
 }
 
 void Document::UpdateStyleAndLayoutTreeForNode(const Node* node) {
   DCHECK(node);
-  if (!NeedsLayoutTreeUpdateForNode(*node))
+  if (!node->InActiveDocument()) {
+    // If |node| is not in the active document, we can't update its style or
+    // layout tree.
+    DCHECK_EQ(node->ownerDocument(), this);
+    return;
+  }
+  DCHECK(node->GetDocument().GetPage());
+  DCHECK(!node->GetDocument()
+              .GetPage()
+              ->Animator()
+              .UpdatingLayoutAndStyleForPainting())
+      << "UpdateStyleAndLayoutTreeForNode called from within a lifecycle "
+         "update";
+  if (!NeedsLayoutTreeUpdateForNodeIncludingDisplayLocked(*node))
     return;
 
   DisplayLockUtilities::ScopedChainForcedUpdate scoped_update_forced(node);
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 2547333..032447a 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -506,8 +506,15 @@
   void SetupFontBuilder(ComputedStyle& document_style);
 
   bool NeedsLayoutTreeUpdate() const;
+  // Whether we need layout tree update for this node or not, without
+  // considering nodes in display locked subtrees.
   bool NeedsLayoutTreeUpdateForNode(const Node&,
                                     bool ignore_adjacent_style = false) const;
+  // Whether we need layout tree update for this node or not, including nodes in
+  // display locked subtrees.
+  bool NeedsLayoutTreeUpdateForNodeIncludingDisplayLocked(
+      const Node&,
+      bool ignore_adjacent_style = false) const;
 
   // Update ComputedStyles and attach LayoutObjects if necessary, but don't
   // lay out.
diff --git a/third_party/blink/renderer/core/dom/document_test.cc b/third_party/blink/renderer/core/dom/document_test.cc
index d1cef10..4d4b713 100644
--- a/third_party/blink/renderer/core/dom/document_test.cc
+++ b/third_party/blink/renderer/core/dom/document_test.cc
@@ -322,6 +322,7 @@
   void DocumentDetached(const Document&) override {
     document_detached_was_called = true;
   }
+  void DidChangeFocusTo(const Node*) override {}
   void WillBeDestroyed() override {}
 
   // virtual void Trace(Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/dom/element_data.cc b/third_party/blink/renderer/core/dom/element_data.cc
index c7dd67c4..92e725d 100644
--- a/third_party/blink/renderer/core/dom/element_data.cc
+++ b/third_party/blink/renderer/core/dom/element_data.cc
@@ -46,8 +46,9 @@
 static_assert(sizeof(ElementData) == sizeof(SameSizeAsElementData),
               "ElementData should stay small");
 
-static size_t SizeForShareableElementDataWithAttributeCount(unsigned count) {
-  return sizeof(ShareableElementData) + sizeof(Attribute) * count;
+static AdditionalBytes AdditionalBytesForShareableElementDataWithAttributeCount(
+    unsigned count) {
+  return AdditionalBytes(sizeof(Attribute) * count);
 }
 
 ElementData::ElementData()
@@ -145,9 +146,10 @@
 
 ShareableElementData* ShareableElementData::CreateWithAttributes(
     const Vector<Attribute>& attributes) {
-  void* slot = ThreadHeap::Allocate<ElementData>(
-      SizeForShareableElementDataWithAttributeCount(attributes.size()));
-  return new (slot) ShareableElementData(attributes);
+  return MakeGarbageCollected<ShareableElementData>(
+      AdditionalBytesForShareableElementDataWithAttributeCount(
+          attributes.size()),
+      attributes);
 }
 
 UniqueElementData::UniqueElementData() = default;
@@ -174,9 +176,10 @@
 }
 
 ShareableElementData* UniqueElementData::MakeShareableCopy() const {
-  void* slot = ThreadHeap::Allocate<ElementData>(
-      SizeForShareableElementDataWithAttributeCount(attribute_vector_.size()));
-  return new (slot) ShareableElementData(*this);
+  return MakeGarbageCollected<ShareableElementData>(
+      AdditionalBytesForShareableElementDataWithAttributeCount(
+          attribute_vector_.size()),
+      *this);
 }
 
 void UniqueElementData::TraceAfterDispatch(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/dom/element_data.h b/third_party/blink/renderer/core/dom/element_data.h
index 3a6541f1..72aacbc 100644
--- a/third_party/blink/renderer/core/dom/element_data.h
+++ b/third_party/blink/renderer/core/dom/element_data.h
@@ -131,13 +131,6 @@
     ElementData::TraceAfterDispatch(visitor);
   }
 
-  // Add support for placement new as ShareableElementData is not allocated
-  // with a fixed size. Instead the allocated memory size is computed based on
-  // the number of attributes. This requires us to use ThreadHeap::allocate
-  // directly with the computed size and subsequently call placement new with
-  // the allocated memory address.
-  void* operator new(std::size_t, void* location) { return location; }
-
   AttributeCollection Attributes() const;
 
   Attribute attribute_array_[0];
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index 85fde89..e9ee5c3 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -5426,7 +5426,9 @@
   // selection bounds, selectRange() will select the *next* element. That's
   // strictly correct, as hit-testing checks the pixel to the lower-right of
   // the input coordinate, but it's a wart on the API.
-  return WebPoint(rect.x + rect.width - 1, rect.y + rect.height - 1);
+  if (rect.width > 0)
+    return WebPoint(rect.x + rect.width - 1, rect.y + rect.height - 1);
+  return WebPoint(rect.x + rect.width, rect.y + rect.height - 1);
 }
 
 static WebRect ElementBounds(WebLocalFrame* frame, const WebString& id) {
diff --git a/third_party/blink/renderer/core/html/anchor_element_metrics.cc b/third_party/blink/renderer/core/html/anchor_element_metrics.cc
index b5ecbd2..4624e5d6 100644
--- a/third_party/blink/renderer/core/html/anchor_element_metrics.cc
+++ b/third_party/blink/renderer/core/html/anchor_element_metrics.cc
@@ -138,10 +138,6 @@
 
 }  // anonymous namespace
 
-// Webpage with more than |kMaxAnchorElementMetricsSize| anchor element metrics
-// to report will be ignored, so it should be large enough to cover most pages.
-const int AnchorElementMetrics::kMaxAnchorElementMetricsSize = 40;
-
 // static
 base::Optional<AnchorElementMetrics> AnchorElementMetrics::Create(
     const HTMLAnchorElement* anchor_element) {
@@ -303,8 +299,10 @@
 
     anchor_elements_metrics.push_back(anchor_metric.value().CreateMetricsPtr());
 
-    if (anchor_elements_metrics.size() > kMaxAnchorElementMetricsSize)
-      return;
+    // Webpages with more than 40 anchors will stop processing at the 40th
+    // anchor element.
+    if (anchor_elements_metrics.size() >= 40)
+      break;
   }
 
   if (anchor_elements_metrics.IsEmpty())
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
index fc8f2ab..6f6cb46 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
@@ -366,15 +366,6 @@
   return ListedElement::IsValidElement();
 }
 
-void HTMLFormControlElement::DispatchBlurEvent(
-    Element* new_focused_element,
-    WebFocusType type,
-    InputDeviceCapabilities* source_capabilities) {
-  HTMLElement::DispatchBlurEvent(new_focused_element, type,
-                                 source_capabilities);
-  HideVisibleValidationMessage();
-}
-
 bool HTMLFormControlElement::IsSuccessfulSubmitButton() const {
   return CanBeSuccessfulSubmitButton() && !IsDisabledFormControl();
 }
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.h b/third_party/blink/renderer/core/html/forms/html_form_control_element.h
index a4d2775..30fd4ef49 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.h
@@ -146,9 +146,6 @@
   bool SupportsFocus() const override;
   bool IsKeyboardFocusable() const override;
   bool ShouldHaveFocusAppearance() const final;
-  void DispatchBlurEvent(Element* new_focused_element,
-                         WebFocusType,
-                         InputDeviceCapabilities* source_capabilities) override;
 
   void DidRecalcStyle(const StyleRecalcChange) override;
 
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element_test.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element_test.cc
index 6726d520..3eadd53 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element_test.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element_test.cc
@@ -43,6 +43,7 @@
   }
 
   void DocumentDetached(const Document&) override {}
+  void DidChangeFocusTo(const Node*) override {}
   void WillBeDestroyed() override {}
   void Trace(Visitor* visitor) override {
     visitor->Trace(anchor_);
diff --git a/third_party/blink/renderer/core/html/forms/text_control_inner_elements.h b/third_party/blink/renderer/core/html/forms/text_control_inner_elements.h
index 0d2696b..0abb2d6 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_inner_elements.h
+++ b/third_party/blink/renderer/core/html/forms/text_control_inner_elements.h
@@ -40,6 +40,7 @@
 
  protected:
   LayoutObject* CreateLayoutObject(const ComputedStyle&, LegacyLayout) override;
+  bool TypeShouldForceLegacyLayout() const final { return true; }
 };
 
 class EditingViewPortElement final : public HTMLDivElement {
@@ -52,6 +53,7 @@
   scoped_refptr<ComputedStyle> CustomStyleForLayoutObject() override;
 
  private:
+  bool TypeShouldForceLegacyLayout() const final { return true; }
   bool SupportsFocus() const override { return false; }
 };
 
@@ -68,6 +70,7 @@
 
  private:
   LayoutObject* CreateLayoutObject(const ComputedStyle&, LegacyLayout) override;
+  bool TypeShouldForceLegacyLayout() const final { return true; }
   scoped_refptr<ComputedStyle> CustomStyleForLayoutObject() override;
   bool SupportsFocus() const override { return false; }
   bool is_visible_ = true;
@@ -83,6 +86,7 @@
   bool WillRespondToMouseClickEvents() override;
 
  private:
+  bool TypeShouldForceLegacyLayout() const final { return true; }
   bool SupportsFocus() const override { return false; }
 };
 
diff --git a/third_party/blink/renderer/core/html/media/html_audio_element.h b/third_party/blink/renderer/core/html/media/html_audio_element.h
index a5add00..bafd92f 100644
--- a/third_party/blink/renderer/core/html/media/html_audio_element.h
+++ b/third_party/blink/renderer/core/html/media/html_audio_element.h
@@ -50,6 +50,7 @@
   void MediaRemotingStarted(
       const WebString& remote_device_friendly_name) override {}
   void MediaRemotingStopped(WebLocalizedString::Name error_msg) override {}
+  void PictureInPictureStopped() override { NOTREACHED(); }
   void OnPictureInPictureStateChange() final { NOTREACHED(); }
   void ActivateViewportIntersectionMonitoring(bool) final {}
 };
diff --git a/third_party/blink/renderer/core/html/media/html_video_element.cc b/third_party/blink/renderer/core/html/media/html_video_element.cc
index 311f3664..7ed409b 100644
--- a/third_party/blink/renderer/core/html/media/html_video_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_video_element.cc
@@ -730,6 +730,11 @@
          PictureInPictureController::Status::kEnabled;
 }
 
+void HTMLVideoElement::PictureInPictureStopped() {
+  PictureInPictureController::From(GetDocument())
+      .OnExitedPictureInPicture(nullptr);
+}
+
 WebMediaPlayer::DisplayType HTMLVideoElement::DisplayType() const {
   if (is_auto_picture_in_picture_ ||
       PictureInPictureController::IsElementInPictureInPicture(this)) {
diff --git a/third_party/blink/renderer/core/html/media/html_video_element.h b/third_party/blink/renderer/core/html/media/html_video_element.h
index 4c1c87ff..2978d06 100644
--- a/third_party/blink/renderer/core/html/media/html_video_element.h
+++ b/third_party/blink/renderer/core/html/media/html_video_element.h
@@ -186,6 +186,7 @@
 
   void MediaRemotingStarted(const WebString& remote_device_friendly_name) final;
   bool SupportsPictureInPicture() const final;
+  void PictureInPictureStopped() final;
   void MediaRemotingStopped(WebLocalizedString::Name error_msg) final;
   WebMediaPlayer::DisplayType DisplayType() const final;
   bool IsInAutoPIP() const final;
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
index 18a5a19..a695e86 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
@@ -209,8 +209,14 @@
   }
   if (target->IsBox())
     return LayoutRect(ToLayoutBoxModelObject(target)->BorderBoundingBox());
-  if (target->IsLayoutInline())
-    return ToLayoutInline(target)->PhysicalLinesBoundingBox();
+  if (target->IsLayoutInline()) {
+    return EnclosingLayoutRect(
+        target
+            ->AncestorToLocalQuad(
+                nullptr, FloatQuad(target->AbsoluteBoundingBoxFloatRect()),
+                kUseTransforms | kApplyContainerFlip)
+            .BoundingBox());
+  }
   return ToLayoutText(target)->PhysicalLinesBoundingBox();
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_theme_mac.mm b/third_party/blink/renderer/core/layout/layout_theme_mac.mm
index 501d389..0a07d91 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_mac.mm
+++ b/third_party/blink/renderer/core/layout/layout_theme_mac.mm
@@ -32,7 +32,6 @@
 #import "third_party/blink/renderer/core/fileapi/file_list.h"
 #import "third_party/blink/renderer/core/html_names.h"
 #import "third_party/blink/renderer/core/layout/layout_progress.h"
-#import "third_party/blink/renderer/core/layout/layout_theme_default.h"
 #import "third_party/blink/renderer/core/layout/layout_view.h"
 #import "third_party/blink/renderer/core/style/shadow_list.h"
 #import "third_party/blink/renderer/platform/data_resource_helper.h"
@@ -139,13 +138,6 @@
 
 namespace {
 
-class LayoutThemeMacRefresh final : public LayoutThemeDefault {
- public:
-  static scoped_refptr<LayoutTheme> Create() {
-    return base::AdoptRef(new LayoutThemeMacRefresh());
-  }
-};
-
 // Inflate an IntRect to account for specific padding around margins.
 enum { kTopMargin = 0, kRightMargin = 1, kBottomMargin = 2, kLeftMargin = 3 };
 
@@ -1019,14 +1011,8 @@
 }
 
 LayoutTheme& LayoutTheme::NativeTheme() {
-  if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) {
-    DEFINE_STATIC_REF(LayoutTheme, layout_theme,
-                      (LayoutThemeMacRefresh::Create()));
-    return *layout_theme;
-  } else {
-    DEFINE_STATIC_REF(LayoutTheme, layout_theme, (LayoutThemeMac::Create()));
-    return *layout_theme;
-  }
+  DEFINE_STATIC_REF(LayoutTheme, layout_theme, (LayoutThemeMac::Create()));
+  return *layout_theme;
 }
 
 scoped_refptr<LayoutTheme> LayoutThemeMac::Create() {
diff --git a/third_party/blink/renderer/core/loader/base_fetch_context.cc b/third_party/blink/renderer/core/loader/base_fetch_context.cc
index c21fcb9..12f2825 100644
--- a/third_party/blink/renderer/core/loader/base_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/base_fetch_context.cc
@@ -243,6 +243,7 @@
 }
 
 void BaseFetchContext::Trace(blink::Visitor* visitor) {
+  visitor->Trace(fetcher_properties_);
   FetchContext::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/loader/base_fetch_context.h b/third_party/blink/renderer/core/loader/base_fetch_context.h
index 48674c8..de03346 100644
--- a/third_party/blink/renderer/core/loader/base_fetch_context.h
+++ b/third_party/blink/renderer/core/loader/base_fetch_context.h
@@ -19,6 +19,7 @@
 namespace blink {
 
 class ConsoleMessage;
+class DetachableResourceFetcherProperties;
 class KURL;
 class PreviewsResourceLoadingHints;
 class SecurityOrigin;
@@ -45,6 +46,11 @@
 
   void Trace(blink::Visitor*) override;
 
+  const DetachableResourceFetcherProperties& GetResourceFetcherProperties()
+      const {
+    return *fetcher_properties_;
+  }
+
   virtual KURL GetSiteForCookies() const = 0;
 
   // Returns the origin of the top frame in the document.
@@ -63,7 +69,9 @@
   virtual const ContentSecurityPolicy* GetContentSecurityPolicy() const = 0;
 
  protected:
-  BaseFetchContext() = default;
+  explicit BaseFetchContext(
+      const DetachableResourceFetcherProperties& properties)
+      : fetcher_properties_(properties) {}
 
   // Used for security checks.
   virtual bool AllowScriptFromSource(const KURL&) const = 0;
@@ -92,6 +100,8 @@
   virtual void AddConsoleMessage(ConsoleMessage*) const = 0;
 
  private:
+  const Member<const DetachableResourceFetcherProperties> fetcher_properties_;
+
   void PrintAccessDeniedMessage(const KURL&) const;
 
   // Utility methods that are used in default implement for CanRequest,
diff --git a/third_party/blink/renderer/core/loader/base_fetch_context_test.cc b/third_party/blink/renderer/core/loader/base_fetch_context_test.cc
index 235401d5..cc4d4bf0 100644
--- a/third_party/blink/renderer/core/loader/base_fetch_context_test.cc
+++ b/third_party/blink/renderer/core/loader/base_fetch_context_test.cc
@@ -46,8 +46,9 @@
 
 class MockBaseFetchContext final : public BaseFetchContext {
  public:
-  explicit MockBaseFetchContext(ExecutionContext* execution_context)
-      : execution_context_(execution_context) {}
+  MockBaseFetchContext(const DetachableResourceFetcherProperties& properties,
+                       ExecutionContext* execution_context)
+      : BaseFetchContext(properties), execution_context_(execution_context) {}
   ~MockBaseFetchContext() override = default;
 
   // BaseFetchContext overrides:
@@ -118,14 +119,15 @@
     execution_context_ = MakeGarbageCollected<NullExecutionContext>();
     static_cast<NullExecutionContext*>(execution_context_.Get())
         ->SetUpSecurityContext();
-    fetch_context_ =
-        MakeGarbageCollected<MockBaseFetchContext>(execution_context_);
     resource_fetcher_properties_ =
         MakeGarbageCollected<TestResourceFetcherProperties>(
             *MakeGarbageCollected<FetchClientSettingsObjectImpl>(
                 *execution_context_));
+    auto& properties = resource_fetcher_properties_->MakeDetachable();
+    fetch_context_ = MakeGarbageCollected<MockBaseFetchContext>(
+        properties, execution_context_);
     resource_fetcher_ = MakeGarbageCollected<ResourceFetcher>(
-        ResourceFetcherInit(*resource_fetcher_properties_, fetch_context_,
+        ResourceFetcherInit(properties, fetch_context_,
                             base::MakeRefCounted<scheduler::FakeTaskRunner>(),
                             MakeGarbageCollected<TestLoaderFactory>()));
   }
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.cc b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
index 84ec841..ec23377 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
@@ -213,18 +213,21 @@
 ResourceFetcher* FrameFetchContext::CreateFetcherForCommittedDocument(
     DocumentLoader& loader,
     Document& document) {
-  auto* frame_or_imported_document =
-      MakeGarbageCollected<FrameOrImportedDocument>(loader, document);
-  auto* resource_fetcher_properties =
-      MakeGarbageCollected<FrameResourceFetcherProperties>(
-          *frame_or_imported_document);
-  LocalFrame& frame = frame_or_imported_document->GetFrame();
+  auto& frame_or_imported_document =
+      *MakeGarbageCollected<FrameOrImportedDocument>(loader, document);
+  auto& properties = *MakeGarbageCollected<DetachableResourceFetcherProperties>(
+      *MakeGarbageCollected<FrameResourceFetcherProperties>(
+          frame_or_imported_document));
+  LocalFrame& frame = frame_or_imported_document.GetFrame();
   ResourceFetcherInit init(
-      *resource_fetcher_properties,
-      MakeGarbageCollected<FrameFetchContext>(*frame_or_imported_document),
+      properties,
+      MakeGarbageCollected<FrameFetchContext>(frame_or_imported_document,
+                                              properties),
       frame.GetTaskRunner(TaskType::kNetworking),
-      MakeGarbageCollected<LoaderFactoryForFrame>(*frame_or_imported_document));
-  init.console_logger = &document;
+      MakeGarbageCollected<LoaderFactoryForFrame>(frame_or_imported_document));
+  auto* console_logger =
+      MakeGarbageCollected<DetachableConsoleLogger>(&document);
+  init.console_logger = console_logger;
   // Frame loading should normally start with |kTight| throttling, as the
   // frame will be in layout-blocking state until the <body> tag is inserted
   init.initial_throttling_policy =
@@ -234,7 +237,7 @@
   ResourceFetcher* fetcher = MakeGarbageCollected<ResourceFetcher>(init);
   fetcher->SetResourceLoadObserver(
       MakeGarbageCollected<ResourceLoadObserverForFrame>(
-          *frame_or_imported_document, fetcher->GetProperties()));
+          frame_or_imported_document, fetcher->GetProperties()));
   fetcher->SetImagesEnabled(frame.GetSettings()->GetImagesEnabled());
   fetcher->SetAutoLoadImages(
       frame.GetSettings()->GetLoadsImagesAutomatically());
@@ -248,14 +251,19 @@
   DCHECK(!document->GetFrame());
   auto& frame_or_imported_document =
       *MakeGarbageCollected<FrameOrImportedDocument>(*document);
+  auto& properties = *MakeGarbageCollected<DetachableResourceFetcherProperties>(
+      *MakeGarbageCollected<FrameResourceFetcherProperties>(
+          frame_or_imported_document));
   LocalFrame& frame = frame_or_imported_document.GetFrame();
   ResourceFetcherInit init(
-      *MakeGarbageCollected<FrameResourceFetcherProperties>(
-          frame_or_imported_document),
-      MakeGarbageCollected<FrameFetchContext>(frame_or_imported_document),
+      properties,
+      MakeGarbageCollected<FrameFetchContext>(frame_or_imported_document,
+                                              properties),
       document->GetTaskRunner(blink::TaskType::kNetworking),
       MakeGarbageCollected<LoaderFactoryForFrame>(frame_or_imported_document));
-  init.console_logger = document;
+  auto* console_logger =
+      MakeGarbageCollected<DetachableConsoleLogger>(document);
+  init.console_logger = console_logger;
   init.frame_scheduler = frame.GetFrameScheduler();
   auto* fetcher = MakeGarbageCollected<ResourceFetcher>(init);
   fetcher->SetResourceLoadObserver(
@@ -265,8 +273,10 @@
 }
 
 FrameFetchContext::FrameFetchContext(
-    const FrameOrImportedDocument& frame_or_imported_document)
-    : frame_or_imported_document_(frame_or_imported_document),
+    const FrameOrImportedDocument& frame_or_imported_document,
+    const DetachableResourceFetcherProperties& properties)
+    : BaseFetchContext(properties),
+      frame_or_imported_document_(frame_or_imported_document),
       save_data_enabled_(
           GetNetworkStateNotifier().SaveDataEnabled() &&
           !GetFrame()->GetSettings()->GetDataSaverHoldbackWebApi()) {}
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.h b/third_party/blink/renderer/core/loader/frame_fetch_context.h
index 88f0be9f..756bbef 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context.h
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context.h
@@ -66,7 +66,8 @@
   // |document_loader_| will be set to nullptr.
   static ResourceFetcher* CreateFetcherForImportedDocument(Document* document);
 
-  explicit FrameFetchContext(const FrameOrImportedDocument&);
+  FrameFetchContext(const FrameOrImportedDocument&,
+                    const DetachableResourceFetcherProperties&);
   ~FrameFetchContext() override = default;
 
   void AddAdditionalRequestHeaders(ResourceRequest&) override;
diff --git a/third_party/blink/renderer/core/loader/modulescript/installed_service_worker_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/installed_service_worker_module_script_fetcher.cc
index d0ca82f9..2292755 100644
--- a/third_party/blink/renderer/core/loader/modulescript/installed_service_worker_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/installed_service_worker_module_script_fetcher.cc
@@ -65,7 +65,8 @@
 
     global_scope_->Initialize(response_url, response_referrer_policy,
                               script_data->GetResponseAddressSpace(),
-                              response_content_security_policy->Headers());
+                              response_content_security_policy->Headers(),
+                              script_data->CreateOriginTrialTokens().get());
   }
 
   ModuleScriptCreationParams params(
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc b/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
index a6536ff..f8e6a18d 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
@@ -166,7 +166,7 @@
   auto* properties =
       MakeGarbageCollected<TestResourceFetcherProperties>(security_origin_);
   fetcher_ = MakeGarbageCollected<ResourceFetcher>(
-      ResourceFetcherInit(*properties, fetch_context,
+      ResourceFetcherInit(properties->MakeDetachable(), fetch_context,
                           base::MakeRefCounted<scheduler::FakeTaskRunner>(),
                           MakeGarbageCollected<TestLoaderFactory>()));
   modulator_ = MakeGarbageCollected<ModuleScriptLoaderTestModulator>(
@@ -178,7 +178,7 @@
   auto* properties =
       MakeGarbageCollected<TestResourceFetcherProperties>(security_origin_);
   fetcher_ = MakeGarbageCollected<ResourceFetcher>(
-      ResourceFetcherInit(*properties, fetch_context,
+      ResourceFetcherInit(properties->MakeDetachable(), fetch_context,
                           base::MakeRefCounted<scheduler::FakeTaskRunner>(),
                           MakeGarbageCollected<TestLoaderFactory>()));
   reporting_proxy_ = std::make_unique<MockWorkerReportingProxy>();
diff --git a/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
index 187aeba8..30399ed 100644
--- a/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
@@ -6,6 +6,7 @@
 
 #include "services/network/public/mojom/referrer_policy.mojom-shared.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
+#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
 #include "third_party/blink/renderer/platform/network/content_security_policy_response_headers.h"
@@ -113,10 +114,15 @@
     response_content_security_policy->DidReceiveHeaders(
         ContentSecurityPolicyResponseHeaders(resource->GetResponse()));
 
+    std::unique_ptr<Vector<String>> response_origin_trial_tokens =
+        OriginTrialContext::ParseHeaderValue(
+            resource->GetResponse().HttpHeaderField(http_names::kOriginTrial));
+
     // Step 12.3-12.6 are implemented in Initialize().
     global_scope_->Initialize(response_url, response_referrer_policy,
                               response_address_space,
-                              response_content_security_policy->Headers());
+                              response_content_security_policy->Headers(),
+                              response_origin_trial_tokens.get());
   }
 
   ModuleScriptCreationParams params(
diff --git a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
index 859dd0d..85b72c9 100644
--- a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
@@ -64,9 +64,10 @@
 
   MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
-      *properties, context, base::MakeRefCounted<scheduler::FakeTaskRunner>(),
-      MakeGarbageCollected<TestLoaderFactory>()));
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(properties->MakeDetachable(), context,
+                          base::MakeRefCounted<scheduler::FakeTaskRunner>(),
+                          MakeGarbageCollected<TestLoaderFactory>()));
 
   // Fetch to cache a resource.
   ResourceRequest request1(url);
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
index 8b15efc7..decab27 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
@@ -358,10 +358,10 @@
 
 ResourceFetcher* CreateFetcher() {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  return MakeGarbageCollected<ResourceFetcher>(
-      ResourceFetcherInit(*properties, MakeGarbageCollected<MockFetchContext>(),
-                          base::MakeRefCounted<scheduler::FakeTaskRunner>(),
-                          MakeGarbageCollected<TestLoaderFactory>()));
+  return MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+      properties->MakeDetachable(), MakeGarbageCollected<MockFetchContext>(),
+      base::MakeRefCounted<scheduler::FakeTaskRunner>(),
+      MakeGarbageCollected<TestLoaderFactory>()));
 }
 
 TEST(ImageResourceTest, MultipartImage) {
@@ -1870,14 +1870,15 @@
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
       page_holder->GetFrame().GetTaskRunner(TaskType::kInternalTest);
   auto* context = MakeGarbageCollected<MockFetchContext>();
-  auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
+  auto& properties =
+      MakeGarbageCollected<TestResourceFetcherProperties>()->MakeDetachable();
   auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
-      ResourceFetcherInit(*properties, context, task_runner,
+      ResourceFetcherInit(properties, context, task_runner,
                           MakeGarbageCollected<TestLoaderFactory>()));
   auto frame_scheduler = std::make_unique<scheduler::FakeFrameScheduler>();
   auto* scheduler = MakeGarbageCollected<ResourceLoadScheduler>(
-      ResourceLoadScheduler::ThrottlingPolicy::kNormal, *properties,
-      frame_scheduler.get(), *MakeGarbageCollected<NullConsoleLogger>());
+      ResourceLoadScheduler::ThrottlingPolicy::kNormal, properties,
+      frame_scheduler.get(), *MakeGarbageCollected<DetachableConsoleLogger>());
   ImageResource* image_resource = ImageResource::CreateForTest(test_url);
 
   // Ensure that |image_resource| has a loader.
diff --git a/third_party/blink/renderer/core/loader/worker_fetch_context.cc b/third_party/blink/renderer/core/loader/worker_fetch_context.cc
index 4fcf3ace..1ad9473 100644
--- a/third_party/blink/renderer/core/loader/worker_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/worker_fetch_context.cc
@@ -35,12 +35,14 @@
 WorkerFetchContext::~WorkerFetchContext() = default;
 
 WorkerFetchContext::WorkerFetchContext(
+    const DetachableResourceFetcherProperties& properties,
     WorkerOrWorkletGlobalScope& global_scope,
     scoped_refptr<WebWorkerFetchContext> web_context,
     SubresourceFilter* subresource_filter,
     ContentSecurityPolicy& content_security_policy,
     WorkerResourceTimingNotifier& resource_timing_notifier)
-    : global_scope_(global_scope),
+    : BaseFetchContext(properties),
+      global_scope_(global_scope),
       web_context_(std::move(web_context)),
       subresource_filter_(subresource_filter),
       content_security_policy_(&content_security_policy),
diff --git a/third_party/blink/renderer/core/loader/worker_fetch_context.h b/third_party/blink/renderer/core/loader/worker_fetch_context.h
index 6d0fda6..68671371 100644
--- a/third_party/blink/renderer/core/loader/worker_fetch_context.h
+++ b/third_party/blink/renderer/core/loader/worker_fetch_context.h
@@ -32,7 +32,8 @@
 // For more details, see core/workers/README.md.
 class WorkerFetchContext final : public BaseFetchContext {
  public:
-  WorkerFetchContext(WorkerOrWorkletGlobalScope&,
+  WorkerFetchContext(const DetachableResourceFetcherProperties&,
+                     WorkerOrWorkletGlobalScope&,
                      scoped_refptr<WebWorkerFetchContext>,
                      SubresourceFilter*,
                      ContentSecurityPolicy&,
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc
index 60f7bf3..8e83720 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -92,6 +92,7 @@
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/popup_opening_observer.h"
+#include "third_party/blink/renderer/core/page/validation_message_client.h"
 #include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
 #include "third_party/blink/renderer/platform/cursor.h"
 #include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h"
@@ -217,6 +218,8 @@
 }
 
 void ChromeClientImpl::FocusedNodeChanged(Node* from_node, Node* to_node) {
+  web_view_->GetPage()->GetValidationMessageClient().DidChangeFocusTo(to_node);
+
   if (!web_view_->Client())
     return;
 
diff --git a/third_party/blink/renderer/core/page/page_animator.h b/third_party/blink/renderer/core/page/page_animator.h
index 0e2616f8..ba21a58d 100644
--- a/third_party/blink/renderer/core/page/page_animator.h
+++ b/third_party/blink/renderer/core/page/page_animator.h
@@ -40,6 +40,9 @@
   void UpdateAllLifecyclePhasesExceptPaint(LocalFrame& root_frame);
   void UpdateLifecycleToLayoutClean(LocalFrame& root_frame);
   AnimationClock& Clock() { return animation_clock_; }
+  bool UpdatingLayoutAndStyleForPainting() {
+    return updating_layout_and_style_for_painting_;
+  }
 
  private:
   Member<Page> page_;
diff --git a/third_party/blink/renderer/core/page/validation_message_client.h b/third_party/blink/renderer/core/page/validation_message_client.h
index 293442f..beb9806 100644
--- a/third_party/blink/renderer/core/page/validation_message_client.h
+++ b/third_party/blink/renderer/core/page/validation_message_client.h
@@ -58,6 +58,7 @@
   virtual bool IsValidationMessageVisible(const Element& anchor) = 0;
 
   virtual void DocumentDetached(const Document&) = 0;
+  virtual void DidChangeFocusTo(const Node* new_node) = 0;
 
   virtual void WillBeDestroyed() = 0;
 
diff --git a/third_party/blink/renderer/core/page/validation_message_client_impl.cc b/third_party/blink/renderer/core/page/validation_message_client_impl.cc
index fadd416..f88383a 100644
--- a/third_party/blink/renderer/core/page/validation_message_client_impl.cc
+++ b/third_party/blink/renderer/core/page/validation_message_client_impl.cc
@@ -155,6 +155,11 @@
     HideValidationMessageImmediately(*current_anchor_);
 }
 
+void ValidationMessageClientImpl::DidChangeFocusTo(const Node* new_node) {
+  if (current_anchor_ && current_anchor_ != new_node)
+    HideValidationMessageImmediately(*current_anchor_);
+}
+
 void ValidationMessageClientImpl::CheckAnchorStatus(TimerBase*) {
   DCHECK(current_anchor_);
   if ((!WebTestSupport::IsRunningWebTest() &&
diff --git a/third_party/blink/renderer/core/page/validation_message_client_impl.h b/third_party/blink/renderer/core/page/validation_message_client_impl.h
index 2164b77..d0f965f 100644
--- a/third_party/blink/renderer/core/page/validation_message_client_impl.h
+++ b/third_party/blink/renderer/core/page/validation_message_client_impl.h
@@ -67,6 +67,7 @@
   void HideValidationMessage(const Element& anchor) override;
   bool IsValidationMessageVisible(const Element& anchor) override;
   void DocumentDetached(const Document&) override;
+  void DidChangeFocusTo(const Node* new_node) override;
   void WillBeDestroyed() override;
   void LayoutOverlay() override;
   void UpdatePrePaint() override;
diff --git a/third_party/blink/renderer/core/paint/paint_layer_test.cc b/third_party/blink/renderer/core/paint/paint_layer_test.cc
index 77fb3ce9..7c0919356 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_test.cc
@@ -1429,7 +1429,13 @@
   // span_layer should be marked NeedsRepaint.
   target_element->setAttribute(html_names::kStyleAttr,
                                "overflow: hidden; float: left");
+
   GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
+  // TODO(yosin): Once multicol in LayoutNG, we should remove following
+  // assignments. This is because the layout tree maybe reattached. In LayoutNG
+  // phase 1, layout tree is reattached because multicol forces legacy layout.
+  target_object = target_element->GetLayoutObject();
+  target_layer = ToLayoutBoxModelObject(target_object)->Layer();
   EXPECT_FALSE(target_layer->IsSelfPaintingLayer());
   if (RuntimeEnabledFeatures::LayoutNGEnabled()) {
     EXPECT_EQ(span_layer, target_layer->CompositingContainer());
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index 51cc2c5..2eb7cdc 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -813,8 +813,9 @@
 
 bool Internals::isValidationMessageVisible(Element* element) {
   DCHECK(element);
-  return IsHTMLFormControlElement(element) &&
-         ToHTMLFormControlElement(element)->IsValidationMessageVisible();
+  if (auto* control = ListedElement::From(*element))
+    return control->IsValidationMessageVisible();
+  return false;
 }
 
 void Internals::selectColorInColorChooser(Element* element,
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
index bea6acc..7431ad9 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
@@ -61,12 +61,16 @@
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
     DedicatedWorkerThread* thread,
     base::TimeTicks time_origin) {
+  std::unique_ptr<Vector<String>> outside_origin_trial_tokens =
+      std::move(creation_params->origin_trial_tokens);
+
   // Off-the-main-thread worker script fetch:
   // Initialize() is called after script fetch.
   if (creation_params->off_main_thread_fetch_option ==
       OffMainThreadWorkerScriptFetchOption::kEnabled) {
     return MakeGarbageCollected<DedicatedWorkerGlobalScope>(
-        std::move(creation_params), thread, time_origin);
+        std::move(creation_params), thread, time_origin,
+        std::move(outside_origin_trial_tokens));
   }
 
   // Legacy on-the-main-thread worker script fetch (to be removed):
@@ -76,19 +80,27 @@
   mojom::IPAddressSpace response_address_space =
       *creation_params->response_address_space;
   auto* global_scope = MakeGarbageCollected<DedicatedWorkerGlobalScope>(
-      std::move(creation_params), thread, time_origin);
-  // A dummy CSP headers is passed here as it is superseded by outside's CSP
-  // headers in Initialize().
+      std::move(creation_params), thread, time_origin,
+      std::move(outside_origin_trial_tokens));
+  // Pass dummy CSP headers here as it is superseded by outside's CSP headers in
+  // Initialize().
+  // Pass dummy origin trial tokens here as it is already set to outside's
+  // origin trial tokens in DedicatedWorkerGlobalScope's constructor.
   global_scope->Initialize(response_script_url, response_referrer_policy,
-                           response_address_space, Vector<CSPHeaderAndType>());
+                           response_address_space, Vector<CSPHeaderAndType>(),
+                           nullptr /* response_origin_trial_tokens */);
   return global_scope;
 }
 
 DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
     DedicatedWorkerThread* thread,
-    base::TimeTicks time_origin)
-    : WorkerGlobalScope(std::move(creation_params), thread, time_origin) {}
+    base::TimeTicks time_origin,
+    std::unique_ptr<Vector<String>> outside_origin_trial_tokens)
+    : WorkerGlobalScope(std::move(creation_params), thread, time_origin) {
+  // Inherit the outside's origin trial tokens.
+  OriginTrialContext::AddTokens(this, outside_origin_trial_tokens.get());
+}
 
 DedicatedWorkerGlobalScope::~DedicatedWorkerGlobalScope() = default;
 
@@ -101,7 +113,8 @@
     const KURL& response_url,
     network::mojom::ReferrerPolicy response_referrer_policy,
     mojom::IPAddressSpace response_address_space,
-    const Vector<CSPHeaderAndType>& /* not used */) {
+    const Vector<CSPHeaderAndType>& /* response_csp_headers */,
+    const Vector<String>* /* response_origin_trial_tokens */) {
   // Step 12.3. "Set worker global scope's url to response's url."
   InitializeURL(response_url);
 
@@ -118,11 +131,15 @@
 
   // Step 12.6. "Execute the Initialize a global object's CSP list algorithm
   // on worker global scope and response. [CSP]"
-  // DedicatedWorkerGlobalScope inherits the outside's CSP instead of the passed
-  // CSP. These should be called after SetAddressSpace() to correctly override
-  // the address space by the "treat-as-public-address" CSP directive.
+  // DedicatedWorkerGlobalScope inherits the outside's CSP instead of the
+  // response CSP headers. These should be called after SetAddressSpace() to
+  // correctly override the address space by the "treat-as-public-address" CSP
+  // directive.
   InitContentSecurityPolicyFromVector(OutsideContentSecurityPolicyHeaders());
   BindContentSecurityPolicyToExecutionContext();
+
+  // DedicatedWorkerGlobalScope inherits the outside's OriginTrialTokens in the
+  // constructor instead of the response origin trial tokens.
 }
 
 // https://html.spec.whatwg.org/C/#worker-processing-model
@@ -260,11 +277,14 @@
   }
 
   // Step 12.3-12.6 are implemented in Initialize().
-  // A dummy CSP headers is passed here as it is superseded by outside's CSP
-  // headers in Initialize().
+  // Pass dummy CSP headers here as it is superseded by outside's CSP headers in
+  // Initialize().
+  // Pass dummy origin trial tokens here as it is already set to outside's
+  // origin trial tokens in DedicatedWorkerGlobalScope's constructor.
   Initialize(classic_script_loader->ResponseURL(), response_referrer_policy,
              classic_script_loader->ResponseAddressSpace(),
-             Vector<CSPHeaderAndType>());
+             Vector<CSPHeaderAndType>(),
+             nullptr /* response_origin_trial_tokens */);
 
   // Step 12.7. "Asynchronously complete the perform the fetch steps with
   // response."
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h
index 3b1d3f7c..36bcb564 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h
@@ -60,9 +60,11 @@
 
   // Do not call this. Use Create() instead. This is public only for
   // MakeGarbageCollected.
-  DedicatedWorkerGlobalScope(std::unique_ptr<GlobalScopeCreationParams>,
-                             DedicatedWorkerThread*,
-                             base::TimeTicks time_origin);
+  DedicatedWorkerGlobalScope(
+      std::unique_ptr<GlobalScopeCreationParams>,
+      DedicatedWorkerThread*,
+      base::TimeTicks time_origin,
+      std::unique_ptr<Vector<String>> outside_origin_trial_tokens);
 
   ~DedicatedWorkerGlobalScope() override;
 
@@ -74,11 +76,11 @@
   const AtomicString& InterfaceName() const override;
 
   // Implements WorkerGlobalScope.
-  void Initialize(
-      const KURL& response_url,
-      network::mojom::ReferrerPolicy response_referrer_policy,
-      mojom::IPAddressSpace response_address_space,
-      const Vector<CSPHeaderAndType>& response_csp_headers) override;
+  void Initialize(const KURL& response_url,
+                  network::mojom::ReferrerPolicy response_referrer_policy,
+                  mojom::IPAddressSpace response_address_space,
+                  const Vector<CSPHeaderAndType>& response_csp_headers,
+                  const Vector<String>* response_origin_trial_tokens) override;
   void FetchAndRunClassicScript(
       const KURL& script_url,
       const FetchClientSettingsObjectSnapshot& outside_settings_object,
diff --git a/third_party/blink/renderer/core/workers/experimental/thread_pool_thread.cc b/third_party/blink/renderer/core/workers/experimental/thread_pool_thread.cc
index 3701f76c..61cb937 100644
--- a/third_party/blink/renderer/core/workers/experimental/thread_pool_thread.cc
+++ b/third_party/blink/renderer/core/workers/experimental/thread_pool_thread.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/workers/experimental/thread_pool_thread.h"
 
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
 #include "third_party/blink/renderer/core/workers/experimental/task_worklet_global_scope.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
 #include "third_party/blink/renderer/core/workers/threaded_object_proxy_base.h"
@@ -36,11 +37,11 @@
   }
 
   // WorkerGlobalScope
-  void Initialize(
-      const KURL& response_url,
-      network::mojom::ReferrerPolicy response_referrer_policy,
-      mojom::IPAddressSpace response_address_space,
-      const Vector<CSPHeaderAndType>& response_csp_headers) override {
+  void Initialize(const KURL& response_url,
+                  network::mojom::ReferrerPolicy response_referrer_policy,
+                  mojom::IPAddressSpace response_address_space,
+                  const Vector<CSPHeaderAndType>& response_csp_headers,
+                  const Vector<String>* response_origin_trial_tokens) override {
     InitializeURL(response_url);
     SetReferrerPolicy(response_referrer_policy);
     SetAddressSpace(response_address_space);
@@ -49,6 +50,8 @@
     // address space by the "treat-as-public-address" CSP directive.
     InitContentSecurityPolicyFromVector(response_csp_headers);
     BindContentSecurityPolicyToExecutionContext();
+
+    OriginTrialContext::AddTokens(this, response_origin_trial_tokens);
   }
   void FetchAndRunClassicScript(
       const KURL& script_url,
diff --git a/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc b/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
index 04a208d..13acda45 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
@@ -38,6 +38,7 @@
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/inspector/worker_thread_debugger.h"
+#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/core/workers/shared_worker_thread.h"
 #include "third_party/blink/renderer/core/workers/worker_classic_script_loader.h"
@@ -74,10 +75,13 @@
   // off-the-main-thread worker script fetch by default.
   Vector<CSPHeaderAndType> response_csp_headers =
       creation_params->outside_content_security_policy_headers;
+  std::unique_ptr<Vector<String>> response_origin_trial_tokens =
+      std::move(creation_params->origin_trial_tokens);
   auto* global_scope = MakeGarbageCollected<SharedWorkerGlobalScope>(
       std::move(creation_params), thread, time_origin);
   global_scope->Initialize(response_script_url, response_referrer_policy,
-                           response_address_space, response_csp_headers);
+                           response_address_space, response_csp_headers,
+                           response_origin_trial_tokens.get());
   return global_scope;
 }
 
@@ -98,7 +102,8 @@
     const KURL& response_url,
     network::mojom::ReferrerPolicy response_referrer_policy,
     mojom::IPAddressSpace response_address_space,
-    const Vector<CSPHeaderAndType>& response_csp_headers) {
+    const Vector<CSPHeaderAndType>& response_csp_headers,
+    const Vector<String>* response_origin_trial_tokens) {
   // Step 12.3. "Set worker global scope's url to response's url."
   InitializeURL(response_url);
 
@@ -119,6 +124,8 @@
   // address space by the "treat-as-public-address" CSP directive.
   InitContentSecurityPolicyFromVector(response_csp_headers);
   BindContentSecurityPolicyToExecutionContext();
+
+  OriginTrialContext::AddTokens(this, response_origin_trial_tokens);
 }
 
 // https://html.spec.whatwg.org/C/#worker-processing-model
@@ -226,7 +233,8 @@
              classic_script_loader->ResponseAddressSpace(),
              classic_script_loader->GetContentSecurityPolicy()
                  ? classic_script_loader->GetContentSecurityPolicy()->Headers()
-                 : Vector<CSPHeaderAndType>());
+                 : Vector<CSPHeaderAndType>(),
+             classic_script_loader->OriginTrialTokens());
 
   // Step 12.7. "Asynchronously complete the perform the fetch steps with
   // response."
diff --git a/third_party/blink/renderer/core/workers/shared_worker_global_scope.h b/third_party/blink/renderer/core/workers/shared_worker_global_scope.h
index bb055e1..0e9938c 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/shared_worker_global_scope.h
@@ -68,11 +68,11 @@
   const AtomicString& InterfaceName() const override;
 
   // WorkerGlobalScope
-  void Initialize(
-      const KURL& response_url,
-      network::mojom::ReferrerPolicy response_referrer_policy,
-      mojom::IPAddressSpace response_address_space,
-      const Vector<CSPHeaderAndType>& response_csp_headers) override;
+  void Initialize(const KURL& response_url,
+                  network::mojom::ReferrerPolicy response_referrer_policy,
+                  mojom::IPAddressSpace response_address_space,
+                  const Vector<CSPHeaderAndType>& response_csp_headers,
+                  const Vector<String>* response_origin_trial_tokens) override;
   void FetchAndRunClassicScript(
       const KURL& script_url,
       const FetchClientSettingsObjectSnapshot& outside_settings_object,
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.cc b/third_party/blink/renderer/core/workers/worker_global_scope.cc
index c1d33b8..54abbb7 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -50,7 +50,6 @@
 #include "third_party/blink/renderer/core/inspector/worker_thread_debugger.h"
 #include "third_party/blink/renderer/core/loader/threadable_loader.h"
 #include "third_party/blink/renderer/core/messaging/message_port.h"
-#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_script_url.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h"
@@ -450,8 +449,6 @@
   SetOutsideContentSecurityPolicyHeaders(
       creation_params->outside_content_security_policy_headers);
   SetWorkerSettings(std::move(creation_params->worker_settings));
-  OriginTrialContext::AddTokens(this,
-                                creation_params->origin_trial_tokens.get());
 
   // TODO(sammc): Require a valid |creation_params->interface_provider| once all
   // worker types provide a valid |creation_params->interface_provider|.
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.h b/third_party/blink/renderer/core/workers/worker_global_scope.h
index e37eb55..3a2df46 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.h
@@ -151,7 +151,8 @@
       const KURL& response_url,
       network::mojom::ReferrerPolicy response_referrer_policy,
       mojom::IPAddressSpace response_address_space,
-      const Vector<CSPHeaderAndType>& response_csp_headers) = 0;
+      const Vector<CSPHeaderAndType>& response_csp_headers,
+      const Vector<String>* response_origin_trial_tokens) = 0;
 
   // These methods should be called in the scope of a pausable
   // task runner. ie. They should not be called when the context
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
index 5875f6e4..fbff9c2 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
@@ -290,28 +290,34 @@
   InitializeWebFetchContextIfNeeded();
   ResourceFetcher* fetcher = nullptr;
   if (web_worker_fetch_context_) {
+    auto& properties =
+        *MakeGarbageCollected<DetachableResourceFetcherProperties>(
+            *MakeGarbageCollected<WorkerResourceFetcherProperties>(
+                *this, fetch_client_settings_object,
+                web_worker_fetch_context_));
     ResourceFetcherInit init(
-        *MakeGarbageCollected<WorkerResourceFetcherProperties>(
-            *this, fetch_client_settings_object, web_worker_fetch_context_),
-
+        properties,
         MakeGarbageCollected<WorkerFetchContext>(
-            *this, web_worker_fetch_context_, subresource_filter_,
+            properties, *this, web_worker_fetch_context_, subresource_filter_,
             content_security_policy, resource_timing_notifier),
         GetTaskRunner(TaskType::kNetworking),
         MakeGarbageCollected<LoaderFactoryForWorker>(
             *this, web_worker_fetch_context_));
-    init.console_logger = this;
+    auto* console_logger = MakeGarbageCollected<DetachableConsoleLogger>(this);
+    init.console_logger = console_logger;
     fetcher = MakeGarbageCollected<ResourceFetcher>(init);
     fetcher->SetResourceLoadObserver(
         MakeGarbageCollected<ResourceLoadObserverForWorker>(
             *probe::ToCoreProbeSink(static_cast<ExecutionContext*>(this)),
             fetcher->GetProperties(), web_worker_fetch_context_));
   } else {
+    auto& properties =
+        *MakeGarbageCollected<DetachableResourceFetcherProperties>(
+            *MakeGarbageCollected<NullResourceFetcherProperties>());
     // This code path is for unittests.
     fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
-        *MakeGarbageCollected<NullResourceFetcherProperties>(),
-        &FetchContext::NullInstance(), GetTaskRunner(TaskType::kNetworking),
-        nullptr /* loader_factory */));
+        properties, &FetchContext::NullInstance(),
+        GetTaskRunner(TaskType::kNetworking), nullptr /* loader_factory */));
   }
   if (IsContextPaused())
     fetcher->SetDefersLoading(true);
diff --git a/third_party/blink/renderer/core/workers/worker_thread_test_helper.h b/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
index cbbc04f..93afffd4 100644
--- a/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
+++ b/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
@@ -18,6 +18,7 @@
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/inspector/worker_devtools_params.h"
+#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
 #include "third_party/blink/renderer/core/script/script.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
 #include "third_party/blink/renderer/core/workers/parent_execution_context_task_runners.h"
@@ -57,16 +58,21 @@
   }
 
   // WorkerGlobalScope
-  void Initialize(
-      const KURL& response_url,
-      network::mojom::ReferrerPolicy response_referrer_policy,
-      mojom::IPAddressSpace response_address_space,
-      const Vector<CSPHeaderAndType>& response_csp_headers) override {
+  void Initialize(const KURL& response_url,
+                  network::mojom::ReferrerPolicy response_referrer_policy,
+                  mojom::IPAddressSpace response_address_space,
+                  const Vector<CSPHeaderAndType>& response_csp_headers,
+                  const Vector<String>* response_origin_trial_tokens) override {
     InitializeURL(response_url);
     SetReferrerPolicy(response_referrer_policy);
     SetAddressSpace(response_address_space);
+
+    // These should be called after SetAddressSpace() to correctly override the
+    // address space by the "treat-as-public-address" CSP directive.
     InitContentSecurityPolicyFromVector(response_csp_headers);
     BindContentSecurityPolicyToExecutionContext();
+
+    OriginTrialContext::AddTokens(this, response_origin_trial_tokens);
   }
   void FetchAndRunClassicScript(
       const KURL& script_url,
diff --git a/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc b/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc
index 0d1f749b..027dcfce 100644
--- a/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc
+++ b/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc
@@ -28,9 +28,10 @@
     platform_->AdvanceClockSeconds(1.);  // For non-zero DocumentParserTimings
     auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
     auto* context = MakeGarbageCollected<MockFetchContext>();
-    fetcher_ = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
-        *properties, context, base::MakeRefCounted<scheduler::FakeTaskRunner>(),
-        MakeGarbageCollected<TestLoaderFactory>()));
+    fetcher_ = MakeGarbageCollected<ResourceFetcher>(
+        ResourceFetcherInit(properties->MakeDetachable(), context,
+                            base::MakeRefCounted<scheduler::FakeTaskRunner>(),
+                            MakeGarbageCollected<TestLoaderFactory>()));
     map_ = MakeGarbageCollected<WorkletModuleResponsesMap>();
   }
 
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc b/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
index 42958d1e..87d99ce 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
@@ -319,6 +319,7 @@
 
 bool ClipboardPromise::IsFocusedDocument(ExecutionContext* context) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(context);
   DCHECK(context->IsSecureContext());  // [SecureContext] in IDL
   Document* doc = To<Document>(context);
   return doc && doc->hasFocus();
diff --git a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
index dd8c6e7..ebd2193 100644
--- a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
+++ b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
@@ -359,7 +359,7 @@
 }
 
 void PictureInPictureControllerImpl::OnStopped() {
-  OnExitedPictureInPicture(nullptr);
+  // TODO(940694): implement OnStopped() and remove IPC message.
 }
 
 bool PictureInPictureControllerImpl::ShouldShowMuteButton(
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index bd3c57d..71d551c 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -133,6 +133,8 @@
       creation_params->referrer_policy;
   mojom::IPAddressSpace response_address_space =
       *creation_params->response_address_space;
+  std::unique_ptr<Vector<String>> response_origin_trial_tokens =
+      std::move(creation_params->origin_trial_tokens);
   // Contrary to the name, |outside_content_security_policy_headers| contains
   // worker script's response CSP headers in this case.
   // TODO(nhiroki): Introduce inside's csp headers field in
@@ -144,7 +146,8 @@
       std::move(creation_params), thread, std::move(cache_storage_info),
       time_origin);
   global_scope->Initialize(response_url, response_referrer_policy,
-                           response_address_space, response_csp_headers);
+                           response_address_space, response_csp_headers,
+                           response_origin_trial_tokens.get());
   return global_scope;
 }
 
@@ -256,10 +259,6 @@
   }
   ReportingProxy().DidLoadClassicScript();
 
-  std::unique_ptr<Vector<String>> origin_trial_tokens =
-      script_data->CreateOriginTrialTokens();
-  OriginTrialContext::AddTokens(this, origin_trial_tokens.get());
-
   auto referrer_policy = network::mojom::ReferrerPolicy::kDefault;
   if (!script_data->GetReferrerPolicy().IsNull()) {
     SecurityPolicy::ReferrerPolicyFromHeaderValue(
@@ -276,8 +275,9 @@
 
   RunClassicScript(
       script_url, referrer_policy, script_data->GetResponseAddressSpace(),
-      content_security_policy->Headers(), script_data->TakeSourceText(),
-      script_data->TakeMetaData(), stack_id);
+      content_security_policy->Headers(),
+      script_data->CreateOriginTrialTokens().get(),
+      script_data->TakeSourceText(), script_data->TakeMetaData(), stack_id);
 }
 
 void ServiceWorkerGlobalScope::RunInstalledModuleScript(
@@ -427,6 +427,7 @@
       classic_script_loader->GetContentSecurityPolicy()
           ? classic_script_loader->GetContentSecurityPolicy()->Headers()
           : Vector<CSPHeaderAndType>(),
+      classic_script_loader->OriginTrialTokens(),
       classic_script_loader->SourceText(),
       classic_script_loader->ReleaseCachedMetadata(), stack_id);
 }
@@ -436,7 +437,8 @@
     const KURL& response_url,
     network::mojom::ReferrerPolicy response_referrer_policy,
     mojom::IPAddressSpace response_address_space,
-    const Vector<CSPHeaderAndType>& response_csp_headers) {
+    const Vector<CSPHeaderAndType>& response_csp_headers,
+    const Vector<String>* response_origin_trial_tokens) {
   // Step 4.5. "Set workerGlobalScope's url to serviceWorker's script url."
   InitializeURL(response_url);
 
@@ -467,6 +469,8 @@
   InitContentSecurityPolicyFromVector(response_csp_headers);
   BindContentSecurityPolicyToExecutionContext();
 
+  OriginTrialContext::AddTokens(this, response_origin_trial_tokens);
+
   // TODO(nhiroki): Clarify mappings between the steps 4.8-4.11 and
   // implementation.
 }
@@ -477,12 +481,13 @@
     network::mojom::ReferrerPolicy response_referrer_policy,
     mojom::IPAddressSpace response_address_space,
     const Vector<CSPHeaderAndType> response_csp_headers,
+    const Vector<String>* response_origin_trial_tokens,
     const String& source_code,
     std::unique_ptr<Vector<uint8_t>> cached_meta_data,
     const v8_inspector::V8StackTraceId& stack_id) {
   // Step 4.5-4.11 are implemented in Initialize().
   Initialize(response_url, response_referrer_policy, response_address_space,
-             response_csp_headers);
+             response_csp_headers, response_origin_trial_tokens);
 
   // Step 4.12. "Let evaluationStatus be the result of running the classic
   // script script if script is a classic script, otherwise, the result of
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
index 7bef808..7cc9df7 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
@@ -83,11 +83,11 @@
   bool ShouldInstallV8Extensions() const final;
 
   // Implements WorkerGlobalScope:
-  void Initialize(
-      const KURL& response_url,
-      network::mojom::ReferrerPolicy response_referrer_policy,
-      mojom::IPAddressSpace response_address_space,
-      const Vector<CSPHeaderAndType>& response_csp_headers) override;
+  void Initialize(const KURL& response_url,
+                  network::mojom::ReferrerPolicy response_referrer_policy,
+                  mojom::IPAddressSpace response_address_space,
+                  const Vector<CSPHeaderAndType>& response_csp_headers,
+                  const Vector<String>* response_origin_trial_tokens) override;
   // Fetches and runs the top-level classic worker script for the 'new' or
   // 'update' service worker cases.
   void FetchAndRunClassicScript(
@@ -206,9 +206,10 @@
 
   // https://w3c.github.io/ServiceWorker/#run-service-worker-algorithm
   void RunClassicScript(const KURL& response_url,
-                        network::mojom::ReferrerPolicy,
+                        network::mojom::ReferrerPolicy response_referrer_policy,
                         mojom::IPAddressSpace response_address_space,
-                        const Vector<CSPHeaderAndType>,
+                        const Vector<CSPHeaderAndType> response_csp_headers,
+                        const Vector<String>* response_origin_trial_tokens,
                         const String& source_code,
                         std::unique_ptr<Vector<uint8_t>> cached_meta_data,
                         const v8_inspector::V8StackTraceId&);
diff --git a/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.cc b/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.cc
index 60ae944..ceae9d6 100644
--- a/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.cc
@@ -14,16 +14,13 @@
 XRBoundedReferenceSpace::XRBoundedReferenceSpace(XRSession* session)
     : XRReferenceSpace(session) {}
 
+XRBoundedReferenceSpace::XRBoundedReferenceSpace(
+    XRSession* session,
+    XRRigidTransform* origin_offset)
+    : XRReferenceSpace(session, origin_offset) {}
+
 XRBoundedReferenceSpace::~XRBoundedReferenceSpace() = default;
 
-void XRBoundedReferenceSpace::setOriginOffset(XRRigidTransform* transform) {
-  XRReferenceSpace::setOriginOffset(transform);
-
-  // Force a bounds update.
-  stage_parameters_id_ = 0;
-  EnsureUpdated();
-}
-
 // No default pose for bounded reference spaces.
 std::unique_ptr<TransformationMatrix> XRBoundedReferenceSpace::DefaultPose() {
   return nullptr;
@@ -51,8 +48,7 @@
     // In order to ensure that the bounds continue to line up with the user's
     // physical environment we need to transform by the inverse of the
     // originOffset.
-    TransformationMatrix bounds_transform =
-        originOffset()->InverseTransformMatrix();
+    TransformationMatrix bounds_transform = InverseOriginOffsetMatrix();
 
     if (display_info->stageParameters->bounds) {
       bounds_geometry_.clear();
@@ -126,4 +122,10 @@
   DispatchEvent(*XRReferenceSpaceEvent::Create(event_type_names::kReset, this));
 }
 
+XRBoundedReferenceSpace* XRBoundedReferenceSpace::cloneWithOriginOffset(
+    XRRigidTransform* origin_offset) {
+  return MakeGarbageCollected<XRBoundedReferenceSpace>(this->session(),
+                                                       origin_offset);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.h b/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.h
index e5bfd7a..61b1170 100644
--- a/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.h
@@ -15,15 +15,14 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  XRBoundedReferenceSpace(XRSession*);
+  explicit XRBoundedReferenceSpace(XRSession*);
+  XRBoundedReferenceSpace(XRSession*, XRRigidTransform*);
   ~XRBoundedReferenceSpace() override;
 
   std::unique_ptr<TransformationMatrix> DefaultPose() override;
   std::unique_ptr<TransformationMatrix> TransformBasePose(
       const TransformationMatrix& base_pose) override;
 
-  void setOriginOffset(XRRigidTransform*) override;
-
   HeapVector<Member<DOMPointReadOnly>> boundsGeometry();
 
   void Trace(blink::Visitor*) override;
@@ -31,6 +30,9 @@
   void OnReset() override;
 
  private:
+  XRBoundedReferenceSpace* cloneWithOriginOffset(
+      XRRigidTransform* origin_offset) override;
+
   void EnsureUpdated();
 
   HeapVector<Member<DOMPointReadOnly>> bounds_geometry_;
diff --git a/third_party/blink/renderer/modules/xr/xr_reference_space.cc b/third_party/blink/renderer/modules/xr/xr_reference_space.cc
index 42bf378..0d26aced 100644
--- a/third_party/blink/renderer/modules/xr/xr_reference_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_reference_space.cc
@@ -13,9 +13,13 @@
 
 // origin offset starts as identity transform
 XRReferenceSpace::XRReferenceSpace(XRSession* session)
-    : XRSpace(session),
-      origin_offset_(MakeGarbageCollected<XRRigidTransform>(nullptr, nullptr)) {
-}
+    : XRReferenceSpace(
+          session,
+          MakeGarbageCollected<XRRigidTransform>(nullptr, nullptr)) {}
+
+XRReferenceSpace::XRReferenceSpace(XRSession* session,
+                                   XRRigidTransform* origin_offset)
+    : XRSpace(session), origin_offset_(origin_offset) {}
 
 XRReferenceSpace::~XRReferenceSpace() = default;
 
@@ -63,14 +67,28 @@
   return transform_matrix;
 }
 
-void XRReferenceSpace::setOriginOffset(XRRigidTransform* transform) {
-  origin_offset_ = transform;
+TransformationMatrix XRReferenceSpace::OriginOffsetMatrix() {
+  return origin_offset_->TransformMatrix();
 }
 
 TransformationMatrix XRReferenceSpace::InverseOriginOffsetMatrix() {
   return origin_offset_->InverseTransformMatrix();
 }
 
+XRReferenceSpace* XRReferenceSpace::getOffsetReferenceSpace(
+    XRRigidTransform* additional_offset) {
+  auto matrix =
+      OriginOffsetMatrix().Multiply(additional_offset->TransformMatrix());
+
+  auto* result_transform = MakeGarbageCollected<XRRigidTransform>(matrix);
+  return cloneWithOriginOffset(result_transform);
+}
+
+XRReferenceSpace* XRReferenceSpace::cloneWithOriginOffset(
+    XRRigidTransform* origin_offset) {
+  return MakeGarbageCollected<XRReferenceSpace>(this->session(), origin_offset);
+}
+
 void XRReferenceSpace::Trace(blink::Visitor* visitor) {
   visitor->Trace(origin_offset_);
   XRSpace::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/xr/xr_reference_space.h b/third_party/blink/renderer/modules/xr/xr_reference_space.h
index de7b49b..078915f9 100644
--- a/third_party/blink/renderer/modules/xr/xr_reference_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_reference_space.h
@@ -19,6 +19,7 @@
 
  public:
   explicit XRReferenceSpace(XRSession*);
+  XRReferenceSpace(XRSession*, XRRigidTransform*);
   ~XRReferenceSpace() override;
 
   std::unique_ptr<TransformationMatrix> DefaultPose() override;
@@ -30,10 +31,11 @@
 
   std::unique_ptr<TransformationMatrix> GetTransformToMojoSpace() override;
 
-  XRRigidTransform* originOffset() const { return origin_offset_; }
-  virtual void setOriginOffset(XRRigidTransform*);
+  TransformationMatrix OriginOffsetMatrix() override;
   TransformationMatrix InverseOriginOffsetMatrix() override;
 
+  XRReferenceSpace* getOffsetReferenceSpace(XRRigidTransform* transform);
+
   DEFINE_ATTRIBUTE_EVENT_LISTENER(reset, kReset)
 
   void Trace(blink::Visitor*) override;
@@ -41,6 +43,9 @@
   virtual void OnReset();
 
  private:
+  virtual XRReferenceSpace* cloneWithOriginOffset(
+      XRRigidTransform* origin_offset);
+
   Member<XRRigidTransform> origin_offset_;
 };
 
diff --git a/third_party/blink/renderer/modules/xr/xr_reference_space.idl b/third_party/blink/renderer/modules/xr/xr_reference_space.idl
index 037a4c7..aa3a3b8 100644
--- a/third_party/blink/renderer/modules/xr/xr_reference_space.idl
+++ b/third_party/blink/renderer/modules/xr/xr_reference_space.idl
@@ -16,6 +16,6 @@
     Exposed=Window,
     RuntimeEnabled=WebXR
 ] interface XRReferenceSpace : XRSpace {
-  attribute XRRigidTransform originOffset;
   attribute EventHandler onreset;
+  XRReferenceSpace getOffsetReferenceSpace(XRRigidTransform originOffset);
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_space.cc b/third_party/blink/renderer/modules/xr/xr_space.cc
index 33de78b..2655d67 100644
--- a/third_party/blink/renderer/modules/xr/xr_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_space.cc
@@ -37,6 +37,11 @@
   return nullptr;
 }
 
+TransformationMatrix XRSpace::OriginOffsetMatrix() {
+  TransformationMatrix identity;
+  return identity;
+}
+
 TransformationMatrix XRSpace::InverseOriginOffsetMatrix() {
   TransformationMatrix identity;
   return identity;
diff --git a/third_party/blink/renderer/modules/xr/xr_space.h b/third_party/blink/renderer/modules/xr/xr_space.h
index 772c6c1..d7a4e5de 100644
--- a/third_party/blink/renderer/modules/xr/xr_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_space.h
@@ -51,6 +51,7 @@
   ExecutionContext* GetExecutionContext() const override;
   const AtomicString& InterfaceName() const override;
 
+  virtual TransformationMatrix OriginOffsetMatrix();
   virtual TransformationMatrix InverseOriginOffsetMatrix();
 
   void Trace(blink::Visitor*) override;
diff --git a/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.cc b/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.cc
index 1ee926e3..a9d98b8 100644
--- a/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.cc
@@ -17,6 +17,12 @@
                                                        Subtype subtype)
     : XRReferenceSpace(session), subtype_(subtype) {}
 
+XRStationaryReferenceSpace::XRStationaryReferenceSpace(
+    XRSession* session,
+    XRRigidTransform* transform,
+    Subtype subtype)
+    : XRReferenceSpace(session, transform), subtype_(subtype) {}
+
 XRStationaryReferenceSpace::~XRStationaryReferenceSpace() = default;
 
 void XRStationaryReferenceSpace::UpdateFloorLevelTransform() {
@@ -67,8 +73,8 @@
 
       // Apply the floor-level transform to the base pose.
       if (floor_level_transform_) {
-        std::unique_ptr<TransformationMatrix> pose(
-            std::make_unique<TransformationMatrix>(*floor_level_transform_));
+        auto pose =
+            std::make_unique<TransformationMatrix>(*floor_level_transform_);
         pose->Multiply(base_pose);
         return pose;
       }
@@ -77,8 +83,7 @@
       // 'position-disabled' poses must not contain any translation components,
       // and as a result the space the base pose is originally in doesn't matter
       // much. Strip out translation component and return.
-      std::unique_ptr<TransformationMatrix> pose(
-          std::make_unique<TransformationMatrix>(base_pose));
+      auto pose = std::make_unique<TransformationMatrix>(base_pose);
       pose->SetM41(0.0);
       pose->SetM42(0.0);
       pose->SetM43(0.0);
@@ -98,8 +103,7 @@
     const TransformationMatrix& base_pose) {
   switch (subtype_) {
     case kSubtypePositionDisabled: {
-      std::unique_ptr<TransformationMatrix> head_model_pose(
-          TransformBasePose(base_pose));
+      auto head_model_pose = TransformBasePose(base_pose);
 
       // Get the positional delta between the base pose and the head model pose.
       float dx = head_model_pose->M41() - base_pose.M41();
@@ -108,8 +112,7 @@
 
       // Translate the controller by the same delta so that it shows up in the
       // right relative position.
-      std::unique_ptr<TransformationMatrix> pose(
-          std::make_unique<TransformationMatrix>(base_input_pose));
+      auto pose = std::make_unique<TransformationMatrix>(base_input_pose);
       pose->SetM41(pose->M41() + dx);
       pose->SetM42(pose->M42() + dy);
       pose->SetM43(pose->M43() + dz);
@@ -133,4 +136,10 @@
   DispatchEvent(*XRReferenceSpaceEvent::Create(event_type_names::kReset, this));
 }
 
+XRStationaryReferenceSpace* XRStationaryReferenceSpace::cloneWithOriginOffset(
+    XRRigidTransform* origin_offset) {
+  return MakeGarbageCollected<XRStationaryReferenceSpace>(
+      this->session(), origin_offset, subtype_);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.h b/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.h
index e4cce49..c08fe2ca 100644
--- a/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.h
@@ -21,6 +21,7 @@
   };
 
   XRStationaryReferenceSpace(XRSession*, Subtype);
+  XRStationaryReferenceSpace(XRSession*, XRRigidTransform*, Subtype);
   ~XRStationaryReferenceSpace() override;
 
   std::unique_ptr<TransformationMatrix> DefaultPose() override;
@@ -35,6 +36,9 @@
   void OnReset() override;
 
  private:
+  XRStationaryReferenceSpace* cloneWithOriginOffset(
+      XRRigidTransform* origin_offset) override;
+
   void UpdateFloorLevelTransform();
 
   unsigned int display_info_id_ = 0;
diff --git a/third_party/blink/renderer/modules/xr/xr_unbounded_reference_space.cc b/third_party/blink/renderer/modules/xr/xr_unbounded_reference_space.cc
index a93dfe38..14df77bf 100644
--- a/third_party/blink/renderer/modules/xr/xr_unbounded_reference_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_unbounded_reference_space.cc
@@ -13,6 +13,11 @@
 XRUnboundedReferenceSpace::XRUnboundedReferenceSpace(XRSession* session)
     : XRReferenceSpace(session) {}
 
+XRUnboundedReferenceSpace::XRUnboundedReferenceSpace(
+    XRSession* session,
+    XRRigidTransform* origin_offset)
+    : XRReferenceSpace(session, origin_offset) {}
+
 XRUnboundedReferenceSpace::~XRUnboundedReferenceSpace() = default;
 
 // No default pose for unbounded reference spaces.
@@ -36,4 +41,10 @@
   DispatchEvent(*XRReferenceSpaceEvent::Create(event_type_names::kReset, this));
 }
 
+XRUnboundedReferenceSpace* XRUnboundedReferenceSpace::cloneWithOriginOffset(
+    XRRigidTransform* origin_offset) {
+  return MakeGarbageCollected<XRUnboundedReferenceSpace>(this->session(),
+                                                         origin_offset);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_unbounded_reference_space.h b/third_party/blink/renderer/modules/xr/xr_unbounded_reference_space.h
index 6c9768b..42f08ea 100644
--- a/third_party/blink/renderer/modules/xr/xr_unbounded_reference_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_unbounded_reference_space.h
@@ -14,7 +14,8 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  XRUnboundedReferenceSpace(XRSession*);
+  explicit XRUnboundedReferenceSpace(XRSession*);
+  XRUnboundedReferenceSpace(XRSession*, XRRigidTransform*);
   ~XRUnboundedReferenceSpace() override;
 
   std::unique_ptr<TransformationMatrix> DefaultPose() override;
@@ -26,6 +27,9 @@
   void OnReset() override;
 
  private:
+  XRUnboundedReferenceSpace* cloneWithOriginOffset(
+      XRRigidTransform* origin_offset) override;
+
   std::unique_ptr<TransformationMatrix> pose_transform_;
 };
 
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index 44ace22..6fc8fc49 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -188,10 +188,6 @@
   RuntimeEnabledFeatures::SetForceTallerSelectPopupEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnableFormControlsRefresh(bool enable) {
-  RuntimeEnabledFeatures::SetFormControlsRefreshEnabled(enable);
-}
-
 void WebRuntimeFeatures::EnableGenericSensor(bool enable) {
   RuntimeEnabledFeatures::SetSensorEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index 942533b..b25a88e 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -462,9 +462,7 @@
     property_trees_.element_id_to_transform_node_index[compositor_element_id] =
         id;
     compositor_node.element_id = compositor_element_id;
-
-    if (transform_node.RequiresCompositingForAnimation())
-      CollectAnimationElementId(compositor_element_id);
+    CollectAnimationElementId(compositor_element_id);
   }
 
   // If this transform is a scroll offset translation, create the associated
@@ -945,7 +943,6 @@
   if (element_namespace == CompositorElementIdNamespace::kPrimaryTransform ||
       element_namespace == CompositorElementIdNamespace::kPrimaryEffect ||
       element_namespace == CompositorElementIdNamespace::kEffectFilter) {
-    DCHECK_EQ(0u, animation_element_ids_.count(element_id));
     animation_element_ids_.insert(element_id);
   }
 }
@@ -994,9 +991,7 @@
         compositor_element_id));
     property_trees_.element_id_to_effect_node_index[compositor_element_id] =
         effect_node.id;
-
-    if (next_effect.RequiresCompositingForAnimation())
-      CollectAnimationElementId(compositor_element_id);
+    CollectAnimationElementId(compositor_element_id);
   }
 
   effect_stack_.emplace_back(current_);
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h
index 4b07732..8f2ae61 100644
--- a/third_party/blink/renderer/platform/heap/heap.h
+++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -515,7 +515,8 @@
   DISALLOW_COPY_AND_ASSIGN(GarbageCollected);
 };
 
-// Constructs an instance of T, which is a garbage collected type.
+// Default MakeGarbageCollected: Constructs an instance of T, which is a garbage
+// collected type.
 template <typename T, typename... Args>
 T* MakeGarbageCollected(Args&&... args) {
   static_assert(WTF::IsGarbageCollectedType<T>::value,
@@ -529,6 +530,28 @@
   return object;
 }
 
+// Used for passing custom sizes to MakeGarbageCollected.
+struct AdditionalBytes {
+  explicit AdditionalBytes(size_t bytes) : value(bytes) {}
+  const size_t value;
+};
+
+// Constructs an instance of T, which is a garbage collected type. This special
+// version takes size which enables constructing inline objects.
+template <typename T, typename... Args>
+T* MakeGarbageCollected(AdditionalBytes additional_bytes, Args&&... args) {
+  static_assert(WTF::IsGarbageCollectedType<T>::value,
+                "T needs to be a garbage collected object");
+  void* memory = T::AllocateObject(sizeof(T) + additional_bytes.value,
+                                   IsEagerlyFinalizedType<T>::value);
+  HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory);
+  header->MarkIsInConstruction();
+  // Placement new as regular operator new() is deleted.
+  T* object = ::new (memory) T(std::forward<Args>(args)...);
+  header->UnmarkIsInConstruction();
+  return object;
+}
+
 // Assigning class types to their arenas.
 //
 // We use sized arenas for most 'normal' objects to improve memory locality.
diff --git a/third_party/blink/renderer/platform/loader/BUILD.gn b/third_party/blink/renderer/platform/loader/BUILD.gn
index 9206af1..29698d2 100644
--- a/third_party/blink/renderer/platform/loader/BUILD.gn
+++ b/third_party/blink/renderer/platform/loader/BUILD.gn
@@ -64,6 +64,7 @@
     "fetch/resource_error.h",
     "fetch/resource_fetcher.cc",
     "fetch/resource_fetcher.h",
+    "fetch/resource_fetcher_properties.cc",
     "fetch/resource_fetcher_properties.h",
     "fetch/resource_finish_observer.h",
     "fetch/resource_load_info.h",
@@ -141,6 +142,7 @@
     "fetch/memory_cache_correctness_test.cc",
     "fetch/memory_cache_test.cc",
     "fetch/raw_resource_test.cc",
+    "fetch/resource_fetcher_properties_test.cc",
     "fetch/resource_fetcher_test.cc",
     "fetch/resource_load_scheduler_test.cc",
     "fetch/resource_loader_defer_loading_test.cc",
diff --git a/third_party/blink/renderer/platform/loader/allowed_by_nosniff.cc b/third_party/blink/renderer/platform/loader/allowed_by_nosniff.cc
index dd93ca30..4a554a39 100644
--- a/third_party/blink/renderer/platform/loader/allowed_by_nosniff.cc
+++ b/third_party/blink/renderer/platform/loader/allowed_by_nosniff.cc
@@ -5,13 +5,10 @@
 #include "third_party/blink/renderer/platform/loader/allowed_by_nosniff.h"
 
 #include "third_party/blink/renderer/platform/loader/fetch/console_logger.h"
-#include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
 #include "third_party/blink/renderer/platform/network/http_names.h"
 #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
-#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
 
@@ -161,10 +158,8 @@
   // Check for certain non-executable MIME types.
   // See:
   // https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-mime-type?
-  bool same_origin = context.GetResourceFetcherProperties()
-                         .GetFetchClientSettingsObject()
-                         .GetSecurityOrigin()
-                         ->CanRequest(response.CurrentRequestUrl());
+  const bool same_origin =
+      response.GetType() == network::mojom::FetchResponseType::kBasic;
 
   // For any MIME type, we can do three things: accept/reject it, print a
   // warning into the console, and count it using a use counter.
diff --git a/third_party/blink/renderer/platform/loader/allowed_by_nosniff_test.cc b/third_party/blink/renderer/platform/loader/allowed_by_nosniff_test.cc
index e8e48d4..a26edc1 100644
--- a/third_party/blink/renderer/platform/loader/allowed_by_nosniff_test.cc
+++ b/third_party/blink/renderer/platform/loader/allowed_by_nosniff_test.cc
@@ -106,9 +106,9 @@
         SecurityOrigin::Create(url));
     auto* context = CountUsageMockFetchContext::Create();
     // Bind |properties| to |context| through a ResourceFetcher.
-    MakeGarbageCollected<ResourceFetcher>(
-        ResourceFetcherInit(*properties, context, CreateTaskRunner(),
-                            MakeGarbageCollected<TestLoaderFactory>()));
+    MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+        properties->MakeDetachable(), context, CreateTaskRunner(),
+        MakeGarbageCollected<TestLoaderFactory>()));
     Persistent<MockConsoleLogger> logger =
         MakeGarbageCollected<MockConsoleLogger>();
     ResourceResponse response(url);
@@ -133,51 +133,58 @@
 }
 
 TEST_F(AllowedByNosniffTest, Counters) {
+  constexpr auto kBasic = network::mojom::FetchResponseType::kBasic;
+  constexpr auto kOpaque = network::mojom::FetchResponseType::kOpaque;
+  constexpr auto kCors = network::mojom::FetchResponseType::kCors;
   const char* bla = "https://bla.com";
   const char* blubb = "https://blubb.com";
   struct {
     const char* url;
     const char* origin;
     const char* mimetype;
+    network::mojom::FetchResponseType response_type;
     WebFeature expected;
   } data[] = {
       // Test same- vs cross-origin cases.
-      {bla, "", "text/plain", WebFeature::kCrossOriginTextScript},
-      {bla, "", "text/plain", WebFeature::kCrossOriginTextPlain},
-      {bla, blubb, "text/plain", WebFeature::kCrossOriginTextScript},
-      {bla, blubb, "text/plain", WebFeature::kCrossOriginTextPlain},
-      {bla, bla, "text/plain", WebFeature::kSameOriginTextScript},
-      {bla, bla, "text/plain", WebFeature::kSameOriginTextPlain},
+      {bla, "", "text/plain", kOpaque, WebFeature::kCrossOriginTextScript},
+      {bla, "", "text/plain", kCors, WebFeature::kCrossOriginTextPlain},
+      {bla, blubb, "text/plain", kCors, WebFeature::kCrossOriginTextScript},
+      {bla, blubb, "text/plain", kOpaque, WebFeature::kCrossOriginTextPlain},
+      {bla, bla, "text/plain", kBasic, WebFeature::kSameOriginTextScript},
+      {bla, bla, "text/plain", kBasic, WebFeature::kSameOriginTextPlain},
 
       // Test mime type and subtype handling.
-      {bla, bla, "text/xml", WebFeature::kSameOriginTextScript},
-      {bla, bla, "text/xml", WebFeature::kSameOriginTextXml},
+      {bla, bla, "text/xml", kBasic, WebFeature::kSameOriginTextScript},
+      {bla, bla, "text/xml", kBasic, WebFeature::kSameOriginTextXml},
 
       // Test mime types from crbug.com/765544, with random cross/same site
       // origins.
-      {bla, bla, "text/plain", WebFeature::kSameOriginTextPlain},
-      {bla, blubb, "text/xml", WebFeature::kCrossOriginTextXml},
-      {blubb, blubb, "application/octet-stream",
+      {bla, bla, "text/plain", kBasic, WebFeature::kSameOriginTextPlain},
+      {bla, bla, "text/xml", kOpaque, WebFeature::kCrossOriginTextXml},
+      {blubb, blubb, "application/octet-stream", kBasic,
        WebFeature::kSameOriginApplicationOctetStream},
-      {blubb, bla, "application/xml", WebFeature::kCrossOriginApplicationXml},
-      {bla, bla, "text/html", WebFeature::kSameOriginTextHtml},
+      {blubb, blubb, "application/xml", kCors,
+       WebFeature::kCrossOriginApplicationXml},
+      {bla, bla, "text/html", kBasic, WebFeature::kSameOriginTextHtml},
   };
 
   for (auto& testcase : data) {
-    SCOPED_TRACE(testing::Message() << "\n  url: " << testcase.url
-                                    << "\n  origin: " << testcase.origin
-                                    << "\n  mime type: " << testcase.mimetype
-                                    << "\n  webfeature: " << testcase.expected);
+    SCOPED_TRACE(testing::Message()
+                 << "\n  url: " << testcase.url << "\n  origin: "
+                 << testcase.origin << "\n  mime type: " << testcase.mimetype
+                 << "\n response type: " << testcase.response_type
+                 << "\n  webfeature: " << testcase.expected);
     auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(
         SecurityOrigin::Create(KURL(testcase.origin)));
     auto* context = CountUsageMockFetchContext::Create();
     // Bind |properties| to |context| through a ResourceFetcher.
-    MakeGarbageCollected<ResourceFetcher>(
-        ResourceFetcherInit(*properties, context, CreateTaskRunner(),
-                            MakeGarbageCollected<TestLoaderFactory>()));
+    MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+        properties->MakeDetachable(), context, CreateTaskRunner(),
+        MakeGarbageCollected<TestLoaderFactory>()));
     Persistent<MockConsoleLogger> logger =
         MakeGarbageCollected<MockConsoleLogger>();
     ResourceResponse response(KURL(testcase.url));
+    response.SetType(testcase.response_type);
     response.SetHttpHeaderField("Content-Type", testcase.mimetype);
 
     EXPECT_CALL(*context, CountUsage(testcase.expected));
@@ -218,9 +225,9 @@
     auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
     auto* context = CountUsageMockFetchContext::Create();
     // Bind |properties| to |context| through a ResourceFetcher.
-    MakeGarbageCollected<ResourceFetcher>(
-        ResourceFetcherInit(*properties, context, CreateTaskRunner(),
-                            MakeGarbageCollected<TestLoaderFactory>()));
+    MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+        properties->MakeDetachable(), context, CreateTaskRunner(),
+        MakeGarbageCollected<TestLoaderFactory>()));
     Persistent<MockConsoleLogger> logger =
         MakeGarbageCollected<MockConsoleLogger>();
     EXPECT_CALL(*logger, AddConsoleMessage(_, _, _))
diff --git a/third_party/blink/renderer/platform/loader/fetch/console_logger.h b/third_party/blink/renderer/platform/loader/fetch/console_logger.h
index 92df929..33b9344c 100644
--- a/third_party/blink/renderer/platform/loader/fetch/console_logger.h
+++ b/third_party/blink/renderer/platform/loader/fetch/console_logger.h
@@ -13,6 +13,9 @@
 namespace blink {
 
 // A pure virtual interface for console logging.
+// Retaining an instance of ConsoleLogger may be dangerous because after the
+// associated fetcher is detached it leads to a leak. Use
+// DetachableConsoleLogger in such a case.
 class PLATFORM_EXPORT ConsoleLogger : public GarbageCollectedMixin {
  public:
   ConsoleLogger() = default;
@@ -23,18 +26,37 @@
                                  const String& message) = 0;
 };
 
-class PLATFORM_EXPORT NullConsoleLogger final
-    : public GarbageCollected<NullConsoleLogger>,
+// A ConsoleLogger subclass which has Detach() method. An instance of
+// DetachableConsoleLogger can be kept even when the associated fetcher is
+// detached.
+class PLATFORM_EXPORT DetachableConsoleLogger final
+    : public GarbageCollected<DetachableConsoleLogger>,
       public ConsoleLogger {
-  USING_GARBAGE_COLLECTED_MIXIN(NullConsoleLogger);
+  USING_GARBAGE_COLLECTED_MIXIN(DetachableConsoleLogger);
 
  public:
-  NullConsoleLogger() = default;
-  ~NullConsoleLogger() override = default;
+  DetachableConsoleLogger() : DetachableConsoleLogger(nullptr) {}
+  DetachableConsoleLogger(ConsoleLogger* logger) : logger_(logger) {}
 
-  void AddConsoleMessage(mojom::ConsoleMessageSource,
-                         mojom::ConsoleMessageLevel,
-                         const String&) override {}
+  // Detaches |logger_|. After this function is called AddConsoleMessage will
+  // be no-op.
+  void Detach() { logger_ = nullptr; }
+
+  void AddConsoleMessage(mojom::ConsoleMessageSource source,
+                         mojom::ConsoleMessageLevel level,
+                         const String& message) override {
+    if (!logger_) {
+      return;
+    }
+    logger_->AddConsoleMessage(source, level, message);
+  }
+
+  void Trace(Visitor* visitor) override {
+    visitor->Trace(logger_);
+    ConsoleLogger::Trace(visitor);
+  }
+
+  Member<ConsoleLogger> logger_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc b/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
index 1ef3572..46682ae 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
@@ -30,9 +30,7 @@
 
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
 
-#include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
 #include "third_party/blink/renderer/platform/platform_probe_sink.h"
 #include "third_party/blink/renderer/platform/probe/platform_trace_events_agent.h"
 
@@ -62,7 +60,6 @@
 
 void FetchContext::Trace(blink::Visitor* visitor) {
   visitor->Trace(platform_probe_sink_);
-  visitor->Trace(resource_fetcher_properties_);
 }
 
 void FetchContext::AddAdditionalRequestHeaders(ResourceRequest&) {}
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_context.h b/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
index 54daece..bb6c6528 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
@@ -60,7 +60,6 @@
 class ClientHintsPreferences;
 class KURL;
 class PlatformProbeSink;
-class ResourceFetcherProperties;
 class ResourceTimingInfo;
 class WebScopedVirtualTimePauser;
 
@@ -80,23 +79,10 @@
 
   virtual ~FetchContext() = default;
 
-  // Called from a ResourceFetcher constructor. This is called only once.
-  // TODO(yhirano): Consider removing this.
-  void Init(const ResourceFetcherProperties& properties) {
-    DCHECK(!resource_fetcher_properties_);
-    resource_fetcher_properties_ = &properties;
-  }
-
   virtual void Trace(blink::Visitor*);
 
   virtual void AddAdditionalRequestHeaders(ResourceRequest&);
 
-  // This function must not be called before |Init| is called.
-  const ResourceFetcherProperties& GetResourceFetcherProperties() const {
-    DCHECK(resource_fetcher_properties_);
-    return *resource_fetcher_properties_;
-  }
-
   // Returns the cache policy for the resource. ResourceRequest is not passed as
   // a const reference as a header needs to be added for doc.write blocking
   // intervention.
@@ -163,9 +149,7 @@
   // with "keepalive" specified).
   // Returns a "detached" fetch context which cannot be null.
   virtual FetchContext* Detach() {
-    DCHECK(resource_fetcher_properties_);
     auto* context = &NullInstance();
-    context->Init(*resource_fetcher_properties_);
     return context;
   }
 
@@ -178,7 +162,6 @@
 
  private:
   Member<PlatformProbeSink> platform_probe_sink_;
-  Member<const ResourceFetcherProperties> resource_fetcher_properties_;
 
   DISALLOW_COPY_AND_ASSIGN(FetchContext);
 };
diff --git a/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc b/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
index 44c5a5c..14e59ed 100644
--- a/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
@@ -120,9 +120,10 @@
     auto* properties =
         MakeGarbageCollected<TestResourceFetcherProperties>(security_origin_);
     properties->SetShouldBlockLoadingSubResource(true);
-    fetcher_ = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
-        *properties, context, base::MakeRefCounted<scheduler::FakeTaskRunner>(),
-        MakeGarbageCollected<TestLoaderFactory>()));
+    fetcher_ = MakeGarbageCollected<ResourceFetcher>(
+        ResourceFetcherInit(properties->MakeDetachable(), context,
+                            base::MakeRefCounted<scheduler::FakeTaskRunner>(),
+                            MakeGarbageCollected<TestLoaderFactory>()));
   }
   void TearDown() override {
     GetMemoryCache()->EvictResources();
diff --git a/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc b/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
index 18dcc3b..ea53c42 100644
--- a/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
@@ -103,7 +103,7 @@
         MakeGarbageCollected<MemoryCache>(platform_->test_task_runner()));
     auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
     fetcher_ = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
-        *properties, MakeGarbageCollected<MockFetchContext>(),
+        properties->MakeDetachable(), MakeGarbageCollected<MockFetchContext>(),
         base::MakeRefCounted<scheduler::FakeTaskRunner>(),
         MakeGarbageCollected<TestLoaderFactory>()));
   }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index 788045c..761a021 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -346,7 +346,7 @@
 }  // namespace
 
 ResourceFetcherInit::ResourceFetcherInit(
-    const ResourceFetcherProperties& properties,
+    DetachableResourceFetcherProperties& properties,
     FetchContext* context,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     ResourceFetcher::LoaderFactory* loader_factory)
@@ -401,121 +401,6 @@
   return mojom::RequestContextType::SUBRESOURCE;
 }
 
-class ResourceFetcher::DetachableConsoleLogger final
-    : public GarbageCollectedFinalized<DetachableConsoleLogger>,
-      public ConsoleLogger {
-  USING_GARBAGE_COLLECTED_MIXIN(DetachableConsoleLogger);
-
- public:
-  DetachableConsoleLogger(ConsoleLogger& logger) : logger_(logger) {}
-
-  void Detach() { logger_ = nullptr; }
-
-  // ConsoleLogger implementation.
-  void AddConsoleMessage(mojom::ConsoleMessageSource source,
-                         mojom::ConsoleMessageLevel level,
-                         const String& message) override {
-    if (logger_) {
-      logger_->AddConsoleMessage(source, level, message);
-    }
-  }
-  void Trace(Visitor* visitor) override {
-    visitor->Trace(logger_);
-    ConsoleLogger::Trace(visitor);
-  }
-
- private:
-  Member<ConsoleLogger> logger_;
-};
-
-// A delegating ResourceFetcherProperties subclass which can be from the
-// original ResourceFetcherProperties.
-class ResourceFetcher::DetachableProperties final
-    : public ResourceFetcherProperties {
- public:
-  explicit DetachableProperties(const ResourceFetcherProperties& properties)
-      : properties_(properties) {}
-  ~DetachableProperties() override = default;
-
-  void Detach() {
-    if (!properties_) {
-      // Already detached.
-      return;
-    }
-
-    fetch_client_settings_object_ =
-        MakeGarbageCollected<FetchClientSettingsObjectSnapshot>(
-            properties_->GetFetchClientSettingsObject());
-    is_main_frame_ = properties_->IsMainFrame();
-    paused_ = properties_->IsPaused();
-    load_complete_ = properties_->IsLoadComplete();
-    is_subframe_deprioritization_enabled_ =
-        properties_->IsSubframeDeprioritizationEnabled();
-
-    properties_ = nullptr;
-  }
-
-  void Trace(Visitor* visitor) override {
-    visitor->Trace(properties_);
-    visitor->Trace(fetch_client_settings_object_);
-    ResourceFetcherProperties::Trace(visitor);
-  }
-
-  // ResourceFetcherProperties implementation
-  // Add a test in resource_fetcher_test.cc when you change behaviors.
-  const FetchClientSettingsObject& GetFetchClientSettingsObject()
-      const override {
-    return properties_ ? properties_->GetFetchClientSettingsObject()
-                       : *fetch_client_settings_object_;
-  }
-  bool IsMainFrame() const override {
-    return properties_ ? properties_->IsMainFrame() : is_main_frame_;
-  }
-  ControllerServiceWorkerMode GetControllerServiceWorkerMode() const override {
-    return properties_ ? properties_->GetControllerServiceWorkerMode()
-                       : ControllerServiceWorkerMode::kNoController;
-  }
-  int64_t ServiceWorkerId() const override {
-    // When detached, GetControllerServiceWorkerMode returns kNoController, so
-    // this function must not be called.
-    DCHECK(properties_);
-    return properties_->ServiceWorkerId();
-  }
-  bool IsPaused() const override {
-    return properties_ ? properties_->IsPaused() : paused_;
-  }
-  bool IsDetached() const override {
-    return properties_ ? properties_->IsDetached() : true;
-  }
-  bool IsLoadComplete() const override {
-    return properties_ ? properties_->IsLoadComplete() : load_complete_;
-  }
-  bool ShouldBlockLoadingSubResource() const override {
-    // Returns true when detached in order to preserve the existing behavior.
-    return properties_ ? properties_->ShouldBlockLoadingSubResource() : true;
-  }
-  bool IsSubframeDeprioritizationEnabled() const override {
-    return properties_ ? properties_->IsSubframeDeprioritizationEnabled()
-                       : is_subframe_deprioritization_enabled_;
-  }
-
-  scheduler::FrameStatus GetFrameStatus() const override {
-    return properties_ ? properties_->GetFrameStatus()
-                       : scheduler::FrameStatus::kNone;
-  }
-
- private:
-  // |properties_| is null if and only if detached.
-  Member<const ResourceFetcherProperties> properties_;
-
-  // The following members are used when detached.
-  Member<const FetchClientSettingsObject> fetch_client_settings_object_;
-  bool is_main_frame_ = false;
-  bool paused_ = false;
-  bool load_complete_ = false;
-  bool is_subframe_deprioritization_enabled_ = false;
-};
-
 ResourceLoadPriority ResourceFetcher::ComputeLoadPriority(
     ResourceType type,
     const ResourceRequest& resource_request,
@@ -614,13 +499,12 @@
 }
 
 ResourceFetcher::ResourceFetcher(const ResourceFetcherInit& init)
-    : properties_(
-          *MakeGarbageCollected<DetachableProperties>(*init.properties)),
+    : properties_(*init.properties),
       context_(init.context),
       task_runner_(init.task_runner),
-      console_logger_(MakeGarbageCollected<DetachableConsoleLogger>(
-          init.console_logger ? *init.console_logger
-                              : *MakeGarbageCollected<NullConsoleLogger>())),
+      console_logger_(init.console_logger
+                          ? init.console_logger.Get()
+                          : MakeGarbageCollected<DetachableConsoleLogger>()),
       loader_factory_(init.loader_factory),
       scheduler_(MakeGarbageCollected<ResourceLoadScheduler>(
           init.initial_throttling_policy,
@@ -643,17 +527,12 @@
   InstanceCounters::IncrementCounter(InstanceCounters::kResourceFetcherCounter);
   if (IsMainThread())
     MainThreadFetchersSet().insert(this);
-  context_->Init(*properties_);
 }
 
 ResourceFetcher::~ResourceFetcher() {
   InstanceCounters::DecrementCounter(InstanceCounters::kResourceFetcherCounter);
 }
 
-const ResourceFetcherProperties& ResourceFetcher::GetProperties() const {
-  return *properties_;
-}
-
 bool ResourceFetcher::IsDetached() const {
   return properties_->IsDetached();
 }
@@ -1777,10 +1656,6 @@
   }
 }
 
-ConsoleLogger& ResourceFetcher::GetConsoleLogger() {
-  return *console_logger_;
-}
-
 int ResourceFetcher::BlockingRequestCount() const {
   return loaders_.size();
 }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
index 9d8c787..7e3f370 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -50,7 +50,8 @@
 
 enum class ResourceType : uint8_t;
 class CodeCacheLoader;
-class ConsoleLogger;
+class DetachableConsoleLogger;
+class DetachableResourceFetcherProperties;
 class FetchContext;
 class FrameScheduler;
 class MHTMLArchive;
@@ -58,7 +59,6 @@
 class PreflightTimingInfo;
 class Resource;
 class ResourceError;
-class ResourceFetcherProperties;
 class ResourceLoadObserver;
 class ResourceTimingInfo;
 class WebURLLoader;
@@ -108,11 +108,9 @@
   // - This function returns the same object throughout this fetcher's
   //   entire life.
   // - The returned object remains valid after ClearContext() is called.
-  // - This function returns a different object from the corresponding
-  //   argument in the constructor.
-  // - This function should be used rather than the properties given
-  //   to the ResourceFetcher constructor.
-  const ResourceFetcherProperties& GetProperties() const;
+  const DetachableResourceFetcherProperties& GetProperties() const {
+    return *properties_;
+  }
 
   // Returns whether this fetcher is detached from the associated context.
   bool IsDetached() const;
@@ -170,7 +168,7 @@
 
   FetchContext& Context() const;
   void ClearContext();
-  ConsoleLogger& GetConsoleLogger();
+  DetachableConsoleLogger& GetConsoleLogger() { return *console_logger_; }
 
   int BlockingRequestCount() const;
   int NonblockingRequestCount() const;
@@ -268,8 +266,6 @@
 
  private:
   friend class ResourceCacheValidationSuppressor;
-  class DetachableConsoleLogger;
-  class DetachableProperties;
   enum class StopFetchingTarget {
     kExcludingKeepaliveLoaders,
     kIncludingKeepaliveLoaders,
@@ -373,7 +369,7 @@
   void ScheduleStaleRevalidate(Resource* stale_resource);
   void RevalidateStaleResource(Resource* stale_resource);
 
-  Member<DetachableProperties> properties_;
+  Member<DetachableResourceFetcherProperties> properties_;
   Member<ResourceLoadObserver> resource_load_observer_;
   Member<FetchContext> context_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
@@ -455,17 +451,16 @@
  public:
   // |context| and |task_runner| must not be null.
   // |loader_factory| can be null if |properties.IsDetached()| is true.
-  // The given ResourceFetcherProperties is kept until ClearContext() is called.
-  ResourceFetcherInit(const ResourceFetcherProperties& properties,
+  ResourceFetcherInit(DetachableResourceFetcherProperties& properties,
                       FetchContext* context,
                       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
                       ResourceFetcher::LoaderFactory* loader_factory);
 
-  const Member<const ResourceFetcherProperties> properties;
+  const Member<DetachableResourceFetcherProperties> properties;
   const Member<FetchContext> context;
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner;
   const Member<ResourceFetcher::LoaderFactory> loader_factory;
-  Member<ConsoleLogger> console_logger;
+  Member<DetachableConsoleLogger> console_logger;
   ResourceLoadScheduler::ThrottlingPolicy initial_throttling_policy =
       ResourceLoadScheduler::ThrottlingPolicy::kNormal;
   Member<MHTMLArchive> archive;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.cc
new file mode 100644
index 0000000..2ab0d51
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.cc
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
+
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
+
+namespace blink {
+
+void DetachableResourceFetcherProperties::Detach() {
+  if (!properties_) {
+    // Already detached.
+    return;
+  }
+
+  fetch_client_settings_object_ =
+      MakeGarbageCollected<FetchClientSettingsObjectSnapshot>(
+          properties_->GetFetchClientSettingsObject());
+  is_main_frame_ = properties_->IsMainFrame();
+  paused_ = properties_->IsPaused();
+  load_complete_ = properties_->IsLoadComplete();
+  is_subframe_deprioritization_enabled_ =
+      properties_->IsSubframeDeprioritizationEnabled();
+
+  properties_ = nullptr;
+}
+
+void DetachableResourceFetcherProperties::Trace(Visitor* visitor) {
+  visitor->Trace(properties_);
+  visitor->Trace(fetch_client_settings_object_);
+  ResourceFetcherProperties::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h
index a68533ed..dc5ab3d 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h
@@ -24,6 +24,10 @@
 // GetCachePolicy(const ResourceRequest&, ResourceType). Do not put a function
 // with default implementation.
 //
+// Storing a non-null ResourceFetcherProperties in an object that can be valid
+// after the associated ResourceFetcher is detached is dangerous. Use
+// DetachedResourceFetcherProperties below.
+//
 // The distinction between FetchClientSettingsObject and
 // ResourceFetcherProperties is sometimes ambiguous. Put a property in
 // FetchClientSettingsObject when the property is clearly defined in the spec.
@@ -80,6 +84,75 @@
   virtual scheduler::FrameStatus GetFrameStatus() const = 0;
 };
 
+// A delegating ResourceFetcherProperties subclass which can be retained
+// even when the associated ResourceFetcher is detached.
+class PLATFORM_EXPORT DetachableResourceFetcherProperties final
+    : public ResourceFetcherProperties {
+ public:
+  explicit DetachableResourceFetcherProperties(
+      const ResourceFetcherProperties& properties)
+      : properties_(properties) {}
+  ~DetachableResourceFetcherProperties() override = default;
+
+  void Detach();
+
+  void Trace(Visitor* visitor) override;
+
+  // ResourceFetcherProperties implementation
+  // Add a test in resource_fetcher_test.cc when you change behaviors.
+  const FetchClientSettingsObject& GetFetchClientSettingsObject()
+      const override {
+    return properties_ ? properties_->GetFetchClientSettingsObject()
+                       : *fetch_client_settings_object_;
+  }
+  bool IsMainFrame() const override {
+    return properties_ ? properties_->IsMainFrame() : is_main_frame_;
+  }
+  ControllerServiceWorkerMode GetControllerServiceWorkerMode() const override {
+    return properties_ ? properties_->GetControllerServiceWorkerMode()
+                       : ControllerServiceWorkerMode::kNoController;
+  }
+  int64_t ServiceWorkerId() const override {
+    // When detached, GetControllerServiceWorkerMode returns kNoController, so
+    // this function must not be called.
+    DCHECK(properties_);
+    return properties_->ServiceWorkerId();
+  }
+  bool IsPaused() const override {
+    return properties_ ? properties_->IsPaused() : paused_;
+  }
+  bool IsDetached() const override {
+    return properties_ ? properties_->IsDetached() : true;
+  }
+  bool IsLoadComplete() const override {
+    return properties_ ? properties_->IsLoadComplete() : load_complete_;
+  }
+  bool ShouldBlockLoadingSubResource() const override {
+    // Returns true when detached in order to preserve the existing behavior.
+    return properties_ ? properties_->ShouldBlockLoadingSubResource() : true;
+  }
+  bool IsSubframeDeprioritizationEnabled() const override {
+    return properties_ ? properties_->IsSubframeDeprioritizationEnabled()
+                       : is_subframe_deprioritization_enabled_;
+  }
+
+  scheduler::FrameStatus GetFrameStatus() const override {
+    return properties_ ? properties_->GetFrameStatus()
+                       : scheduler::FrameStatus::kNone;
+  }
+
+ private:
+  // |properties_| is null if and only if detached.
+  Member<const ResourceFetcherProperties> properties_;
+
+  // The following members are used when detached.
+  Member<const FetchClientSettingsObject> fetch_client_settings_object_;
+  bool is_main_frame_ = false;
+  bool paused_ = false;
+  bool load_complete_ = false;
+  bool is_subframe_deprioritization_enabled_ = false;
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_FETCHER_PROPERTIES_H_
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties_test.cc
new file mode 100644
index 0000000..a914377
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties_test.cc
@@ -0,0 +1,126 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
+#include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h"
+
+namespace blink {
+
+namespace {
+
+class DetachableResourceFetcherPropertiesTest : public testing::Test {
+ public:
+  const FetchClientSettingsObjectSnapshot& CreateFetchClientSettingsObject(
+      mojom::IPAddressSpace address_space) {
+    return *MakeGarbageCollected<FetchClientSettingsObjectSnapshot>(
+        KURL("https://example.com/foo.html"),
+        KURL("https://example.com/foo.html"),
+        SecurityOrigin::Create(KURL("https://example.com/")),
+        network::mojom::ReferrerPolicy::kDefault,
+        "https://example.com/foo.html", HttpsState::kModern,
+        AllowedByNosniff::MimeTypeCheck::kStrict, address_space,
+        kLeaveInsecureRequestsAlone,
+        FetchClientSettingsObject::InsecureNavigationsSet(),
+        false /* mixed_autoupgrade_opt_out */);
+  }
+};
+
+TEST_F(DetachableResourceFetcherPropertiesTest, DetachWithDefaultValues) {
+  const auto& original_client_settings_object =
+      CreateFetchClientSettingsObject(mojom::IPAddressSpace::kPublic);
+  auto& properties = *MakeGarbageCollected<DetachableResourceFetcherProperties>(
+      *MakeGarbageCollected<TestResourceFetcherProperties>(
+          original_client_settings_object));
+
+  const auto& client_settings_object =
+      properties.GetFetchClientSettingsObject();
+  EXPECT_EQ(&original_client_settings_object, &client_settings_object);
+  EXPECT_FALSE(properties.IsMainFrame());
+  EXPECT_EQ(properties.GetControllerServiceWorkerMode(),
+            mojom::ControllerServiceWorkerMode::kNoController);
+  // We cannot call ServiceWorkerId as the service worker mode is kNoController.
+  EXPECT_FALSE(properties.IsPaused());
+  EXPECT_FALSE(properties.IsDetached());
+  EXPECT_FALSE(properties.IsLoadComplete());
+  EXPECT_FALSE(properties.ShouldBlockLoadingSubResource());
+  EXPECT_FALSE(properties.IsSubframeDeprioritizationEnabled());
+  EXPECT_EQ(scheduler::FrameStatus::kNone, properties.GetFrameStatus());
+
+  properties.Detach();
+
+  EXPECT_NE(&client_settings_object,
+            &properties.GetFetchClientSettingsObject());
+  EXPECT_EQ(properties.GetFetchClientSettingsObject().BaseUrl(),
+            KURL("https://example.com/foo.html"));
+  EXPECT_FALSE(properties.IsMainFrame());
+  EXPECT_EQ(properties.GetControllerServiceWorkerMode(),
+            mojom::ControllerServiceWorkerMode::kNoController);
+  // We cannot call ServiceWorkerId as the service worker mode is kNoController.
+  EXPECT_FALSE(properties.IsPaused());
+  EXPECT_TRUE(properties.IsDetached());
+  EXPECT_FALSE(properties.IsLoadComplete());
+  EXPECT_TRUE(properties.ShouldBlockLoadingSubResource());
+  EXPECT_FALSE(properties.IsSubframeDeprioritizationEnabled());
+  EXPECT_EQ(scheduler::FrameStatus::kNone, properties.GetFrameStatus());
+}
+
+TEST_F(DetachableResourceFetcherPropertiesTest, DetachWithNonDefaultValues) {
+  const auto& original_client_settings_object =
+      CreateFetchClientSettingsObject(mojom::IPAddressSpace::kPublic);
+  auto& original_properties =
+      *MakeGarbageCollected<TestResourceFetcherProperties>(
+          original_client_settings_object);
+  auto& properties = *MakeGarbageCollected<DetachableResourceFetcherProperties>(
+      original_properties);
+
+  original_properties.SetIsMainFrame(true);
+  original_properties.SetControllerServiceWorkerMode(
+      mojom::ControllerServiceWorkerMode::kControlled);
+  original_properties.SetServiceWorkerId(133);
+  original_properties.SetIsPaused(true);
+  original_properties.SetIsLoadComplete(true);
+  original_properties.SetShouldBlockLoadingSubResource(true);
+  original_properties.SetIsSubframeDeprioritizationEnabled(true);
+  original_properties.SetFrameStatus(scheduler::FrameStatus::kMainFrameVisible);
+
+  const auto& client_settings_object =
+      properties.GetFetchClientSettingsObject();
+  EXPECT_EQ(&original_client_settings_object, &client_settings_object);
+  EXPECT_TRUE(properties.IsMainFrame());
+  EXPECT_EQ(properties.GetControllerServiceWorkerMode(),
+            mojom::ControllerServiceWorkerMode::kControlled);
+  EXPECT_EQ(properties.ServiceWorkerId(), 133);
+  EXPECT_TRUE(properties.IsPaused());
+  EXPECT_FALSE(properties.IsDetached());
+  EXPECT_TRUE(properties.IsLoadComplete());
+  EXPECT_TRUE(properties.ShouldBlockLoadingSubResource());
+  EXPECT_TRUE(properties.IsSubframeDeprioritizationEnabled());
+  EXPECT_EQ(scheduler::FrameStatus::kMainFrameVisible,
+            properties.GetFrameStatus());
+
+  properties.Detach();
+
+  EXPECT_NE(&client_settings_object,
+            &properties.GetFetchClientSettingsObject());
+  EXPECT_EQ(properties.GetFetchClientSettingsObject().BaseUrl(),
+            KURL("https://example.com/foo.html"));
+  EXPECT_TRUE(properties.IsMainFrame());
+  EXPECT_EQ(properties.GetControllerServiceWorkerMode(),
+            mojom::ControllerServiceWorkerMode::kNoController);
+  // We cannot call ServiceWorkerId as the service worker mode is kNoController.
+  EXPECT_TRUE(properties.IsPaused());
+  EXPECT_TRUE(properties.IsDetached());
+  EXPECT_TRUE(properties.IsLoadComplete());
+  EXPECT_TRUE(properties.ShouldBlockLoadingSubResource());
+  EXPECT_TRUE(properties.IsSubframeDeprioritizationEnabled());
+  EXPECT_EQ(scheduler::FrameStatus::kNone, properties.GetFrameStatus());
+}
+
+}  // namespace
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
index bbad9b8..8a47cdc9 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
@@ -49,7 +49,6 @@
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
 #include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
-#include "third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h"
 #include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h"
@@ -163,13 +162,15 @@
   scoped_refptr<scheduler::FakeTaskRunner> CreateTaskRunner() {
     return base::MakeRefCounted<scheduler::FakeTaskRunner>();
   }
-  ResourceFetcher* CreateFetcher(const ResourceFetcherProperties& properties,
-                                 FetchContext* context) {
-    return MakeGarbageCollected<ResourceFetcher>(
-        ResourceFetcherInit(properties, context, CreateTaskRunner(),
-                            MakeGarbageCollected<TestLoaderFactory>()));
+  ResourceFetcher* CreateFetcher(
+      const TestResourceFetcherProperties& properties,
+      FetchContext* context) {
+    return MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+        properties.MakeDetachable(), context, CreateTaskRunner(),
+        MakeGarbageCollected<TestLoaderFactory>()));
   }
-  ResourceFetcher* CreateFetcher(const ResourceFetcherProperties& properties) {
+  ResourceFetcher* CreateFetcher(
+      const TestResourceFetcherProperties& properties) {
     return CreateFetcher(properties, MakeGarbageCollected<MockFetchContext>());
   }
   ResourceFetcher* CreateFetcher() {
@@ -190,9 +191,8 @@
   KURL secure_url("https://secureorigin.test/image.png");
   // Try to request a url. The request should fail, and a resource in an error
   // state should be returned, and no resource should be present in the cache.
-  auto* fetcher =
-      CreateFetcher(*MakeGarbageCollected<NullResourceFetcherProperties>(),
-                    &FetchContext::NullInstance());
+  auto* fetcher = CreateFetcher();
+  fetcher->ClearContext();
   ResourceRequest resource_request(secure_url);
   resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL);
   FetchParameters fetch_params(resource_request);
@@ -373,9 +373,10 @@
     auto* properties =
         MakeGarbageCollected<TestResourceFetcherProperties>(source_origin_);
     MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
-    auto* fetcher2 = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
-        *properties, context, base::MakeRefCounted<scheduler::FakeTaskRunner>(),
-        MakeGarbageCollected<TestLoaderFactory>()));
+    auto* fetcher2 = MakeGarbageCollected<ResourceFetcher>(
+        ResourceFetcherInit(properties->MakeDetachable(), context,
+                            base::MakeRefCounted<scheduler::FakeTaskRunner>(),
+                            MakeGarbageCollected<TestLoaderFactory>()));
     ResourceRequest resource_request2(GetResource()->Url());
     resource_request2.SetCacheMode(mojom::FetchCacheMode::kValidateCache);
     FetchParameters fetch_params2(resource_request2);
@@ -526,9 +527,9 @@
 
   void Request(const WebString& url) {
     auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-    auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
-        ResourceFetcherInit(*properties, context_, task_runner_,
-                            MakeGarbageCollected<TestLoaderFactory>()));
+    auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+        properties->MakeDetachable(), context_, task_runner_,
+        MakeGarbageCollected<TestLoaderFactory>()));
     ResourceRequest resource_request(url);
     resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL);
     FetchParameters fetch_params(resource_request);
@@ -1067,105 +1068,22 @@
   }
 }
 
-TEST_F(ResourceFetcherTest, DetachedPropertiesWithDefaultValues) {
-  const auto& original_client_settings_object =
-      CreateFetchClientSettingsObject(mojom::IPAddressSpace::kPublic);
-  const auto& original_properties =
-      *MakeGarbageCollected<TestResourceFetcherProperties>(
-          original_client_settings_object);
-  auto* const fetcher = CreateFetcher(original_properties);
-  const auto& properties = fetcher->GetProperties();
+TEST_F(ResourceFetcherTest, Detach) {
+  DetachableResourceFetcherProperties& properties =
+      MakeGarbageCollected<TestResourceFetcherProperties>()->MakeDetachable();
+  auto* const fetcher =
+      MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+          properties, MakeGarbageCollected<MockFetchContext>(),
+          CreateTaskRunner(), MakeGarbageCollected<TestLoaderFactory>()));
 
-  EXPECT_NE(&original_properties, &properties);
-
-  const auto& client_settings_object =
-      properties.GetFetchClientSettingsObject();
-  EXPECT_EQ(&original_client_settings_object, &client_settings_object);
-  EXPECT_FALSE(properties.IsMainFrame());
-  EXPECT_EQ(properties.GetControllerServiceWorkerMode(),
-            mojom::ControllerServiceWorkerMode::kNoController);
-  // We cannot call ServiceWorkerId as the service worker mode is kNoController.
-  EXPECT_FALSE(properties.IsPaused());
+  EXPECT_EQ(&properties, &fetcher->GetProperties());
   EXPECT_FALSE(properties.IsDetached());
-  EXPECT_FALSE(properties.IsLoadComplete());
-  EXPECT_FALSE(properties.ShouldBlockLoadingSubResource());
-  EXPECT_FALSE(properties.IsSubframeDeprioritizationEnabled());
-  EXPECT_EQ(scheduler::FrameStatus::kNone, properties.GetFrameStatus());
 
   fetcher->ClearContext();
   // ResourceFetcher::GetProperties always returns the same object.
   EXPECT_EQ(&properties, &fetcher->GetProperties());
 
-  EXPECT_NE(&client_settings_object,
-            &properties.GetFetchClientSettingsObject());
-  EXPECT_EQ(properties.GetFetchClientSettingsObject().BaseUrl(),
-            KURL("https://example.com/foo.html"));
-  EXPECT_FALSE(properties.IsMainFrame());
-  EXPECT_EQ(properties.GetControllerServiceWorkerMode(),
-            mojom::ControllerServiceWorkerMode::kNoController);
-  // We cannot call ServiceWorkerId as the service worker mode is kNoController.
-  EXPECT_FALSE(properties.IsPaused());
   EXPECT_TRUE(properties.IsDetached());
-  EXPECT_FALSE(properties.IsLoadComplete());
-  EXPECT_TRUE(properties.ShouldBlockLoadingSubResource());
-  EXPECT_FALSE(properties.IsSubframeDeprioritizationEnabled());
-  EXPECT_EQ(scheduler::FrameStatus::kNone, properties.GetFrameStatus());
-}
-
-TEST_F(ResourceFetcherTest, DetachedPropertiesWithNonDefaultValues) {
-  const auto& original_client_settings_object =
-      CreateFetchClientSettingsObject(mojom::IPAddressSpace::kPublic);
-  auto& original_properties =
-      *MakeGarbageCollected<TestResourceFetcherProperties>(
-          original_client_settings_object);
-  auto* const fetcher = CreateFetcher(original_properties);
-  const auto& properties = fetcher->GetProperties();
-
-  EXPECT_NE(&original_properties, &properties);
-
-  original_properties.SetIsMainFrame(true);
-  original_properties.SetControllerServiceWorkerMode(
-      mojom::ControllerServiceWorkerMode::kControlled);
-  original_properties.SetServiceWorkerId(133);
-  original_properties.SetIsPaused(true);
-  original_properties.SetIsLoadComplete(true);
-  original_properties.SetShouldBlockLoadingSubResource(true);
-  original_properties.SetIsSubframeDeprioritizationEnabled(true);
-  original_properties.SetFrameStatus(scheduler::FrameStatus::kMainFrameVisible);
-
-  const auto& client_settings_object =
-      properties.GetFetchClientSettingsObject();
-  EXPECT_EQ(&original_client_settings_object, &client_settings_object);
-  EXPECT_TRUE(properties.IsMainFrame());
-  EXPECT_EQ(properties.GetControllerServiceWorkerMode(),
-            mojom::ControllerServiceWorkerMode::kControlled);
-  EXPECT_EQ(properties.ServiceWorkerId(), 133);
-  EXPECT_TRUE(properties.IsPaused());
-  EXPECT_FALSE(properties.IsDetached());
-  EXPECT_TRUE(properties.IsLoadComplete());
-  EXPECT_TRUE(properties.ShouldBlockLoadingSubResource());
-  EXPECT_TRUE(properties.IsSubframeDeprioritizationEnabled());
-  EXPECT_EQ(scheduler::FrameStatus::kMainFrameVisible,
-            properties.GetFrameStatus());
-
-  fetcher->ClearContext();
-  // ResourceFetcher::GetProperties always returns the same object.
-  EXPECT_EQ(&properties, &fetcher->GetProperties());
-
-  EXPECT_NE(&client_settings_object,
-            &properties.GetFetchClientSettingsObject());
-  EXPECT_EQ(properties.GetFetchClientSettingsObject().BaseUrl(),
-            KURL("https://example.com/foo.html"));
-  EXPECT_TRUE(properties.IsMainFrame());
-  EXPECT_EQ(properties.GetControllerServiceWorkerMode(),
-            mojom::ControllerServiceWorkerMode::kNoController);
-  // We cannot call ServiceWorkerId as the service worker mode is kNoController.
-  EXPECT_TRUE(properties.IsPaused());
-  EXPECT_TRUE(properties.IsDetached());
-  EXPECT_TRUE(properties.IsLoadComplete());
-  EXPECT_TRUE(properties.ShouldBlockLoadingSubResource());
-  EXPECT_TRUE(properties.IsSubframeDeprioritizationEnabled());
-  EXPECT_EQ(scheduler::FrameStatus::kNone, properties.GetFrameStatus());
 }
 
 // TODO(crbug.com/850785): Reenable this.
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc
index 99839fe..789596cf 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc
@@ -97,7 +97,7 @@
 }
 
 size_t GetOutstandingThrottledLimit(
-    const ResourceFetcherProperties& properties) {
+    const DetachableResourceFetcherProperties& properties) {
   if (!RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled())
     return ResourceLoadScheduler::kOutstandingUnlimited;
 
@@ -127,7 +127,7 @@
 class ResourceLoadScheduler::TrafficMonitor {
  public:
   explicit TrafficMonitor(
-      const ResourceFetcherProperties& resource_fetcher_properties);
+      const DetachableResourceFetcherProperties& resource_fetcher_properties);
   ~TrafficMonitor();
 
   // Notified when the ThrottlingState is changed.
@@ -140,7 +140,7 @@
   void ReportAll();
 
  private:
-  const Persistent<const ResourceFetcherProperties>
+  const Persistent<const DetachableResourceFetcherProperties>
       resource_fetcher_properties_;
 
   scheduler::SchedulingLifecycleState current_state_ =
@@ -162,7 +162,7 @@
 };
 
 ResourceLoadScheduler::TrafficMonitor::TrafficMonitor(
-    const ResourceFetcherProperties& resource_fetcher_properties)
+    const DetachableResourceFetcherProperties& resource_fetcher_properties)
     : resource_fetcher_properties_(resource_fetcher_properties),
       traffic_kilobytes_per_frame_status_(
           "Blink.ResourceLoadScheduler.TrafficBytes.KBPerFrameStatus",
@@ -351,9 +351,9 @@
 
 ResourceLoadScheduler::ResourceLoadScheduler(
     ThrottlingPolicy initial_throttling_policy,
-    const ResourceFetcherProperties& resource_fetcher_properties,
+    const DetachableResourceFetcherProperties& resource_fetcher_properties,
     FrameScheduler* frame_scheduler,
-    ConsoleLogger& console_logger)
+    DetachableConsoleLogger& console_logger)
     : resource_fetcher_properties_(resource_fetcher_properties),
       policy_(initial_throttling_policy),
       outstanding_limit_for_throttled_frame_scheduler_(
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h
index dd8ca10..1a8697f 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h
@@ -17,8 +17,8 @@
 
 namespace blink {
 
-class ConsoleLogger;
-class ResourceFetcherProperties;
+class DetachableConsoleLogger;
+class DetachableResourceFetcherProperties;
 
 // Client interface to use the throttling/scheduling functionality that
 // ResourceLoadScheduler provides.
@@ -147,9 +147,9 @@
       std::numeric_limits<size_t>::max();
 
   ResourceLoadScheduler(ThrottlingPolicy initial_throttling_poilcy,
-                        const ResourceFetcherProperties&,
+                        const DetachableResourceFetcherProperties&,
                         FrameScheduler*,
-                        ConsoleLogger& console_logger);
+                        DetachableConsoleLogger& console_logger);
   ~ResourceLoadScheduler() override;
 
   void Trace(blink::Visitor*);
@@ -272,7 +272,8 @@
 
   void ShowConsoleMessageIfNeeded();
 
-  const Member<const ResourceFetcherProperties> resource_fetcher_properties_;
+  const Member<const DetachableResourceFetcherProperties>
+      resource_fetcher_properties_;
 
   // A flag to indicate an internal running state.
   // TODO(toyoshim): We may want to use enum once we start to have more states.
@@ -339,7 +340,7 @@
   std::unique_ptr<FrameScheduler::LifecycleObserverHandle>
       scheduler_observer_handle_;
 
-  const Member<ConsoleLogger> console_logger_;
+  const Member<DetachableConsoleLogger> console_logger_;
 
   DISALLOW_COPY_AND_ASSIGN(ResourceLoadScheduler);
 };
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
index 9e2a157..729dd7c 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
@@ -58,8 +58,8 @@
   }
 
  private:
-  Member<ConsoleLogger> console_logger_ =
-      MakeGarbageCollected<NullConsoleLogger>();
+  Member<DetachableConsoleLogger> console_logger_ =
+      MakeGarbageCollected<DetachableConsoleLogger>();
   MockClientDelegate* delegate_;
   bool was_run_ = false;
 };
@@ -91,8 +91,9 @@
     auto frame_scheduler = std::make_unique<scheduler::FakeFrameScheduler>();
     console_logger_ = MakeGarbageCollected<MockConsoleLogger>();
     scheduler_ = MakeGarbageCollected<ResourceLoadScheduler>(
-        ResourceLoadScheduler::ThrottlingPolicy::kTight, *properties,
-        frame_scheduler.get(), *console_logger_);
+        ResourceLoadScheduler::ThrottlingPolicy::kTight,
+        properties->MakeDetachable(), frame_scheduler.get(),
+        *MakeGarbageCollected<DetachableConsoleLogger>(console_logger_));
     Scheduler()->SetOutstandingLimitForTesting(1);
   }
   void TearDown() override { Scheduler()->Shutdown(); }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc
index a9ec4d2..4978be07 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc
@@ -37,7 +37,7 @@
 
   ResourceFetcher* CreateFetcher() {
     return MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
-        *MakeGarbageCollected<TestResourceFetcherProperties>(),
+        MakeGarbageCollected<TestResourceFetcherProperties>()->MakeDetachable(),
         MakeGarbageCollected<MockFetchContext>(),
         base::MakeRefCounted<scheduler::FakeTaskRunner>(),
         MakeGarbageCollected<TestLoaderFactory>()));
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
index b28bb82..2d7bcfb 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
@@ -179,9 +179,9 @@
     auto* properties =
         MakeGarbageCollected<TestResourceFetcherProperties>(origin);
     FetchContext* context = MakeGarbageCollected<MockFetchContext>();
-    auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
-        ResourceFetcherInit(*properties, context, CreateTaskRunner(),
-                            MakeGarbageCollected<NoopLoaderFactory>()));
+    auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+        properties->MakeDetachable(), context, CreateTaskRunner(),
+        MakeGarbageCollected<NoopLoaderFactory>()));
     ResourceRequest request;
     request.SetUrl(test.url);
     request.SetFetchRequestMode(test.request_mode);
@@ -213,9 +213,9 @@
 TEST_F(ResourceLoaderTest, LoadResponseBody) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
   FetchContext* context = MakeGarbageCollected<MockFetchContext>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
-      ResourceFetcherInit(*properties, context, CreateTaskRunner(),
-                          MakeGarbageCollected<NoopLoaderFactory>()));
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+      properties->MakeDetachable(), context, CreateTaskRunner(),
+      MakeGarbageCollected<NoopLoaderFactory>()));
 
   KURL url("https://www.example.com/");
   ResourceRequest request(url);
@@ -277,9 +277,9 @@
 TEST_F(ResourceLoaderTest, LoadDataURL_AsyncAndNonStream) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
   FetchContext* context = MakeGarbageCollected<MockFetchContext>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
-      ResourceFetcherInit(*properties, context, CreateTaskRunner(),
-                          MakeGarbageCollected<NoopLoaderFactory>()));
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+      properties->MakeDetachable(), context, CreateTaskRunner(),
+      MakeGarbageCollected<NoopLoaderFactory>()));
 
   // Fetch a data url.
   KURL url("data:text/plain,Hello%20World!");
@@ -332,9 +332,9 @@
 TEST_F(ResourceLoaderTest, LoadDataURL_AsyncAndStream) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
   FetchContext* context = MakeGarbageCollected<MockFetchContext>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
-      ResourceFetcherInit(*properties, context, CreateTaskRunner(),
-                          MakeGarbageCollected<NoopLoaderFactory>()));
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+      properties->MakeDetachable(), context, CreateTaskRunner(),
+      MakeGarbageCollected<NoopLoaderFactory>()));
   scheduler::FakeTaskRunner* task_runner =
       static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get());
 
@@ -371,9 +371,9 @@
 TEST_F(ResourceLoaderTest, LoadDataURL_AsyncEmptyData) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
   FetchContext* context = MakeGarbageCollected<MockFetchContext>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
-      ResourceFetcherInit(*properties, context, CreateTaskRunner(),
-                          MakeGarbageCollected<NoopLoaderFactory>()));
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+      properties->MakeDetachable(), context, CreateTaskRunner(),
+      MakeGarbageCollected<NoopLoaderFactory>()));
 
   // Fetch an empty data url.
   KURL url("data:text/html,");
@@ -394,9 +394,9 @@
 TEST_F(ResourceLoaderTest, LoadDataURL_Sync) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
   FetchContext* context = MakeGarbageCollected<MockFetchContext>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
-      ResourceFetcherInit(*properties, context, CreateTaskRunner(),
-                          MakeGarbageCollected<NoopLoaderFactory>()));
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+      properties->MakeDetachable(), context, CreateTaskRunner(),
+      MakeGarbageCollected<NoopLoaderFactory>()));
 
   // Fetch a data url synchronously.
   KURL url("data:text/plain,Hello%20World!");
@@ -419,9 +419,9 @@
 TEST_F(ResourceLoaderTest, LoadDataURL_SyncEmptyData) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
   FetchContext* context = MakeGarbageCollected<MockFetchContext>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
-      ResourceFetcherInit(*properties, context, CreateTaskRunner(),
-                          MakeGarbageCollected<NoopLoaderFactory>()));
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+      properties->MakeDetachable(), context, CreateTaskRunner(),
+      MakeGarbageCollected<NoopLoaderFactory>()));
 
   // Fetch an empty data url synchronously.
   KURL url("data:text/html,");
@@ -440,9 +440,9 @@
 TEST_F(ResourceLoaderTest, LoadDataURL_DefersAsyncAndNonStream) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
   FetchContext* context = MakeGarbageCollected<MockFetchContext>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
-      ResourceFetcherInit(*properties, context, CreateTaskRunner(),
-                          MakeGarbageCollected<NoopLoaderFactory>()));
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+      properties->MakeDetachable(), context, CreateTaskRunner(),
+      MakeGarbageCollected<NoopLoaderFactory>()));
   scheduler::FakeTaskRunner* task_runner =
       static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get());
 
@@ -486,9 +486,9 @@
 TEST_F(ResourceLoaderTest, LoadDataURL_DefersAsyncAndStream) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
   FetchContext* context = MakeGarbageCollected<MockFetchContext>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
-      ResourceFetcherInit(*properties, context, CreateTaskRunner(),
-                          MakeGarbageCollected<NoopLoaderFactory>()));
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+      properties->MakeDetachable(), context, CreateTaskRunner(),
+      MakeGarbageCollected<NoopLoaderFactory>()));
   scheduler::FakeTaskRunner* task_runner =
       static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get());
 
@@ -559,9 +559,9 @@
     auto* properties =
         MakeGarbageCollected<TestResourceFetcherProperties>(origin);
     FetchContext* context = MakeGarbageCollected<MockFetchContext>();
-    auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
-        ResourceFetcherInit(*properties, context, CreateTaskRunner(),
-                            MakeGarbageCollected<NoopLoaderFactory>()));
+    auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+        properties->MakeDetachable(), context, CreateTaskRunner(),
+        MakeGarbageCollected<NoopLoaderFactory>()));
     ResourceRequest request;
     request.SetUrl(foo_url_);
     request.SetRequestContext(mojom::RequestContextType::FETCH);
diff --git a/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h b/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h
index 157133d..9cda1a0 100644
--- a/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h
+++ b/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h
@@ -25,6 +25,10 @@
 
   void Trace(Visitor* visitor) override;
 
+  DetachableResourceFetcherProperties& MakeDetachable() const {
+    return *MakeGarbageCollected<DetachableResourceFetcherProperties>(*this);
+  }
+
   // ResourceFetcherProperties implementation
   const FetchClientSettingsObject& GetFetchClientSettingsObject()
       const override {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 3648344b..67f03a9 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -601,11 +601,6 @@
       name: "FormAssociatedCustomElements",
       status: "experimental",
     },
-    // This is to communicate features::FormControlsRefresh from ui (and can be
-    // removed when the feature launches).
-    {
-      name: "FormControlsRefresh",
-    },
     {
       name: "FormDataEvent",
       status: "experimental",
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 67a51f64c..73f6a48 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3954,8 +3954,6 @@
 crbug.com/835717 virtual/omt-worker-fetch/external/wpt/content-security-policy/inside-worker/dedicated-inheritance.html [ Skip ]
 # This is failing because of the lack of CSP support.
 crbug.com/940316 virtual/omt-worker-fetch/external/wpt/content-security-policy/inside-worker/shared-script.html [ Failure Pass ]
-# This is failing because of the lack of origin trial context support.
-crbug.com/945215 virtual/omt-worker-fetch/http/tests/origin_trials/sample-api-workers.html [ Failure ]
 
 # This fails because AllowedByNoSniff::MimeTypeAsScript() blocks the nested worker's
 # worker script, because the script url has a .html file extension.
diff --git a/third_party/blink/web_tests/custom-elements/form-validation-bubble-blur.html b/third_party/blink/web_tests/custom-elements/form-validation-bubble-blur.html
new file mode 100644
index 0000000..d9b21533
--- /dev/null
+++ b/third_party/blink/web_tests/custom-elements/form-validation-bubble-blur.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<style>
+control-element {
+  display: inline-block;
+  width: 10em;
+  height: 2em;
+}
+</style>
+<body>
+<script>
+class ControlElement extends HTMLElement {
+  static formAssociated = true;
+  constructor() {
+    super();
+    this.i = this.attachInternals();
+    this.i.setValidity({tooShort: true}, 'Too short');
+  }
+}
+customElements.define('control-element', ControlElement);
+
+async_test(t => {
+  assert_own_property(window, 'internals');
+  document.body.insertAdjacentHTML('afterbegin', '<control-element tabindex=0></control-element>');
+  let control = document.body.querySelector('control-element');
+  control.i.reportValidity();
+  assert_true(internals.isValidationMessageVisible(control),
+              'prereq: A validation bubble should be shown.');
+  assert_equals(document.activeElement, control,
+                'prereq: The control should be focused.');
+
+  control.blur();
+  requestAnimationFrame(t.step_func_done(() => {
+    assert_false(internals.isValidationMessageVisible(control));
+  }));
+}, 'Ensure that removing focus from an invalid form-associated custom ' +
+    'element with a validation bubble closes the validation bubble.');
+</script>
+</body>
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 c1ab574..b9bd504 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
@@ -15103,6 +15103,18 @@
      {}
     ]
    ],
+   "css/CSS2/normal-flow/dynamic-percentage-height.html": [
+    [
+     "css/CSS2/normal-flow/dynamic-percentage-height.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square-only.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/CSS2/normal-flow/float-percentage-resolution-quirks-mode.html": [
     [
      "css/CSS2/normal-flow/float-percentage-resolution-quirks-mode.html",
@@ -58001,6 +58013,114 @@
      {}
     ]
    ],
+   "css/css-position/position-absolute-dynamic-list-marker.html": [
+    [
+     "css/css-position/position-absolute-dynamic-list-marker.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square-only.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-position/position-absolute-dynamic-overflow-001.html": [
+    [
+     "css/css-position/position-absolute-dynamic-overflow-001.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square-only.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-position/position-absolute-dynamic-overflow-002.html": [
+    [
+     "css/css-position/position-absolute-dynamic-overflow-002.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square-only.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-position/position-absolute-dynamic-static-position-floats-001.html": [
+    [
+     "css/css-position/position-absolute-dynamic-static-position-floats-001.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square-only.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-position/position-absolute-dynamic-static-position-floats-002.html": [
+    [
+     "css/css-position/position-absolute-dynamic-static-position-floats-002.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square-only.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-position/position-absolute-dynamic-static-position-floats-003.html": [
+    [
+     "css/css-position/position-absolute-dynamic-static-position-floats-003.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square-only.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-position/position-absolute-dynamic-static-position-margin-001.html": [
+    [
+     "css/css-position/position-absolute-dynamic-static-position-margin-001.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square-only.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-position/position-absolute-dynamic-static-position-margin-002.html": [
+    [
+     "css/css-position/position-absolute-dynamic-static-position-margin-002.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square-only.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-position/position-absolute-dynamic-static-position.html": [
+    [
+     "css/css-position/position-absolute-dynamic-static-position.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square-only.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-position/position-relative-table-tbody-left-absolute-child.html": [
     [
      "css/css-position/position-relative-table-tbody-left-absolute-child.html",
@@ -65013,6 +65133,174 @@
      {}
     ]
    ],
+   "css/css-text/line-break/line-break-anywhere-003.html": [
+    [
+     "css/css-text/line-break/line-break-anywhere-003.html",
+     [
+      [
+       "/css/css-text/line-break/reference/line-break-anywhere-003-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/line-break/line-break-anywhere-004.html": [
+    [
+     "css/css-text/line-break/line-break-anywhere-004.html",
+     [
+      [
+       "/css/css-text/line-break/reference/line-break-anywhere-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/line-break/line-break-anywhere-005.html": [
+    [
+     "css/css-text/line-break/line-break-anywhere-005.html",
+     [
+      [
+       "/css/css-text/line-break/reference/line-break-anywhere-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/line-break/line-break-anywhere-006.html": [
+    [
+     "css/css-text/line-break/line-break-anywhere-006.html",
+     [
+      [
+       "/css/css-text/line-break/reference/line-break-anywhere-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/line-break/line-break-anywhere-007.html": [
+    [
+     "css/css-text/line-break/line-break-anywhere-007.html",
+     [
+      [
+       "/css/css-text/line-break/reference/line-break-anywhere-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/line-break/line-break-anywhere-008.html": [
+    [
+     "css/css-text/line-break/line-break-anywhere-008.html",
+     [
+      [
+       "/css/css-text/line-break/reference/line-break-anywhere-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/line-break/line-break-anywhere-009.html": [
+    [
+     "css/css-text/line-break/line-break-anywhere-009.html",
+     [
+      [
+       "/css/css-text/line-break/reference/line-break-anywhere-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/line-break/line-break-anywhere-010.html": [
+    [
+     "css/css-text/line-break/line-break-anywhere-010.html",
+     [
+      [
+       "/css/css-text/line-break/reference/line-break-anywhere-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/line-break/line-break-anywhere-011.html": [
+    [
+     "css/css-text/line-break/line-break-anywhere-011.html",
+     [
+      [
+       "/css/css-text/line-break/reference/line-break-anywhere-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/line-break/line-break-anywhere-012.html": [
+    [
+     "css/css-text/line-break/line-break-anywhere-012.html",
+     [
+      [
+       "/css/css-text/line-break/reference/line-break-anywhere-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/line-break/line-break-anywhere-013.html": [
+    [
+     "css/css-text/line-break/line-break-anywhere-013.html",
+     [
+      [
+       "/css/css-text/line-break/reference/line-break-anywhere-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/line-break/line-break-anywhere-014.html": [
+    [
+     "css/css-text/line-break/line-break-anywhere-014.html",
+     [
+      [
+       "/css/css-text/line-break/reference/line-break-anywhere-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/line-break/line-break-anywhere-015.html": [
+    [
+     "css/css-text/line-break/line-break-anywhere-015.html",
+     [
+      [
+       "/css/css-text/line-break/reference/line-break-anywhere-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/line-break/line-break-anywhere-016.html": [
+    [
+     "css/css-text/line-break/line-break-anywhere-016.html",
+     [
+      [
+       "/css/css-text/line-break/reference/line-break-anywhere-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/line-break/line-break-loose-011.xht": [
     [
      "css/css-text/line-break/line-break-loose-011.xht",
@@ -70061,6 +70349,54 @@
      {}
     ]
    ],
+   "css/css-text/word-break/word-break-break-all-016.html": [
+    [
+     "css/css-text/word-break/word-break-break-all-016.html",
+     [
+      [
+       "/css/css-text/word-break/reference/word-break-break-all-010-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/word-break/word-break-break-all-017.html": [
+    [
+     "css/css-text/word-break/word-break-break-all-017.html",
+     [
+      [
+       "/css/css-text/word-break/reference/word-break-break-all-010-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/word-break/word-break-break-all-018.html": [
+    [
+     "css/css-text/word-break/word-break-break-all-018.html",
+     [
+      [
+       "/css/css-text/word-break/reference/word-break-break-all-010-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/word-break/word-break-break-all-019.html": [
+    [
+     "css/css-text/word-break/word-break-break-all-019.html",
+     [
+      [
+       "/css/css-text/word-break/reference/word-break-break-all-010-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/word-break/word-break-break-all-020.html": [
     [
      "css/css-text/word-break/word-break-break-all-020.html",
@@ -70073,6 +70409,102 @@
      {}
     ]
    ],
+   "css/css-text/word-break/word-break-break-all-021.html": [
+    [
+     "css/css-text/word-break/word-break-break-all-021.html",
+     [
+      [
+       "/css/css-text/word-break/reference/word-break-break-all-010-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/word-break/word-break-break-all-022.html": [
+    [
+     "css/css-text/word-break/word-break-break-all-022.html",
+     [
+      [
+       "/css/css-text/word-break/reference/word-break-break-all-010-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/word-break/word-break-break-all-023.html": [
+    [
+     "css/css-text/word-break/word-break-break-all-023.html",
+     [
+      [
+       "/css/css-text/word-break/reference/word-break-break-all-010-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/word-break/word-break-break-all-024.html": [
+    [
+     "css/css-text/word-break/word-break-break-all-024.html",
+     [
+      [
+       "/css/css-text/word-break/reference/word-break-break-all-010-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/word-break/word-break-break-all-025.html": [
+    [
+     "css/css-text/word-break/word-break-break-all-025.html",
+     [
+      [
+       "/css/css-text/word-break/reference/word-break-break-all-010-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/word-break/word-break-break-all-026.html": [
+    [
+     "css/css-text/word-break/word-break-break-all-026.html",
+     [
+      [
+       "/css/css-text/word-break/reference/word-break-break-all-010-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/word-break/word-break-break-all-027.html": [
+    [
+     "css/css-text/word-break/word-break-break-all-027.html",
+     [
+      [
+       "/css/css-text/word-break/reference/word-break-break-all-010-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/word-break/word-break-break-all-028.html": [
+    [
+     "css/css-text/word-break/word-break-break-all-028.html",
+     [
+      [
+       "/css/css-text/word-break/reference/word-break-break-all-010-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/word-break/word-break-break-word-overflow-wrap-interactions.html": [
     [
      "css/css-text/word-break/word-break-break-word-overflow-wrap-interactions.html",
@@ -149546,6 +149978,16 @@
      {}
     ]
    ],
+   "css/css-text/line-break/reference/line-break-anywhere-003-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "css/css-text/line-break/reference/line-break-anywhere-004-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-text/line-break/reference/line-break-loose-011-ref.xht": [
     [
      {}
@@ -149871,11 +150313,6 @@
      {}
     ]
    ],
-   "css/css-text/parsing/line-break-valid-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "css/css-text/parsing/overflow-wrap-valid-expected.txt": [
     [
      {}
@@ -153366,11 +153803,6 @@
      {}
     ]
    ],
-   "css/css-typed-om/the-stylepropertymap/properties/line-break-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "css/css-typed-om/the-stylepropertymap/properties/line-height-expected.txt": [
     [
      {}
@@ -358431,6 +358863,10 @@
    "f30ace92e9def67f70bc1b6b6b4ad6e43f1561bd",
    "testharness"
   ],
+  "css/CSS2/normal-flow/dynamic-percentage-height.html": [
+   "2e995963edc200aa857663e4816d0f7807e2969b",
+   "reftest"
+  ],
   "css/CSS2/normal-flow/float-percentage-resolution-quirks-mode.html": [
    "a3794579f0ea242f9e7faaffcc16de5a3a69cf7c",
    "reftest"
@@ -393951,6 +394387,42 @@
    "3968f685849663574ca213fcb90dc5fb3eaffaa3",
    "testharness"
   ],
+  "css/css-position/position-absolute-dynamic-list-marker.html": [
+   "79535326396e451d0db5ae6b6a1cfe56b810c443",
+   "reftest"
+  ],
+  "css/css-position/position-absolute-dynamic-overflow-001.html": [
+   "c07919b90f61d8446c53b01e249ad65095202c51",
+   "reftest"
+  ],
+  "css/css-position/position-absolute-dynamic-overflow-002.html": [
+   "0c3d36275b2cfc687cd68dd06469799b31fa6f3b",
+   "reftest"
+  ],
+  "css/css-position/position-absolute-dynamic-static-position-floats-001.html": [
+   "a63df41089e7e75d33ee1f46d458e97c8ebf0fb0",
+   "reftest"
+  ],
+  "css/css-position/position-absolute-dynamic-static-position-floats-002.html": [
+   "49e3807224fcf034e51c1734c60cca34ea24d300",
+   "reftest"
+  ],
+  "css/css-position/position-absolute-dynamic-static-position-floats-003.html": [
+   "fa7cc695c361f5b43300b42f3c5872e89b5c0879",
+   "reftest"
+  ],
+  "css/css-position/position-absolute-dynamic-static-position-margin-001.html": [
+   "78b0ced9df2fd0ea299a7cc35e9ca0c360f453fa",
+   "reftest"
+  ],
+  "css/css-position/position-absolute-dynamic-static-position-margin-002.html": [
+   "98b18efcf96d12581583b7a74b1437b41aaa9a2f",
+   "reftest"
+  ],
+  "css/css-position/position-absolute-dynamic-static-position.html": [
+   "35d5f65055d09b377e55a7d3e5dcaf8d3b609a39",
+   "reftest"
+  ],
   "css/css-position/position-absolute-in-inline-001.html": [
    "204260ee6784c9e648ab9f1e86b113f0d7227e22",
    "testharness"
@@ -401344,11 +401816,67 @@
    "support"
   ],
   "css/css-text/line-break/line-break-anywhere-001.html": [
-   "8c8252eb56374c9f8bef59d677ccc195eaddcce8",
+   "a031765b3245aa8b4efe303b5d4cd6019b6aa4a0",
    "reftest"
   ],
   "css/css-text/line-break/line-break-anywhere-002.html": [
-   "79d9daba1ccef1363ec92e03e95448ec9da8509a",
+   "ddbf554730740798b119e3e34e321c094be2742f",
+   "reftest"
+  ],
+  "css/css-text/line-break/line-break-anywhere-003.html": [
+   "c1d177777050fe7e144f3902215d4c28bc4c6e24",
+   "reftest"
+  ],
+  "css/css-text/line-break/line-break-anywhere-004.html": [
+   "95b73c28e35ee37c0a0269048ca9e54e145526a5",
+   "reftest"
+  ],
+  "css/css-text/line-break/line-break-anywhere-005.html": [
+   "7c9c61744d34b35d5c4120b12fe318aa16abb40d",
+   "reftest"
+  ],
+  "css/css-text/line-break/line-break-anywhere-006.html": [
+   "874846c7743081cfbb330e74eb053901d2ce62ba",
+   "reftest"
+  ],
+  "css/css-text/line-break/line-break-anywhere-007.html": [
+   "9f097ee9ffbb9de9d4b62e5d28454e10d499dcd3",
+   "reftest"
+  ],
+  "css/css-text/line-break/line-break-anywhere-008.html": [
+   "c10310939259e2f30301a5fc03faf859bfd56347",
+   "reftest"
+  ],
+  "css/css-text/line-break/line-break-anywhere-009.html": [
+   "7beba06190183f50e27a63667764f1d59e0efc5b",
+   "reftest"
+  ],
+  "css/css-text/line-break/line-break-anywhere-010.html": [
+   "2e0c7178e987536418ace1dfa7c34fca1fed8eac",
+   "reftest"
+  ],
+  "css/css-text/line-break/line-break-anywhere-011.html": [
+   "acc46dbbef3c7b6d54cf9b0488120c238eee1dd6",
+   "reftest"
+  ],
+  "css/css-text/line-break/line-break-anywhere-012.html": [
+   "e2c5d363fcf4c8b9a275794a0b9e8fd3dfcde1a7",
+   "reftest"
+  ],
+  "css/css-text/line-break/line-break-anywhere-013.html": [
+   "6b0a1bde2110bd50225387af6b16099c023c8d7f",
+   "reftest"
+  ],
+  "css/css-text/line-break/line-break-anywhere-014.html": [
+   "bfe72e6aeaab46e37f50751a64fc732112bf9835",
+   "reftest"
+  ],
+  "css/css-text/line-break/line-break-anywhere-015.html": [
+   "9fe998d8ae8eabd1d235f237af9509d580c3c90f",
+   "reftest"
+  ],
+  "css/css-text/line-break/line-break-anywhere-016.html": [
+   "923a6c318226a6bdd0feddd5f643823e2bcd6398",
    "reftest"
   ],
   "css/css-text/line-break/line-break-loose-011.xht": [
@@ -401479,6 +402007,14 @@
    "ff74b3bce2d5ece698b404ecd30b53538c0eb420",
    "support"
   ],
+  "css/css-text/line-break/reference/line-break-anywhere-003-ref.html": [
+   "ceec198d03b27a1b5de9a48f3f120ae5360fcec6",
+   "support"
+  ],
+  "css/css-text/line-break/reference/line-break-anywhere-004-ref.html": [
+   "0e0300a72dc920a5ffb54cda6fbe84a2f517d010",
+   "support"
+  ],
   "css/css-text/line-break/reference/line-break-loose-011-ref.xht": [
    "cc6d9023c0843f126ce179674127ead69abb2076",
    "support"
@@ -401892,7 +402428,7 @@
    "reftest"
   ],
   "css/css-text/overflow-wrap/overflow-wrap-break-word-008.html": [
-   "9a3a95ba382e770eb6506102a64771caaf4f00af",
+   "d3c0f491ee78df226585f0f2b3f2043752faeafd",
    "reftest"
   ],
   "css/css-text/overflow-wrap/overflow-wrap-break-word-fit-content-001.html": [
@@ -402039,10 +402575,6 @@
    "aca16649360766eed8a2b91d1692890cd03a5e4b",
    "testharness"
   ],
-  "css/css-text/parsing/line-break-valid-expected.txt": [
-   "adb0181ecebe89688191257ba9d6abdbf5ffb30d",
-   "support"
-  ],
   "css/css-text/parsing/line-break-valid.html": [
    "caaae9a5a0150a35563662a8c917046fb08e79fb",
    "testharness"
@@ -403880,7 +404412,7 @@
    "reftest"
   ],
   "css/css-text/white-space/break-spaces-009.html": [
-   "36aa9dafd6e588c292f598179465233be653ab54",
+   "128aeaf5ed151807092b083ed765082f0482c8e0",
    "reftest"
   ],
   "css/css-text/white-space/break-spaces-before-first-char-001.html": [
@@ -404811,10 +405343,58 @@
    "9602a1f2cf0bab45bc7f70dbe3fa0aeb75d6df79",
    "reftest"
   ],
+  "css/css-text/word-break/word-break-break-all-016.html": [
+   "8917d428d0f3fa2f99fb95e70a4e10c0dd5e3705",
+   "reftest"
+  ],
+  "css/css-text/word-break/word-break-break-all-017.html": [
+   "6cf3be6b90f8a7bf617aa46472d0d291031d9e55",
+   "reftest"
+  ],
+  "css/css-text/word-break/word-break-break-all-018.html": [
+   "be092be8512ea94b0e3e8c188a7072a5a53031a6",
+   "reftest"
+  ],
+  "css/css-text/word-break/word-break-break-all-019.html": [
+   "66dba54f41df2b1819c5fb53abcbf01210482353",
+   "reftest"
+  ],
   "css/css-text/word-break/word-break-break-all-020.html": [
    "ac1840131c3f214b993ffd58dcd2124523f89b6b",
    "reftest"
   ],
+  "css/css-text/word-break/word-break-break-all-021.html": [
+   "80f2dda836ac33e566833e8c76887e735f9f62ac",
+   "reftest"
+  ],
+  "css/css-text/word-break/word-break-break-all-022.html": [
+   "73e8503bb8ceaace26567abe5435f8b063405d3c",
+   "reftest"
+  ],
+  "css/css-text/word-break/word-break-break-all-023.html": [
+   "3e9791f81138ba60d7ef9eaeda8c3beba9c76595",
+   "reftest"
+  ],
+  "css/css-text/word-break/word-break-break-all-024.html": [
+   "0f6bb5b7333302a1e9ab0a485eda657a1b03fa65",
+   "reftest"
+  ],
+  "css/css-text/word-break/word-break-break-all-025.html": [
+   "eedd49a59d0da7ad4b9d365c2e5ea92ede199f1f",
+   "reftest"
+  ],
+  "css/css-text/word-break/word-break-break-all-026.html": [
+   "001d83f3a01e2fa15fc7554e730b5c769bb75671",
+   "reftest"
+  ],
+  "css/css-text/word-break/word-break-break-all-027.html": [
+   "4d34872951738ff34d1e0b8c3bdd6e174f6e71b2",
+   "reftest"
+  ],
+  "css/css-text/word-break/word-break-break-all-028.html": [
+   "dd0d2949ceb17edfdb602b944faf5593caf8f9c8",
+   "reftest"
+  ],
   "css/css-text/word-break/word-break-break-word-overflow-wrap-interactions-ref.html": [
    "3c6ab3863f88646f2fe9132963704c72d6b8d3d7",
    "support"
@@ -412363,10 +412943,6 @@
    "aec16433723313664ba37974c5190be08a540ad7",
    "testharness"
   ],
-  "css/css-typed-om/the-stylepropertymap/properties/line-break-expected.txt": [
-   "69e86aa9ac59850b828d90c98984a1595341b703",
-   "support"
-  ],
   "css/css-typed-om/the-stylepropertymap/properties/line-break.html": [
    "481a9e97fe852c5b2d69f0d5616dc618fd53b51e",
    "testharness"
@@ -496016,7 +496592,7 @@
    "support"
   ],
   "tools/ci/website_build.sh": [
-   "66593eb55290a9711c3e74b7786a91fd1be2a4e0",
+   "f91975719be21e7c1461e4f8603c4f34639b840f",
    "support"
   ],
   "tools/conftest.py": [
diff --git a/third_party/blink/web_tests/external/wpt/intersection-observer/inline-with-block-child-client-rect.html b/third_party/blink/web_tests/external/wpt/intersection-observer/inline-with-block-child-client-rect.html
new file mode 100644
index 0000000..81a8fd1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/intersection-observer/inline-with-block-child-client-rect.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/intersection-observer-test-utils.js"></script>
+
+<style>
+pre, #log {
+  position: absolute;
+  top: 120px;
+  left: 0;
+}
+#target {
+  display: inline;
+}
+</style>
+
+<div id="target">
+  <div>
+    <img width=100 height=100 />
+  </div>
+</div>
+
+<script>
+var vw = document.documentElement.clientWidth;
+var vh = document.documentElement.clientHeight;
+var entries = [];
+var target;
+
+runTestCycle(function() {
+  target = document.getElementById("target");
+  assert_true(!!target, "target exists");
+  var observer = new IntersectionObserver(function(changes) {
+    entries = entries.concat(changes)
+  });
+  observer.observe(target);
+  entries = entries.concat(observer.takeRecords());
+  assert_equals(entries.length, 0, "No initial notifications.");
+  runTestCycle(step0, "First rAF");
+}, "Inline target containing a block child");
+
+function step0() {
+  assert_equals(entries.length, 1);
+  checkRect(entries[0].boundingClientRect, clientBounds(target),
+            "entry.boundingClientRect == target.getBoundingClientRect()");
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/website_build.sh b/third_party/blink/web_tests/external/wpt/tools/ci/website_build.sh
index 66593eb..f919757 100755
--- a/third_party/blink/web_tests/external/wpt/tools/ci/website_build.sh
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/website_build.sh
@@ -56,12 +56,12 @@
 
 # Prepare the output directory so that the new build can be pushed to the
 # repository as an incremental change to the prior build.
-mkdir --parents docs/_build/html
+mkdir -p docs/_build/html
 cd docs/_build/html
 git init
 git fetch --depth 1 ${remote_url} gh-pages
 git checkout FETCH_HEAD
-git rm -r .
+git rm -rf .
 
 # Build the website
 cd ../..
@@ -78,7 +78,7 @@
 # Publish the website by pushing the built contents to the `gh-pages` branch
 git add .
 
-if ! git diff --exit-code --quiet --staged ; then
+if git diff --exit-code --quiet --staged ; then
   echo No change to the website contents. Exiting without publishing.
 
   exit ${neutral_status}
diff --git a/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
index 45331013..c488c45e 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 202 tests; 200 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 202 tests; 201 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Partial interface Navigator: original interface defined
 PASS Partial dictionary WebGLContextAttributes: original dictionary defined
@@ -74,7 +74,7 @@
 PASS XRReferenceSpace interface: existence and properties of interface prototype object
 PASS XRReferenceSpace interface: existence and properties of interface prototype object's "constructor" property
 PASS XRReferenceSpace interface: existence and properties of interface prototype object's @@unscopables property
-FAIL XRReferenceSpace interface: operation getOffsetReferenceSpace(XRRigidTransform) assert_own_property: interface prototype object missing non-static operation expected property "getOffsetReferenceSpace" missing
+PASS XRReferenceSpace interface: operation getOffsetReferenceSpace(XRRigidTransform)
 PASS XRReferenceSpace interface: attribute onreset
 PASS XRBoundedReferenceSpace interface: existence and properties of interface object
 PASS XRBoundedReferenceSpace interface object length
diff --git a/third_party/blink/web_tests/external/wpt/webxr/xrFrame_getPose.https.html b/third_party/blink/web_tests/external/wpt/webxr/xrFrame_getPose.https.html
index a8015e3..e3dd77b 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/xrFrame_getPose.https.html
+++ b/third_party/blink/web_tests/external/wpt/webxr/xrFrame_getPose.https.html
@@ -37,7 +37,7 @@
       let space2 = spaces[1];
 
       // Rotate 90 degrees about x axis, then move 1 meter along y axis.
-      space1.originOffset = new XRRigidTransform(
+      space1 = space1.getOffsetReferenceSpace(new XRRigidTransform(
         DOMPointReadOnly.fromPoint({
           x : 0,
           y : 1,
@@ -50,10 +50,10 @@
           z : 0,
           w : Math.cos(radians / 2)
         })
-      );
+      ));
 
       // Rotate 90 degrees about z axis, then move 1 meter along x axis.
-      space2.originOffset = new XRRigidTransform(
+      space2 = space2.getOffsetReferenceSpace(new XRRigidTransform(
         DOMPointReadOnly.fromPoint({
           x : 1,
           y : 0,
@@ -66,7 +66,7 @@
           z : Math.sin(radians / 2),
           w : Math.cos(radians / 2)
         })
-      );
+      ));
 
       let space1_from_space2 = xrFrame.getPose(space1, space2);
       const EXPECTED_POSE_MATRIX = [
diff --git a/third_party/blink/web_tests/fast/forms/validation-bubble-blur.html b/third_party/blink/web_tests/fast/forms/validation-bubble-blur.html
new file mode 100644
index 0000000..0ad9684
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/validation-bubble-blur.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<input required>
+<script>
+async_test(t => {
+  let input = document.querySelector('input');
+  assert_own_property(window, 'internals');
+  input.reportValidity();
+  assert_true(internals.isValidationMessageVisible(input));
+  assert_equals(document.activeElement, input);
+
+  input.blur();
+  requestAnimationFrame(t.step_func_done(() => {
+    assert_false(internals.isValidationMessageVisible(input));
+  }));
+}, 'Ensure that removing focus from an invalid control with ' +
+    'a validation bubble closes the validation bubble.');
+</script>
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 8add08bf..e9e0338 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -10626,10 +10626,9 @@
 interface XRReferenceSpace : XRSpace
     attribute @@toStringTag
     getter onreset
-    getter originOffset
     method constructor
+    method getOffsetReferenceSpace
     setter onreset
-    setter originOffset
 interface XRReferenceSpaceEvent : Event
     attribute @@toStringTag
     getter referenceSpace
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/locked-style.html b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/locked-style.html
new file mode 100644
index 0000000..bd563333
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/locked-style.html
@@ -0,0 +1,60 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>Display Locking: style on locked element & child</title>
+<link rel="author" title="Rakina Zata Amni" href="mailto:rakina@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<style>
+#container {
+  contain: style layout;
+}
+</style>
+
+<div id="container">
+  <div id="child">
+    <div id="grandchild"></div>
+  </div>
+</div>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test((t) => {
+  async function runTest() {
+    let container = document.getElementById("container");
+    await container.displayLock.acquire({ timeout: Infinity });
+
+    container.style = "color: blue;";
+    t.step(() => assert_equals(getComputedStyle(container).color, "rgb(0, 0, 255)", "container color changed to blue"));
+    t.step(() => assert_equals(getComputedStyle(child).color, "rgb(0, 0, 255)", "child inherits blue color"));
+    t.step(() => assert_equals(getComputedStyle(grandchild).color, "rgb(0, 0, 255)", "grandchild inherits blue color"));
+
+    child.style = "color: green;";
+    t.step(() => assert_equals(getComputedStyle(container).color, "rgb(0, 0, 255)", "container color is still blue"));
+    t.step(() => assert_equals(getComputedStyle(child).color, "rgb(0, 128, 0)", "child color changed to green"));
+    t.step(() => assert_equals(getComputedStyle(grandchild).color, "rgb(0, 128, 0)", "grandchild inherits green color"));
+
+    // Commit container, lock child.
+    await container.displayLock.commit();
+    child.style = "contain: style layout";
+    await child.displayLock.acquire({ timeout: Infinity });
+
+    // Update style outside of the locked subtree.
+    container.style = "color: red;";
+    container.offsetTop;
+
+    // Inheritance works as usual through locked boundaries.
+    t.step(() => assert_equals(getComputedStyle(grandchild).color, "rgb(255, 0, 0)", "grandchild inherits red color"));
+    t.step(() => assert_equals(getComputedStyle(child).color, "rgb(255, 0, 0)", "child inherits red color"));
+    t.step(() => assert_equals(getComputedStyle(container).color, "rgb(255, 0, 0)", "container color changed to red"));
+
+    t.done();
+  }
+
+  window.onload = function() {
+    requestAnimationFrame(() => requestAnimationFrame(runTest));
+  };
+}, "getComputedStyle gets up-to-date style");
+</script>
+
diff --git a/third_party/blink/web_tests/xr/xrReferenceSpace_originOffset.html b/third_party/blink/web_tests/xr/xrReferenceSpace_originOffset.html
index c8342eb5..4dbdb07 100644
--- a/third_party/blink/web_tests/xr/xrReferenceSpace_originOffset.html
+++ b/third_party/blink/web_tests/xr/xrReferenceSpace_originOffset.html
@@ -63,13 +63,19 @@
     input_source.pointerOffset = POINTER_OFFSET_WITH_ROTATION;
     fakeDeviceController.addInputSource(input_source);
 
+    const RADIANS_90D = Math.PI / 2;
+
     const EXPECTED_VIEW_MATRIX_1 = [0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, -1, -1, -1, 1];
     const EXPECTED_GRIP_MATRIX_1 = [0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 2, 3, 1];
     const EXPECTED_RAY_MATRIX_1 = [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2, 2, 1, 1];
 
-    const EXPECTED_VIEW_MATRIX_2 = [0.7419161200523376, 0.6434604525566101, -0.1884651929140091, 0, -0.1884651929140091, -0.06961867958307266, -0.9796091318130493, 0, -0.6434604525566101, 0.7623069882392883, 0.06961867958307266, 0, -4, 4, 9, 1];
-    const EXPECTED_GRIP_MATRIX_2 = [-0.6434604525566101, 0.06961867958307266, -0.7623069882392883, 0, 0.7419161200523376, -0.1884651929140091, -0.6434604525566101, 0, -0.1884651929140091, -0.9796091318130493, 0.06961867958307266, 0, 4.118846416473389, 8.01339340209961, -5.368484020233154, 1];
-    const EXPECTED_RAY_MATRIX_2 = [0.7419161200523376, -0.1884651929140091, -0.6434604525566101, 0, 0.6434604525566101, -0.06961867958307266, 0.7623069882392883, 0, -0.1884651929140091, -0.9796091318130493, 0.06961867958307266, 0, 2.643460512161255, 7.1730217933654785, -6.823479652404785, 1];
+    const EXPECTED_VIEW_MATRIX_2 = [0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, 0, -4, 4, 9, 1];
+    const EXPECTED_GRIP_MATRIX_2 = [0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, -9, -2, -5, 1];
+    const EXPECTED_RAY_MATRIX_2 = [0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, -8, -4, -5, 1];
+
+    const EXPECTED_VIEW_MATRIX_3 = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -4, 6, 14, 1];
+    const EXPECTED_GRIP_MATRIX_3 = [0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 5, -4, -14, 1];
+    const EXPECTED_RAY_MATRIX_3 = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 5, -6, -13, 1];
 
     // Must have a reference space to get input poses. eye-level doesn't apply
     // any transforms to the given matrix.
@@ -77,7 +83,7 @@
       function OnFrame(time, frame) {
         let source = session.getInputSources()[0];
 
-        function CheckState(expected_view_matrix, expected_grip_matrix, expected_ray_matrix) {
+        function CheckState(referenceSpace, expected_view_matrix, expected_grip_matrix, expected_ray_matrix) {
           let pose = frame.getViewerPose(referenceSpace);
           let grip_pose = frame.getPose(source.gripSpace, referenceSpace);
           let input_pose = frame.getPose(source.targetRaySpace, referenceSpace);
@@ -91,25 +97,38 @@
           assert_matrices_approx_equal(ray_matrix, expected_ray_matrix);
         }
 
-        CheckState(EXPECTED_VIEW_MATRIX_1, EXPECTED_GRIP_MATRIX_1, EXPECTED_RAY_MATRIX_1);
+        CheckState(referenceSpace, EXPECTED_VIEW_MATRIX_1, EXPECTED_GRIP_MATRIX_1, EXPECTED_RAY_MATRIX_1);
 
-        const new_position = {
-          x : 10,
-          y : -3,
-          z : 5
+        const new_position1 = {
+          x: 10,
+          y: -3,
+          z: 5,
+        };
+        const new_orientation1 = {
+          x: Math.sin(RADIANS_90D / 2),
+          y: 0,
+          z: 0,
+          w: Math.cos(RADIANS_90D / 2),
         };
 
-        const degrees_45 = Math.PI / 4;
-        const rotation = Math.sin(degrees_45 / 2);
-        const new_orientation = {
-          x : rotation,
-          y : rotation * -1,
-          z : rotation * 3,
-          w : Math.cos(degrees_45 / 2)
+        referenceSpace = referenceSpace.getOffsetReferenceSpace(new XRRigidTransform(new_position1, new_orientation1));
+        CheckState(referenceSpace, EXPECTED_VIEW_MATRIX_2, EXPECTED_GRIP_MATRIX_2, EXPECTED_RAY_MATRIX_2);
+
+        const new_position2 = {
+          x: 5,
+          y: 2,
+          z: 0,
+        };
+        const new_orientation2 = {
+          x: 0,
+          y: Math.sin(RADIANS_90D / 2),
+          z: 0,
+          w: Math.cos(RADIANS_90D / 2),
         };
 
-        referenceSpace.originOffset = new XRRigidTransform(new_position, new_orientation);
-        CheckState(EXPECTED_VIEW_MATRIX_2, EXPECTED_GRIP_MATRIX_2, EXPECTED_RAY_MATRIX_2);
+        referenceSpace = referenceSpace.getOffsetReferenceSpace(new XRRigidTransform(new_position2, new_orientation2));
+        CheckState(referenceSpace, EXPECTED_VIEW_MATRIX_3, EXPECTED_GRIP_MATRIX_3, EXPECTED_RAY_MATRIX_3);
+
         resolve();
       }
 
diff --git a/third_party/closure_compiler/externs/html_imports.js b/third_party/closure_compiler/externs/html_imports.js
new file mode 100644
index 0000000..906713e
--- /dev/null
+++ b/third_party/closure_compiler/externs/html_imports.js
@@ -0,0 +1,7 @@
+/** @fileoverview Externs for HTML imports polyfill. */
+
+/** @see https://github.com/webcomponents/html-imports */
+var HTMLImports = {};
+
+/** @param {!Function} callback */
+HTMLImports.whenReady = function(callback) {};
diff --git a/third_party/wayland-protocols/BUILD.gn b/third_party/wayland-protocols/BUILD.gn
index b72da6c..8a204e5 100644
--- a/third_party/wayland-protocols/BUILD.gn
+++ b/third_party/wayland-protocols/BUILD.gn
@@ -55,7 +55,6 @@
 
 wayland_protocol("gaming_input_protocol") {
   sources = [
-    "unstable/gaming-input/gaming-input-unstable-v1.xml",
     "unstable/gaming-input/gaming-input-unstable-v2.xml",
   ]
 }
diff --git a/third_party/wayland-protocols/unstable/gaming-input/gaming-input-unstable-v1.xml b/third_party/wayland-protocols/unstable/gaming-input/gaming-input-unstable-v1.xml
deleted file mode 100644
index 8ae1872..0000000
--- a/third_party/wayland-protocols/unstable/gaming-input/gaming-input-unstable-v1.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="gaming_input_unstable_v1">
-
-  <copyright>
-    Copyright 2016 The Chromium Authors.
-
-    Permission is hereby granted, free of charge, to any person obtaining a
-    copy of this software and associated documentation files (the "Software"),
-    to deal in the Software without restriction, including without limitation
-    the rights to use, copy, modify, merge, publish, distribute, sublicense,
-    and/or sell copies of the Software, and to permit persons to whom the
-    Software is furnished to do so, subject to the following conditions:
-
-    The above copyright notice and this permission notice (including the next
-    paragraph) shall be included in all copies or substantial portions of the
-    Software.
-
-    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-    DEALINGS IN THE SOFTWARE.
-  </copyright>
-
-  <interface name="zcr_gaming_input_v1" version="1">
-    <description summary="extends wl_seat with gaming input devices">
-      A global interface to provide gaming input devices for a given seat.
-
-      Currently only gamepad devices are supported.
-
-      Warning! The protocol described in this file is experimental and
-      backward incompatible changes may be made. Backward compatible changes
-      may be added together with the corresponding uinterface version bump.
-      Backward incompatible changes are done by bumping the version number in
-      the protocol and uinterface names and resetting the interface version.
-      Once the protocol is to be declared stable, the 'z' prefix and the
-      version number in the protocol and interface names are removed and the
-      interface version number is reset.
-    </description>
-
-    <request name="get_gamepad">
-      <description summary="get gamepad device assigned to seat">
-        Create gamepad object. See zcr_gamepad_v1 interface for details.
-      </description>
-      <arg name="id" type="new_id" interface="zcr_gamepad_v1"/>
-      <arg name="seat" type="object" interface="wl_seat"/>
-    </request>
-  </interface>
-
-  <interface name="zcr_gamepad_v1" version="1">
-    <description summary="gamepad input device">
-      The zcr_gamepad_v1 interface represents one or more gamepad input devices,
-      which are reported as a normalized 'Standard Gamepad' as it is specified
-      by the W3C Gamepad API at: https://w3c.github.io/gamepad/#remapping
-    </description>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy gamepad object"/>
-    </request>
-
-    <enum name="gamepad_state">
-      <description summary="connection state"/>
-      <entry name="off" value="0" summary="no gamepads are connected or on."/>
-      <entry name="on" value="1" summary="at least one gamepad is connected."/>
-    </enum>
-
-    <event name="state_change">
-      <description summary="state change event">
-        Notification that this seat's connection state has changed.
-      </description>
-      <arg name="state" type="uint" enum="gamepad_state" summary="new state"/>
-    </event>
-
-    <event name="axis">
-      <description summary="axis change event">
-        Notification of axis change.
-
-        The axis id specifies which axis has changed as defined by the W3C
-        'Standard Gamepad'.
-
-        The value is calibrated and normalized to the -1 to 1 range.
-      </description>
-      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
-      <arg name="axis" type="uint" summary="axis that produced this event"/>
-      <arg name="value" type="fixed" summary="new value of axis"/>
-    </event>
-
-    <enum name="button_state">
-      <description summary="physical button state">
-        Describes the physical state of a button that produced the button
-        event.
-      </description>
-      <entry name="released" value="0" summary="the button is not pressed"/>
-      <entry name="pressed" value="1" summary="the button is pressed"/>
-    </enum>
-
-    <event name="button">
-      <description summary="Gamepad button changed">
-        Notification of button change.
-
-        The button id specifies which button has changed as defined by the W3C
-        'Standard Gamepad'.
-
-        A button can have a digital and an analog value. The analog value is
-        normalized to a 0 to 1 range.
-        If a button does not provide an analog value, it will be derived from
-        the digital state.
-      </description>
-      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
-      <arg name="button" type="uint" summary="id of button"/>
-      <arg name="state" type="uint" enum="button_state" summary="digital state of the button"/>
-      <arg name="analog" type="fixed" summary="analog value of the button"/>
-    </event>
-
-    <event name="frame">
-      <description summary="Notifies end of a series of gamepad changes.">
-        Indicates the end of a set of events that logically belong together.
-        A client is expected to accumulate the data in all events within the
-        frame before proceeding.
-      </description>
-      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
-    </event>
-  </interface>
-
-</protocol>
diff --git a/third_party/webxr_test_pages/webxr-samples/input-selection.html b/third_party/webxr_test_pages/webxr-samples/input-selection.html
index 04251b2..1aaa6c1 100644
--- a/third_party/webxr_test_pages/webxr-samples/input-selection.html
+++ b/third_party/webxr_test_pages/webxr-samples/input-selection.html
@@ -429,8 +429,8 @@
           {x: invPosition[0], y: invPosition[1], z: invPosition[2]},
           {x: invOrientation[0], y: invOrientation[1], z: invOrientation[2], w: invOrientation[3]});
 
-        // Update originOffset to use the teleported player position and orientation
-        refSpace.originOffset = xform;
+        // Update refSpace to use a new originOffset with the teleported player position and orientation
+        refSpace = refSpace.getOffsetReferenceSpace(xform);
 
         console.log('teleport to', trackingSpaceOriginInWorldSpace);
       }
diff --git a/third_party/webxr_test_pages/webxr-samples/room-scale.html b/third_party/webxr_test_pages/webxr-samples/room-scale.html
index dbb6fad..97344c10 100644
--- a/third_party/webxr_test_pages/webxr-samples/room-scale.html
+++ b/third_party/webxr_test_pages/webxr-samples/room-scale.html
@@ -173,8 +173,7 @@
                 // right level. (Here we're moving it 1.6 meters, which should
                 // *very* roughly align us with the eye height of an "average"
                 // adult human.)
-                refSpace.originOffset = new XRRigidTransform({y: -1.6});
-                return refSpace;
+                return refSpace.getOffsetReferenceSpace(new XRRigidTransform({y: -1.6}));
               });
             } else {
               throw e;
diff --git a/tools/grit/grit/node/include.py b/tools/grit/grit/node/include.py
index 667ee61..daf5e72 100644
--- a/tools/grit/grit/node/include.py
+++ b/tools/grit/grit/node/include.py
@@ -76,7 +76,12 @@
     # We have no control over code that calles ToRealPath later, so convert
     # the path to be relative against our basedir.
     if self.attrs.get('use_base_dir', 'true') != 'true':
-      return os.path.relpath(self.attrs['file'], self.GetRoot().GetBaseDir())
+      # Normalize the directory path to use the appropriate OS separator.
+      # GetBaseDir() may return paths\like\this or paths/like/this, since it is
+      # read from the base_dir attribute in the grd file.
+      norm_base_dir = os.path.normpath(
+              self.GetRoot().GetBaseDir().replace('\\', '/'))
+      return os.path.relpath(self.attrs['file'], norm_base_dir)
 
     return self.attrs['file']
 
diff --git a/tools/grit/grit/node/include_unittest.py b/tools/grit/grit/node/include_unittest.py
index cef8c09..b4dcb8a 100755
--- a/tools/grit/grit/node/include_unittest.py
+++ b/tools/grit/grit/node/include_unittest.py
@@ -65,9 +65,11 @@
     includes.AddChild(include_node)
     root.EndParsing()
 
+    last_dir = os.path.basename(os.getcwd())
+    expected_path = util.normpath(os.path.join(
+        u'..', last_dir, u'flugel/kugel.pdf'))
     self.assertEqual(root.ToRealPath(include_node.GetInputPath()),
-                     util.normpath(
-                       os.path.join(ur'../', ur'flugel/kugel.pdf')))
+                     expected_path)
 
   def testCompressGzip(self):
     root = util.ParseGrdForUnittest('''
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index e902451..c1fb8ec 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -51531,14 +51531,17 @@
 
 <enum name="SignedExchangeSignatureVerificationResult">
   <int value="0" label="Success"/>
-  <int value="1" label="No certificate"/>
-  <int value="2" label="No cert-sha256"/>
+  <int value="1" label="No certificate (deprecated)"/>
+  <int value="2" label="No cert-sha256 (deprecated)"/>
   <int value="3" label="cert-sha256 mismatch"/>
-  <int value="4" label="Failed to reconstruct signed message"/>
+  <int value="4" label="Failed to reconstruct signed message (deprecated)"/>
   <int value="5" label="Failed to verify signature"/>
   <int value="6" label="Invalid integrity scheme"/>
-  <int value="7" label="Invalid timestamp"/>
+  <int value="7" label="Invalid timestamp (deprecated)"/>
   <int value="8" label="Unsupported certificate type"/>
+  <int value="9" label="Validity period is loo long"/>
+  <int value="10" label="Signature is not yet valid"/>
+  <int value="11" label="Signature is expired"/>
 </enum>
 
 <enum name="SignedExchangeValidityPingResult">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 4b782e4..dbeee8d9 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -121640,6 +121640,30 @@
   </summary>
 </histogram>
 
+<histogram name="Stability.Android.OomKillReverseRank" units="rank"
+    expires_after="2020-04-10">
+  <owner>boliu@chromium.org</owner>
+  <owner>ssid@chromium.org</owner>
+  <summary>
+    Records the reverse rank of a child process when it is killed by android if
+    applicable. Chrome on Android ranks some child processes and provides hints
+    to android that it should kill from lowest to highest ranked. The lowest
+    ranked process has reverse rank 0. This is a measure how good the hints to
+    android are; if hints were perfect, then all android kills should have
+    reverse rank 0.
+  </summary>
+</histogram>
+
+<histogram name="Stability.Android.OomKillReverseRankSuccess"
+    enum="BooleanSuccess" expires_after="2020-04-10">
+  <owner>boliu@chromium.org</owner>
+  <owner>ssid@chromium.org</owner>
+  <summary>
+    Getting value for OomKillReverseRank may fail. Recorded when an applicable
+    child process is killed by android.
+  </summary>
+</histogram>
+
 <histogram name="Stability.Android.PendingMinidumpsOnStartup" units="minidumps"
     expires_after="2017-05-16">
   <obsolete>
diff --git a/tools/perf/bootstrap_deps b/tools/perf/bootstrap_deps
index a277bc0..c3efae9 100644
--- a/tools/perf/bootstrap_deps
+++ b/tools/perf/bootstrap_deps
@@ -9,6 +9,7 @@
     "src/build/android": "",
     "src/chrome/test/data/perf/canvas_bench": "",
     "src/chrome/test/data/third_party/spaceport": "",
+    "src/components/variations/service": "",
     "src/tools/json_comment_eater": "",
     "src/tools/json_to_struct": "",
     "src/tools/perf/benchmarks": "",
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index d3aac86..715f95f 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -815,23 +815,23 @@
 # that live in tools/perf/core.  We need to verify off of that list.
 def get_telemetry_tests_in_performance_test_suite():
   tests = set()
-  add_benchmarks_from_sharding_map(
-      tests, "shard_maps/linux-perf_map.json")
-  add_benchmarks_from_sharding_map(
-      tests, "shard_maps/android-pixel2-perf_map.json")
+  for platform in bot_platforms.OFFICIAL_PLATFORMS:
+    add_benchmarks_from_sharding_map(
+        tests, platform.shards_map_file_path)
   return tests
 
 
-def add_benchmarks_from_sharding_map(tests, shard_map_name):
-  path = os.path.join(os.path.dirname(__file__), shard_map_name)
-  if os.path.exists(path):
-    with open(path) as f:
-      sharding_map = json.load(f)
-    for shard, benchmarks in sharding_map.iteritems():
-      if "extra_infos" in shard:
-        continue
-      for benchmark, _ in benchmarks['benchmarks'].iteritems():
-        tests.add(benchmark)
+def add_benchmarks_from_sharding_map(tests, shard_map_path):
+  if not os.path.exists(shard_map_path):
+    raise RuntimeError(
+        'Platform does not have a shard map at %s.' % shard_map_path)
+  with open(shard_map_path) as f:
+    shard_map = json.load(f)
+  for shard, benchmarks in shard_map.iteritems():
+    if "extra_infos" in shard:
+      continue
+    for benchmark, _ in benchmarks['benchmarks'].iteritems():
+      tests.add(benchmark)
 
 
 def get_scheduled_non_telemetry_benchmarks(perf_waterfall_file):
diff --git a/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java b/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java
index 123c8f1..80c64ed 100644
--- a/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java
+++ b/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java
@@ -40,7 +40,6 @@
     private static final String FALLBACK_LOCALE = "en-US";
     private static final String COMPRESSED_LOCALES_DIR = "locales";
     private static final String COMPRESSED_LOCALES_FALLBACK_DIR = "fallback-locales";
-    private static final int BUFFER_SIZE = 16 * 1024;
 
     private class ExtractTask implements Runnable {
         private final List<Runnable> mCompletionCallbacks = new ArrayList<Runnable>();
@@ -105,19 +104,13 @@
                 throw new RuntimeException();
             }
 
-            AssetManager assetManager = ContextUtils.getApplicationAssets();
-            byte[] buffer = new byte[BUFFER_SIZE];
             for (int n = 0; n < assetPaths.length; ++n) {
                 String assetPath = assetPaths[n];
                 File output = new File(outputDir, outputNames[n]);
-                TraceEvent.begin("ExtractResource");
-                try (InputStream inputStream = assetManager.open(assetPath)) {
-                    FileUtils.copyFileStreamAtomicWithBuffer(inputStream, output, buffer);
-                } catch (IOException e) {
+                if (!FileUtils.extractAsset(
+                            ContextUtils.getApplicationContext(), assetPath, output)) {
                     // The app would just crash later if files are missing.
-                    throw new RuntimeException(e);
-                } finally {
-                    TraceEvent.end("ExtractResource");
+                    throw new RuntimeException();
                 }
             }
         }
@@ -345,21 +338,15 @@
         return new File(getAppDataDir(), "paks");
     }
 
-    private static void deleteFile(File file) {
-        if (file.exists() && !file.delete()) {
-            Log.w(TAG, "Unable to remove %s", file.getName());
-        }
-    }
-
     private void deleteFiles(String[] existingFileNames) {
         // These used to be extracted, but no longer are, so just clean them up.
-        deleteFile(new File(getAppDataDir(), ICU_DATA_FILENAME));
-        deleteFile(new File(getAppDataDir(), V8_NATIVES_DATA_FILENAME));
-        deleteFile(new File(getAppDataDir(), V8_SNAPSHOT_DATA_FILENAME));
+        FileUtils.recursivelyDeleteFile(new File(getAppDataDir(), ICU_DATA_FILENAME));
+        FileUtils.recursivelyDeleteFile(new File(getAppDataDir(), V8_NATIVES_DATA_FILENAME));
+        FileUtils.recursivelyDeleteFile(new File(getAppDataDir(), V8_SNAPSHOT_DATA_FILENAME));
 
         if (existingFileNames != null) {
             for (String fileName : existingFileNames) {
-                deleteFile(new File(getOutputDir(), fileName));
+                FileUtils.recursivelyDeleteFile(new File(getOutputDir(), fileName));
             }
         }
     }
diff --git a/ui/base/clipboard/clipboard_format_type_win.cc b/ui/base/clipboard/clipboard_format_type_win.cc
index 129ef0c..c477acb 100644
--- a/ui/base/clipboard/clipboard_format_type_win.cc
+++ b/ui/base/clipboard/clipboard_format_type_win.cc
@@ -17,30 +17,17 @@
 // ClipboardFormatType implementation.
 ClipboardFormatType::ClipboardFormatType() = default;
 
-ClipboardFormatType::ClipboardFormatType(UINT native_format) {
-  // There's no good way to actually initialize this in the constructor in
-  // C++03.
-  data_.cfFormat = static_cast<CLIPFORMAT>(native_format);
-  data_.dwAspect = DVASPECT_CONTENT;
-  data_.lindex = -1;
-  data_.tymed = TYMED_HGLOBAL;
-}
+ClipboardFormatType::ClipboardFormatType(UINT native_format)
+    : ClipboardFormatType(native_format, -1) {}
 
-ClipboardFormatType::ClipboardFormatType(UINT native_format, LONG index) {
-  // There's no good way to actually initialize this in the constructor in
-  // C++03.
-  data_.cfFormat = static_cast<CLIPFORMAT>(native_format);
-  data_.dwAspect = DVASPECT_CONTENT;
-  data_.lindex = index;
-  data_.tymed = TYMED_HGLOBAL;
-}
+ClipboardFormatType::ClipboardFormatType(UINT native_format, LONG index)
+    : ClipboardFormatType(native_format, index, TYMED_HGLOBAL) {}
 
-// TODO(https://crbug.com/949411): Use delegating constructors.
 ClipboardFormatType::ClipboardFormatType(UINT native_format,
                                          LONG index,
                                          DWORD tymed) {
   // There's no good way to actually initialize this in the constructor in
-  // C++03.
+  // C++14. In C++ 20, we can use designated initializers.
   data_.cfFormat = static_cast<CLIPFORMAT>(native_format);
   data_.dwAspect = DVASPECT_CONTENT;
   data_.lindex = index;
diff --git a/ui/base/ime/chromeos/input_method_chromeos.cc b/ui/base/ime/chromeos/input_method_chromeos.cc
index a0e968f..c8daab1 100644
--- a/ui/base/ime/chromeos/input_method_chromeos.cc
+++ b/ui/base/ime/chromeos/input_method_chromeos.cc
@@ -776,7 +776,7 @@
 }
 
 void InputMethodChromeOS::HidePreeditText() {
-  if (composition_.text.empty() || IsTextInputTypeNone())
+  if (IsTextInputTypeNone())
     return;
 
   // Intentionally leaves |composing_text_| unchanged.
diff --git a/ui/chromeos/user_activity_power_manager_notifier.cc b/ui/chromeos/user_activity_power_manager_notifier.cc
index bb905ee..d28cc106 100644
--- a/ui/chromeos/user_activity_power_manager_notifier.cc
+++ b/ui/chromeos/user_activity_power_manager_notifier.cc
@@ -4,7 +4,6 @@
 
 #include "ui/chromeos/user_activity_power_manager_notifier.h"
 
-#include "chromeos/dbus/power/power_manager_client.h"
 #include "services/device/public/mojom/constants.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/base/user_activity/user_activity_detector.h"
@@ -50,6 +49,7 @@
     : detector_(detector), fingerprint_observer_binding_(this) {
   detector_->AddObserver(this);
   ui::InputDeviceManager::GetInstance()->AddObserver(this);
+  chromeos::PowerManagerClient::Get()->AddObserver(this);
 
   // Connector can be null in tests.
   if (connector) {
@@ -63,6 +63,7 @@
 }
 
 UserActivityPowerManagerNotifier::~UserActivityPowerManagerNotifier() {
+  chromeos::PowerManagerClient::Get()->RemoveObserver(this);
   ui::InputDeviceManager::GetInstance()->RemoveObserver(this);
   detector_->RemoveObserver(this);
 }
@@ -99,11 +100,26 @@
   base::TimeTicks now = base::TimeTicks::Now();
   // InSeconds() truncates rather than rounding, so it's fine for this
   // comparison.
-  if (last_notify_time_.is_null() ||
+  // powerd depends on this D-Bus call to track input events. When the
+  // system is about to suspend or in dark resume, report user activity
+  // immediately so that powerd can transition to full resume and turn the
+  // display on as soon as possible. OnUserActivity calls are rate-limited by
+  // the sender, so it's safe to always notify while we're suspending.
+  if (suspending_ || last_notify_time_.is_null() ||
       (now - last_notify_time_).InSeconds() >= kNotifyIntervalSec) {
     chromeos::PowerManagerClient::Get()->NotifyUserActivity(user_activity_type);
     last_notify_time_ = now;
   }
 }
 
+void UserActivityPowerManagerNotifier::SuspendImminent(
+    power_manager::SuspendImminent::Reason reason) {
+  suspending_ = true;
+}
+
+void UserActivityPowerManagerNotifier::SuspendDone(
+    const base::TimeDelta& sleep_duration) {
+  suspending_ = false;
+}
+
 }  // namespace ui
diff --git a/ui/chromeos/user_activity_power_manager_notifier.h b/ui/chromeos/user_activity_power_manager_notifier.h
index 1710fdb..6c9db45 100644
--- a/ui/chromeos/user_activity_power_manager_notifier.h
+++ b/ui/chromeos/user_activity_power_manager_notifier.h
@@ -8,6 +8,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/time/time.h"
+#include "chromeos/dbus/power/power_manager_client.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/device/public/mojom/fingerprint.mojom.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
@@ -27,7 +28,8 @@
 class UI_CHROMEOS_EXPORT UserActivityPowerManagerNotifier
     : public InputDeviceEventObserver,
       public UserActivityObserver,
-      public device::mojom::FingerprintObserver {
+      public device::mojom::FingerprintObserver,
+      public chromeos::PowerManagerClient::Observer {
  public:
   // Registers and unregisters itself as an observer of |detector| on
   // construction and destruction.
@@ -52,6 +54,10 @@
                         bool enroll_session_complete,
                         int percent_complete) override;
 
+  // chromeos::PowerManagerClient::Observer:
+  void SuspendImminent(power_manager::SuspendImminent::Reason reason) override;
+  void SuspendDone(const base::TimeDelta& sleep_duration) override;
+
  private:
   // Notifies power manager that the user is active and activity type. No-op if
   // it is within 5 seconds from |last_notify_time_|.
@@ -67,6 +73,10 @@
   // Last time that the power manager was notified.
   base::TimeTicks last_notify_time_;
 
+  // True after SuspendImminent has been received and when SuspendDone has not
+  // been received.
+  bool suspending_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(UserActivityPowerManagerNotifier);
 };
 
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js
index e7983f4..9262ac1 100644
--- a/ui/file_manager/file_manager/common/js/util.js
+++ b/ui/file_manager/file_manager/common/js/util.js
@@ -1166,6 +1166,50 @@
 };
 
 /**
+ * Returns true if specified entry is a special entry such as MyFiles/Downloads,
+ * MyFiles/PluginVm or Linux files root which cannot be modified such as
+ * deleted/cut or renamed.
+ *
+ * @param {!VolumeManager} volumeManager
+ * @param {(Entry|FakeEntry)} entry Entry or a fake entry.
+ * @return {boolean}
+ */
+util.isNonModifiable = (volumeManager, entry) => {
+  if (!entry) {
+    return false;
+  }
+  if (util.isFakeEntry(entry)) {
+    return true;
+  }
+
+  // If the entry is not a valid entry.
+  if (!volumeManager) {
+    return false;
+  }
+
+  const volumeInfo = volumeManager.getVolumeInfo(entry);
+  if (!volumeInfo) {
+    return false;
+  }
+
+  if (volumeInfo.volumeType === VolumeManagerCommon.RootType.DOWNLOADS) {
+    if (util.isMyFilesVolumeEnabled() && entry.fullPath === '/Downloads') {
+      return true;
+    }
+    if (util.isPluginVmEnabled() && entry.fullPath === '/PluginVm') {
+      return true;
+    }
+  }
+
+  if (volumeInfo.volumeType === VolumeManagerCommon.RootType.CROSTINI &&
+      entry.fullPath === '/') {
+    return true;
+  }
+
+  return false;
+};
+
+/**
  * Checks if the specified set of allowed effects contains the given effect.
  * See: http://www.w3.org/TR/html5/editing.html#the-datatransfer-interface
  *
diff --git a/ui/file_manager/file_manager/foreground/js/dialog_action_controller.js b/ui/file_manager/file_manager/foreground/js/dialog_action_controller.js
index be393f1..858a44c 100644
--- a/ui/file_manager/file_manager/foreground/js/dialog_action_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/dialog_action_controller.js
@@ -5,509 +5,477 @@
 /**
  * Controler for handling behaviors of the Files app opened as a file/folder
  * selection dialog.
- *
- * @param {DialogType} dialogType Dialog type.
- * @param {!DialogFooter} dialogFooter Dialog footer.
- * @param {!DirectoryModel} directoryModel Directory model.
- * @param {!MetadataModel} metadataModel Metadata cache.
- * @param {!VolumeManager} volumeManager Volume manager.
- * @param {!FileFilter} fileFilter File filter model.
- * @param {!NamingController} namingController Naming controller.
- * @param {!FileSelectionHandler} fileSelectionHandler Initial file selection.
- * @param {!LaunchParam} launchParam Whether the dialog should return local
- *     path or not.
- * @constructor
- * @struct
  */
-function DialogActionController(
-    dialogType, dialogFooter, directoryModel, metadataModel, volumeManager,
-    fileFilter, namingController, fileSelectionHandler, launchParam) {
+class DialogActionController {
   /**
-   * @type {!DialogType}
-   * @const
-   * @private
+   * @param {!DialogType} dialogType Dialog type.
+   * @param {!DialogFooter} dialogFooter Dialog footer.
+   * @param {!DirectoryModel} directoryModel Directory model.
+   * @param {!MetadataModel} metadataModel Metadata cache.
+   * @param {!VolumeManager} volumeManager Volume manager.
+   * @param {!FileFilter} fileFilter File filter model.
+   * @param {!NamingController} namingController Naming controller.
+   * @param {!FileSelectionHandler} fileSelectionHandler Initial file selection.
+   * @param {!LaunchParam} launchParam Whether the dialog should return local
+   *     path or not.
    */
-  this.dialogType_ = dialogType;
+  constructor(
+      dialogType, dialogFooter, directoryModel, metadataModel, volumeManager,
+      fileFilter, namingController, fileSelectionHandler, launchParam) {
+    /** @private @const {!DialogType} */
+    this.dialogType_ = dialogType;
 
-  /**
-   * @type {!DialogFooter}
-   * @const
-   * @private
-   */
-  this.dialogFooter_ = dialogFooter;
+    /** @private @const {!DialogFooter} */
+    this.dialogFooter_ = dialogFooter;
 
-  /**
-   * @type {!DirectoryModel}
-   * @const
-   * @private
-   */
-  this.directoryModel_ = directoryModel;
+    /** @private @const {!DirectoryModel} */
+    this.directoryModel_ = directoryModel;
 
-  /**
-   * @type {!MetadataModel}
-   * @const
-   * @private
-   */
-  this.metadataModel_ = metadataModel;
+    /** @private @const {!MetadataModel} */
+    this.metadataModel_ = metadataModel;
 
-  /**
-   * @type {!VolumeManager}
-   * @const
-   * @private
-   */
-  this.volumeManager_ = volumeManager;
+    /** @private @const {!VolumeManager} */
+    this.volumeManager_ = volumeManager;
 
-  /**
-   * @type {!FileFilter}
-   * @const
-   * @private
-   */
-  this.fileFilter_ = fileFilter;
+    /** @private @const {!FileFilter} */
+    this.fileFilter_ = fileFilter;
 
-  /**
-   * @type {!NamingController}
-   * @const
-   * @private
-   */
-  this.namingController_ = namingController;
+    /** @private @const {!NamingController} */
+    this.namingController_ = namingController;
 
-  /**
-   * @type {!FileSelectionHandler}
-   * @private
-   * @const
-   */
-  this.fileSelectionHandler_ = fileSelectionHandler;
+    /** @private @const {!FileSelectionHandler} */
+    this.fileSelectionHandler_ = fileSelectionHandler;
 
-  /**
-   * List of acceptable file types for open dialog.
-   * @type {!Array<Object>}
-   * @const
-   * @private
-   */
-  this.fileTypes_ = launchParam.typeList || [];
+    /**
+     * List of acceptable file types for open dialog.
+     * @private @const {!Array<Object>}
+     */
+    this.fileTypes_ = launchParam.typeList || [];
 
-  /**
-   * @type {!AllowedPaths}
-   * @const
-   * @private
-   */
-  this.allowedPaths_ = launchParam.allowedPaths;
+    /** @private @const {!AllowedPaths} */
+    this.allowedPaths_ = launchParam.allowedPaths;
 
-  /**
-   * Bound function for onCancel_.
-   * @type {!function(this:DialogActionController, Event)}
-   * @private
-   */
-  this.onCancelBound_ = this.processCancelAction_.bind(this);
+    /**
+     * Bound function for onCancel_.
+     * @private @const {!function(this:DialogActionController, Event)}
+     */
+    this.onCancelBound_ = this.processCancelAction_.bind(this);
 
-  dialogFooter.okButton.addEventListener(
-      'click', this.processOKAction_.bind(this));
-  dialogFooter.cancelButton.addEventListener('click', this.onCancelBound_);
-  dialogFooter.newFolderButton.addEventListener(
-      'click', this.processNewFolderAction_.bind(this));
-  dialogFooter.fileTypeSelector.addEventListener(
-      'change', this.onFileTypeFilterChanged_.bind(this));
-  dialogFooter.filenameInput.addEventListener(
-      'input', this.updateOkButton_.bind(this));
-  fileSelectionHandler.addEventListener(
-      FileSelectionHandler.EventType.CHANGE_THROTTLED,
-      this.onFileSelectionChanged_.bind(this));
-  volumeManager.addEventListener(
-      'drive-connection-changed', this.updateOkButton_.bind(this));
+    dialogFooter.okButton.addEventListener(
+        'click', this.processOKAction_.bind(this));
+    dialogFooter.cancelButton.addEventListener('click', this.onCancelBound_);
+    dialogFooter.newFolderButton.addEventListener(
+        'click', this.processNewFolderAction_.bind(this));
+    dialogFooter.fileTypeSelector.addEventListener(
+        'change', this.onFileTypeFilterChanged_.bind(this));
+    dialogFooter.filenameInput.addEventListener(
+        'input', this.updateOkButton_.bind(this));
+    fileSelectionHandler.addEventListener(
+        FileSelectionHandler.EventType.CHANGE_THROTTLED,
+        this.onFileSelectionChanged_.bind(this));
+    volumeManager.addEventListener(
+        'drive-connection-changed', this.updateOkButton_.bind(this));
 
-  dialogFooter.initFileTypeFilter(this.fileTypes_, launchParam.includeAllFiles);
-  this.onFileTypeFilterChanged_();
+    dialogFooter.initFileTypeFilter(
+        this.fileTypes_, launchParam.includeAllFiles);
+    this.onFileTypeFilterChanged_();
 
-  this.newFolderCommand_ =
-      /** @type {cr.ui.Command} */ ($('new-folder'));
-  this.newFolderCommand_.addEventListener(
-      'disabledChange', this.updateNewFolderButton_.bind(this));
-}
-
-/**
- * @private
- */
-DialogActionController.prototype.processOKActionForSaveDialog_ = function() {
-  const selection = this.fileSelectionHandler_.selection;
-
-  // If OK action is clicked when a directory is selected, open the directory.
-  if (selection.directoryCount === 1 && selection.fileCount === 0) {
-    this.directoryModel_.changeDirectoryEntry(
-        /** @type {!DirectoryEntry} */ (selection.entries[0]));
-    return;
+    this.newFolderCommand_ =
+        /** @type {cr.ui.Command} */ ($('new-folder'));
+    this.newFolderCommand_.addEventListener(
+        'disabledChange', this.updateNewFolderButton_.bind(this));
   }
 
-  // Save-as doesn't require a valid selection from the list, since
-  // we're going to take the filename from the text input.
-  const filename = this.dialogFooter_.filenameInput.value;
-  if (!filename) {
-    throw new Error('Missing filename!');
-  }
+  /**
+   * @private
+   */
+  processOKActionForSaveDialog_() {
+    const selection = this.fileSelectionHandler_.selection;
 
-  this.namingController_.validateFileNameForSaving(filename)
-      .then(url => {
-        // TODO(mtomasz): Clean this up by avoiding constructing a URL
-        //                via string concatenation.
-        this.selectFilesAndClose_({
-          urls: [url],
-          multiple: false,
-          filterIndex: this.dialogFooter_.selectedFilterIndex
+    // If OK action is clicked when a directory is selected, open the directory.
+    if (selection.directoryCount === 1 && selection.fileCount === 0) {
+      this.directoryModel_.changeDirectoryEntry(
+          /** @type {!DirectoryEntry} */ (selection.entries[0]));
+      return;
+    }
+
+    // Save-as doesn't require a valid selection from the list, since
+    // we're going to take the filename from the text input.
+    const filename = this.dialogFooter_.filenameInput.value;
+    if (!filename) {
+      throw new Error('Missing filename!');
+    }
+
+    this.namingController_.validateFileNameForSaving(filename)
+        .then(url => {
+          // TODO(mtomasz): Clean this up by avoiding constructing a URL
+          //                via string concatenation.
+          this.selectFilesAndClose_({
+            urls: [url],
+            multiple: false,
+            filterIndex: this.dialogFooter_.selectedFilterIndex
+          });
+        })
+        .catch(error => {
+          if (error instanceof Error) {
+            console.error(error.stack && error);
+          }
         });
-      })
-      .catch(error => {
-        if (error instanceof Error) {
-          console.error(error.stack && error);
-        }
-      });
-};
-
-/**
- * Handle a click of the ok button.
- *
- * The ok button has different UI labels depending on the type of dialog, but
- * in code it's always referred to as 'ok'.
- *
- * @private
- */
-DialogActionController.prototype.processOKAction_ = function() {
-  if (this.dialogFooter_.okButton.disabled) {
-    throw new Error('Disabled!');
-  }
-  if (this.dialogType_ === DialogType.SELECT_SAVEAS_FILE) {
-    this.processOKActionForSaveDialog_();
-    return;
   }
 
-  const files = [];
-  const selectedIndexes =
-      this.directoryModel_.getFileListSelection().selectedIndexes;
+  /**
+   * Handle a click of the ok button.
+   *
+   * The ok button has different UI labels depending on the type of dialog, but
+   * in code it's always referred to as 'ok'.
+   *
+   * @private
+   */
+  processOKAction_() {
+    if (this.dialogFooter_.okButton.disabled) {
+      throw new Error('Disabled!');
+    }
+    if (this.dialogType_ === DialogType.SELECT_SAVEAS_FILE) {
+      this.processOKActionForSaveDialog_();
+      return;
+    }
 
-  if (DialogType.isFolderDialog(this.dialogType_) &&
-      selectedIndexes.length === 0) {
-    const url = this.directoryModel_.getCurrentDirEntry().toURL();
+    const files = [];
+    const selectedIndexes =
+        this.directoryModel_.getFileListSelection().selectedIndexes;
+
+    if (DialogType.isFolderDialog(this.dialogType_) &&
+        selectedIndexes.length === 0) {
+      const url = this.directoryModel_.getCurrentDirEntry().toURL();
+      const singleSelection = {
+        urls: [url],
+        multiple: false,
+        filterIndex: this.dialogFooter_.selectedFilterIndex
+      };
+      this.selectFilesAndClose_(singleSelection);
+      return;
+    }
+
+    // All other dialog types require at least one selected list item.
+    // The logic to control whether or not the ok button is enabled should
+    // prevent us from ever getting here, but we sanity check to be sure.
+    if (!selectedIndexes.length) {
+      throw new Error('Nothing selected!');
+    }
+
+    const dm = this.directoryModel_.getFileList();
+    for (let i = 0; i < selectedIndexes.length; i++) {
+      const entry = dm.item(selectedIndexes[i]);
+      if (!entry) {
+        console.error('Error locating selected file at index: ' + i);
+        continue;
+      }
+
+      files.push(entry.toURL());
+    }
+
+    // Multi-file selection has no other restrictions.
+    if (this.dialogType_ === DialogType.SELECT_OPEN_MULTI_FILE) {
+      const multipleSelection = {
+        urls: files,
+        multiple: true,
+      };
+      this.selectFilesAndClose_(multipleSelection);
+      return;
+    }
+
+    // Everything else must have exactly one.
+    if (files.length > 1) {
+      throw new Error('Too many files selected!');
+    }
+
+    const selectedEntry = dm.item(selectedIndexes[0]);
+
+    if (DialogType.isFolderDialog(this.dialogType_)) {
+      if (!selectedEntry.isDirectory) {
+        throw new Error('Selected entry is not a folder!');
+      }
+    } else if (this.dialogType_ === DialogType.SELECT_OPEN_FILE) {
+      if (!selectedEntry.isFile) {
+        throw new Error('Selected entry is not a file!');
+      }
+    }
+
     const singleSelection = {
-      urls: [url],
+      urls: [files[0]],
       multiple: false,
       filterIndex: this.dialogFooter_.selectedFilterIndex
     };
     this.selectFilesAndClose_(singleSelection);
-    return;
   }
 
-  // All other dialog types require at least one selected list item.
-  // The logic to control whether or not the ok button is enabled should
-  // prevent us from ever getting here, but we sanity check to be sure.
-  if (!selectedIndexes.length) {
-    throw new Error('Nothing selected!');
+  /**
+   * Cancels file selection and closes the file selection dialog.
+   * @private
+   */
+  processCancelAction_() {
+    chrome.fileManagerPrivate.cancelDialog();
+    window.close();
   }
 
-  const dm = this.directoryModel_.getFileList();
-  for (let i = 0; i < selectedIndexes.length; i++) {
-    const entry = dm.item(selectedIndexes[i]);
-    if (!entry) {
-      console.error('Error locating selected file at index: ' + i);
-      continue;
-    }
-
-    files.push(entry.toURL());
+  /**
+   * Creates a new folder using new-folder command.
+   * @private
+   */
+  processNewFolderAction_() {
+    this.newFolderCommand_.canExecuteChange(this.dialogFooter_.newFolderButton);
+    this.newFolderCommand_.execute(this.dialogFooter_.newFolderButton);
   }
 
-  // Multi-file selection has no other restrictions.
-  if (this.dialogType_ === DialogType.SELECT_OPEN_MULTI_FILE) {
-    const multipleSelection = {
-      urls: files,
-      multiple: true,
-    };
-    this.selectFilesAndClose_(multipleSelection);
-    return;
+  /**
+   * Handles disabledChange event to update the new-folder button's
+   * avaliability.
+   * @private
+   */
+  updateNewFolderButton_() {
+    this.dialogFooter_.newFolderButton.disabled =
+        this.newFolderCommand_.disabled;
   }
 
-  // Everything else must have exactly one.
-  if (files.length > 1) {
-    throw new Error('Too many files selected!');
-  }
-
-  const selectedEntry = dm.item(selectedIndexes[0]);
-
-  if (DialogType.isFolderDialog(this.dialogType_)) {
-    if (!selectedEntry.isDirectory) {
-      throw new Error('Selected entry is not a folder!');
-    }
-  } else if (this.dialogType_ === DialogType.SELECT_OPEN_FILE) {
-    if (!selectedEntry.isFile) {
-      throw new Error('Selected entry is not a file!');
-    }
-  }
-
-  const singleSelection = {
-    urls: [files[0]],
-    multiple: false,
-    filterIndex: this.dialogFooter_.selectedFilterIndex
-  };
-  this.selectFilesAndClose_(singleSelection);
-};
-
-/**
- * Cancels file selection and closes the file selection dialog.
- * @private
- */
-DialogActionController.prototype.processCancelAction_ = () => {
-  chrome.fileManagerPrivate.cancelDialog();
-  window.close();
-};
-
-/**
- * Creates a new folder using new-folder command.
- * @private
- */
-DialogActionController.prototype.processNewFolderAction_ = function() {
-  this.newFolderCommand_.canExecuteChange(this.dialogFooter_.newFolderButton);
-  this.newFolderCommand_.execute(this.dialogFooter_.newFolderButton);
-};
-
-/**
- * Handles disabledChange event to update the new-folder button's avaliability.
- * @private
- */
-DialogActionController.prototype.updateNewFolderButton_ = function() {
-  this.dialogFooter_.newFolderButton.disabled = this.newFolderCommand_.disabled;
-};
-
-/**
- * Tries to close this modal dialog with some files selected.
- * Performs preprocessing if needed (e.g. for Drive).
- * @param {Object} selection Contains urls, filterIndex and multiple fields.
- * @private
- */
-DialogActionController.prototype.selectFilesAndClose_ = function(selection) {
-  const currentRootType = this.directoryModel_.getCurrentRootType();
-  const callSelectFilesApiAndClose = callback => {
-    const onFileSelected = () => {
-      callback();
-      if (!chrome.runtime.lastError) {
-        // Call next method on a timeout, as it's unsafe to
-        // close a window from a callback.
-        setTimeout(window.close.bind(window), 0);
+  /**
+   * Tries to close this modal dialog with some files selected.
+   * Performs preprocessing if needed (e.g. for Drive).
+   * @param {Object} selection Contains urls, filterIndex and multiple fields.
+   * @private
+   */
+  selectFilesAndClose_(selection) {
+    const currentRootType = this.directoryModel_.getCurrentRootType();
+    const callSelectFilesApiAndClose = callback => {
+      const onFileSelected = () => {
+        callback();
+        if (!chrome.runtime.lastError) {
+          // Call next method on a timeout, as it's unsafe to
+          // close a window from a callback.
+          setTimeout(window.close.bind(window), 0);
+        }
+      };
+      // Record the root types of chosen files in OPEN dialog.
+      if (this.dialogType_ == DialogType.SELECT_OPEN_FILE ||
+          this.dialogType_ == DialogType.SELECT_OPEN_MULTI_FILE) {
+        metrics.recordEnum(
+            'OpenFiles.RootType', currentRootType,
+            VolumeManagerCommon.RootTypesForUMA);
+      }
+      if (selection.multiple) {
+        chrome.fileManagerPrivate.selectFiles(
+            selection.urls, this.allowedPaths_ === AllowedPaths.NATIVE_PATH,
+            onFileSelected);
+      } else {
+        chrome.fileManagerPrivate.selectFile(
+            selection.urls[0], selection.filterIndex,
+            this.dialogType_ !==
+                DialogType.SELECT_SAVEAS_FILE /* for opening */,
+            this.allowedPaths_ === AllowedPaths.NATIVE_PATH, onFileSelected);
       }
     };
-    // Record the root types of chosen files in OPEN dialog.
-    if (this.dialogType_ == DialogType.SELECT_OPEN_FILE ||
-        this.dialogType_ == DialogType.SELECT_OPEN_MULTI_FILE) {
-      metrics.recordEnum(
-          'OpenFiles.RootType', currentRootType,
-          VolumeManagerCommon.RootTypesForUMA);
-    }
-    if (selection.multiple) {
-      chrome.fileManagerPrivate.selectFiles(
-          selection.urls, this.allowedPaths_ === AllowedPaths.NATIVE_PATH,
-          onFileSelected);
-    } else {
-      chrome.fileManagerPrivate.selectFile(
-          selection.urls[0], selection.filterIndex,
-          this.dialogType_ !== DialogType.SELECT_SAVEAS_FILE /* for opening */,
-          this.allowedPaths_ === AllowedPaths.NATIVE_PATH, onFileSelected);
-    }
-  };
 
-  if (currentRootType !== VolumeManagerCommon.VolumeType.DRIVE ||
-      this.dialogType_ === DialogType.SELECT_SAVEAS_FILE) {
-    callSelectFilesApiAndClose(() => {});
-    return;
-  }
-
-  const shade = document.createElement('div');
-  shade.className = 'shade';
-  const footer = this.dialogFooter_.element;
-  const progress = footer.querySelector('.progress-track');
-  progress.style.width = '0%';
-  const cancelled = false;
-
-  const progressMap = {};
-  let filesStarted = 0;
-  let filesTotal = selection.urls.length;
-  for (let index = 0; index < selection.urls.length; index++) {
-    progressMap[selection.urls[index]] = -1;
-  }
-  let lastPercent = 0;
-  let bytesTotal = 0;
-  let bytesDone = 0;
-
-  const onFileTransfersUpdated = status => {
-    if (!(status.fileUrl in progressMap)) {
-      return;
-    }
-    if (status.total === -1) {
+    if (currentRootType !== VolumeManagerCommon.VolumeType.DRIVE ||
+        this.dialogType_ === DialogType.SELECT_SAVEAS_FILE) {
+      callSelectFilesApiAndClose(() => {});
       return;
     }
 
-    let old = progressMap[status.fileUrl];
-    if (old === -1) {
-      // -1 means we don't know file size yet.
-      bytesTotal += status.total;
-      filesStarted++;
-      old = 0;
+    const shade = document.createElement('div');
+    shade.className = 'shade';
+    const footer = this.dialogFooter_.element;
+    const progress = footer.querySelector('.progress-track');
+    progress.style.width = '0%';
+    const cancelled = false;
+
+    const progressMap = {};
+    let filesStarted = 0;
+    let filesTotal = selection.urls.length;
+    for (let index = 0; index < selection.urls.length; index++) {
+      progressMap[selection.urls[index]] = -1;
     }
-    bytesDone += status.processed - old;
-    progressMap[status.fileUrl] = status.processed;
+    let lastPercent = 0;
+    let bytesTotal = 0;
+    let bytesDone = 0;
 
-    let percent = bytesTotal === 0 ? 0 : bytesDone / bytesTotal;
-    // For files we don't have information about, assume the progress is zero.
-    percent = percent * filesStarted / filesTotal * 100;
-    // Do not decrease the progress. This may happen, if first downloaded
-    // file is small, and the second one is large.
-    lastPercent = Math.max(lastPercent, percent);
-    progress.style.width = lastPercent + '%';
-  };
+    const onFileTransfersUpdated = status => {
+      if (!(status.fileUrl in progressMap)) {
+        return;
+      }
+      if (status.total === -1) {
+        return;
+      }
 
-  const setup = () => {
-    document.querySelector('.dialog-container').appendChild(shade);
-    setTimeout(() => {
-      shade.setAttribute('fadein', 'fadein');
-    }, 100);
-    footer.setAttribute('progress', 'progress');
-    this.dialogFooter_.cancelButton.removeEventListener(
-        'click', this.onCancelBound_);
-    this.dialogFooter_.cancelButton.addEventListener('click', onCancel);
-    chrome.fileManagerPrivate.onFileTransfersUpdated.addListener(
-        onFileTransfersUpdated);
-  };
+      let old = progressMap[status.fileUrl];
+      if (old === -1) {
+        // -1 means we don't know file size yet.
+        bytesTotal += status.total;
+        filesStarted++;
+        old = 0;
+      }
+      bytesDone += status.processed - old;
+      progressMap[status.fileUrl] = status.processed;
 
-  const cleanup = () => {
-    shade.parentNode.removeChild(shade);
-    footer.removeAttribute('progress');
-    this.dialogFooter_.cancelButton.removeEventListener('click', onCancel);
-    this.dialogFooter_.cancelButton.addEventListener(
-        'click', this.onCancelBound_);
-    chrome.fileManagerPrivate.onFileTransfersUpdated.removeListener(
-        onFileTransfersUpdated);
-  };
+      let percent = bytesTotal === 0 ? 0 : bytesDone / bytesTotal;
+      // For files we don't have information about, assume the progress is zero.
+      percent = percent * filesStarted / filesTotal * 100;
+      // Do not decrease the progress. This may happen, if first downloaded
+      // file is small, and the second one is large.
+      lastPercent = Math.max(lastPercent, percent);
+      progress.style.width = lastPercent + '%';
+    };
 
-  const onCancel = () => {
-    // According to API cancel may fail, but there is no proper UI to reflect
-    // this. So, we just silently assume that everything is cancelled.
-    util.URLsToEntries(selection.urls).then(entries => {
-      chrome.fileManagerPrivate.cancelFileTransfers(
-          entries, util.checkAPIError);
+    const setup = () => {
+      document.querySelector('.dialog-container').appendChild(shade);
+      setTimeout(() => {
+        shade.setAttribute('fadein', 'fadein');
+      }, 100);
+      footer.setAttribute('progress', 'progress');
+      this.dialogFooter_.cancelButton.removeEventListener(
+          'click', this.onCancelBound_);
+      this.dialogFooter_.cancelButton.addEventListener('click', onCancel);
+      chrome.fileManagerPrivate.onFileTransfersUpdated.addListener(
+          onFileTransfersUpdated);
+    };
+
+    const cleanup = () => {
+      shade.parentNode.removeChild(shade);
+      footer.removeAttribute('progress');
+      this.dialogFooter_.cancelButton.removeEventListener('click', onCancel);
+      this.dialogFooter_.cancelButton.addEventListener(
+          'click', this.onCancelBound_);
+      chrome.fileManagerPrivate.onFileTransfersUpdated.removeListener(
+          onFileTransfersUpdated);
+    };
+
+    const onCancel = () => {
+      // According to API cancel may fail, but there is no proper UI to reflect
+      // this. So, we just silently assume that everything is cancelled.
+      util.URLsToEntries(selection.urls).then(entries => {
+        chrome.fileManagerPrivate.cancelFileTransfers(
+            entries, util.checkAPIError);
+      });
+    };
+
+    const onProperties = properties => {
+      for (let i = 0; i < properties.length; i++) {
+        if (properties[i].present) {
+          // For files already in GCache, we don't get any transfer updates.
+          filesTotal--;
+        }
+      }
+      callSelectFilesApiAndClose(cleanup);
+    };
+
+    setup();
+
+    // TODO(mtomasz): Use Entry instead of URLs, if possible.
+    util.URLsToEntries(selection.urls, entries => {
+      this.metadataModel_.get(entries, ['present']).then(onProperties);
     });
-  };
+  }
 
-  const onProperties = properties => {
-    for (let i = 0; i < properties.length; i++) {
-      if (properties[i].present) {
-        // For files already in GCache, we don't get any transfer updates.
-        filesTotal--;
-      }
-    }
-    callSelectFilesApiAndClose(cleanup);
-  };
+  /**
+   * Filters file according to the selected file type.
+   * @private
+   */
+  onFileTypeFilterChanged_() {
+    this.fileFilter_.removeFilter('fileType');
+    const selectedIndex = this.dialogFooter_.selectedFilterIndex;
+    if (selectedIndex > 0) {  // Specific filter selected.
+      const regexp = new RegExp(
+          '\\.(' + this.fileTypes_[selectedIndex - 1].extensions.join('|') +
+              ')$',
+          'i');
+      const filter = entry => {
+        return entry.isDirectory || regexp.test(entry.name);
+      };
+      this.fileFilter_.addFilter('fileType', filter);
 
-  setup();
-
-  // TODO(mtomasz): Use Entry instead of URLs, if possible.
-  util.URLsToEntries(selection.urls, entries => {
-    this.metadataModel_.get(entries, ['present']).then(onProperties);
-  });
-};
-
-/**
- * Filters file according to the selected file type.
- * @private
- */
-DialogActionController.prototype.onFileTypeFilterChanged_ = function() {
-  this.fileFilter_.removeFilter('fileType');
-  const selectedIndex = this.dialogFooter_.selectedFilterIndex;
-  if (selectedIndex > 0) {  // Specific filter selected.
-    const regexp = new RegExp(
-        '\\.(' + this.fileTypes_[selectedIndex - 1].extensions.join('|') + ')$',
-        'i');
-    const filter = entry => {
-      return entry.isDirectory || regexp.test(entry.name);
-    };
-    this.fileFilter_.addFilter('fileType', filter);
-
-    // In save dialog, update the destination name extension.
-    if (this.dialogType_ === DialogType.SELECT_SAVEAS_FILE) {
-      const current = this.dialogFooter_.filenameInput.value;
-      const newExt = this.fileTypes_[selectedIndex - 1].extensions[0];
-      if (newExt && !regexp.test(current)) {
-        const i = current.lastIndexOf('.');
-        if (i >= 0) {
-          this.dialogFooter_.filenameInput.value =
-              current.substr(0, i) + '.' + newExt;
-          this.dialogFooter_.selectTargetNameInFilenameInput();
+      // In save dialog, update the destination name extension.
+      if (this.dialogType_ === DialogType.SELECT_SAVEAS_FILE) {
+        const current = this.dialogFooter_.filenameInput.value;
+        const newExt = this.fileTypes_[selectedIndex - 1].extensions[0];
+        if (newExt && !regexp.test(current)) {
+          const i = current.lastIndexOf('.');
+          if (i >= 0) {
+            this.dialogFooter_.filenameInput.value =
+                current.substr(0, i) + '.' + newExt;
+            this.dialogFooter_.selectTargetNameInFilenameInput();
+          }
         }
       }
     }
   }
-};
 
-/**
- * Handles selection change.
- *
- * @private
- */
-DialogActionController.prototype.onFileSelectionChanged_ = function() {
-  // If this is a save-as dialog, copy the selected file into the filename
-  // input text box.
-  const selection = this.fileSelectionHandler_.selection;
-  if (this.dialogType_ === DialogType.SELECT_SAVEAS_FILE &&
-      selection.totalCount === 1 && selection.entries[0].isFile &&
-      this.dialogFooter_.filenameInput.value !== selection.entries[0].name) {
-    this.dialogFooter_.filenameInput.value = selection.entries[0].name;
-  }
-
-  this.updateOkButton_();
-  if (!this.dialogFooter_.okButton.disabled) {
-    util.testSendMessage('dialog-ready');
-  }
-};
-
-/**
- * Updates the Ok button enabled state.
- * @private
- */
-DialogActionController.prototype.updateOkButton_ = function() {
-  const selection = this.fileSelectionHandler_.selection;
-
-  if (this.dialogType_ === DialogType.FULL_PAGE) {
-    // No "select" buttons on the full page UI.
-    this.dialogFooter_.okButton.disabled = false;
-    return;
-  }
-
-  if (DialogType.isFolderDialog(this.dialogType_)) {
-    // In SELECT_FOLDER mode, we allow to select current directory
-    // when nothing is selected.
-    this.dialogFooter_.okButton.disabled =
-        selection.directoryCount > 1 || selection.fileCount !== 0;
-    return;
-  }
-
-  if (this.dialogType_ === DialogType.SELECT_SAVEAS_FILE) {
-    if (selection.directoryCount === 1 && selection.fileCount === 0) {
-      this.dialogFooter_.okButtonLabel.textContent = str('OPEN_LABEL');
-      this.dialogFooter_.okButton.disabled = false;
-    } else {
-      this.dialogFooter_.okButtonLabel.textContent = str('SAVE_LABEL');
-      this.dialogFooter_.okButton.disabled =
-          this.directoryModel_.isReadOnly() ||
-          !this.dialogFooter_.filenameInput.value ||
-          !this.fileSelectionHandler_.isAvailable();
+  /**
+   * Handles selection change.
+   * @private
+   */
+  onFileSelectionChanged_() {
+    // If this is a save-as dialog, copy the selected file into the filename
+    // input text box.
+    const selection = this.fileSelectionHandler_.selection;
+    if (this.dialogType_ === DialogType.SELECT_SAVEAS_FILE &&
+        selection.totalCount === 1 && selection.entries[0].isFile &&
+        this.dialogFooter_.filenameInput.value !== selection.entries[0].name) {
+      this.dialogFooter_.filenameInput.value = selection.entries[0].name;
     }
-    return;
+
+    this.updateOkButton_();
+    if (!this.dialogFooter_.okButton.disabled) {
+      util.testSendMessage('dialog-ready');
+    }
   }
 
-  if (this.dialogType_ === DialogType.SELECT_OPEN_FILE) {
-    this.dialogFooter_.okButton.disabled = selection.directoryCount !== 0 ||
-        selection.fileCount !== 1 || !this.fileSelectionHandler_.isAvailable();
-    return;
-  }
+  /**
+   * Updates the Ok button enabled state.
+   * @private
+   */
+  updateOkButton_() {
+    const selection = this.fileSelectionHandler_.selection;
 
-  if (this.dialogType_ === DialogType.SELECT_OPEN_MULTI_FILE) {
-    this.dialogFooter_.okButton.disabled = selection.directoryCount !== 0 ||
-        selection.fileCount === 0 || !this.fileSelectionHandler_.isAvailable();
-    return;
-  }
+    if (this.dialogType_ === DialogType.FULL_PAGE) {
+      // No "select" buttons on the full page UI.
+      this.dialogFooter_.okButton.disabled = false;
+      return;
+    }
 
-  assertNotReached('Unknown dialog type.');
-};
+    if (DialogType.isFolderDialog(this.dialogType_)) {
+      // In SELECT_FOLDER mode, we allow to select current directory
+      // when nothing is selected.
+      this.dialogFooter_.okButton.disabled =
+          selection.directoryCount > 1 || selection.fileCount !== 0;
+      return;
+    }
+
+    if (this.dialogType_ === DialogType.SELECT_SAVEAS_FILE) {
+      if (selection.directoryCount === 1 && selection.fileCount === 0) {
+        this.dialogFooter_.okButtonLabel.textContent = str('OPEN_LABEL');
+        this.dialogFooter_.okButton.disabled = false;
+      } else {
+        this.dialogFooter_.okButtonLabel.textContent = str('SAVE_LABEL');
+        this.dialogFooter_.okButton.disabled =
+            this.directoryModel_.isReadOnly() ||
+            !this.dialogFooter_.filenameInput.value ||
+            !this.fileSelectionHandler_.isAvailable();
+      }
+      return;
+    }
+
+    if (this.dialogType_ === DialogType.SELECT_OPEN_FILE) {
+      this.dialogFooter_.okButton.disabled = selection.directoryCount !== 0 ||
+          selection.fileCount !== 1 ||
+          !this.fileSelectionHandler_.isAvailable();
+      return;
+    }
+
+    if (this.dialogType_ === DialogType.SELECT_OPEN_MULTI_FILE) {
+      this.dialogFooter_.okButton.disabled = selection.directoryCount !== 0 ||
+          selection.fileCount === 0 ||
+          !this.fileSelectionHandler_.isAvailable();
+      return;
+    }
+
+    assertNotReached('Unknown dialog type.');
+  }
+}
diff --git a/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.js b/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.js
index d28452b..405081ed 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.js
@@ -4,291 +4,273 @@
 
 /**
  * Naming controller for directory tree.
- * @param {!DirectoryModel} directoryModel
- * @param {!DirectoryTree} directoryTree
- * @param {!cr.ui.dialogs.AlertDialog} alertDialog
- * @constructor
- * @struct
  */
-function DirectoryTreeNamingController(
-    directoryModel, directoryTree, alertDialog) {
+class DirectoryTreeNamingController {
   /**
-   * @private {!DirectoryModel}
-   * @const
+   * @param {!DirectoryModel} directoryModel
+   * @param {!DirectoryTree} directoryTree
+   * @param {!cr.ui.dialogs.AlertDialog} alertDialog
    */
-  this.directoryModel_ = directoryModel;
+  constructor(directoryModel, directoryTree, alertDialog) {
+    /** @private @const {!DirectoryModel} */
+    this.directoryModel_ = directoryModel;
 
-  /**
-   * @private {!DirectoryTree}
-   * @const
-   */
-  this.directoryTree_ = directoryTree;
+    /** @private @const {!DirectoryTree} */
+    this.directoryTree_ = directoryTree;
 
-  /**
-   * @private {!cr.ui.dialogs.AlertDialog}
-   * @const
-   */
-  this.alertDialog_ = alertDialog;
+    /** @private @const {!cr.ui.dialogs.AlertDialog} */
+    this.alertDialog_ = alertDialog;
 
-  /**
-   * @private {DirectoryItem}
-   */
-  this.currentDirectoryItem_ = null;
+    /** @private {?DirectoryItem} */
+    this.currentDirectoryItem_ = null;
 
-  /**
-   * @private {boolean}
-   */
-  this.editting_ = false;
+    /** @private {boolean} */
+    this.editting_ = false;
 
-  /**
-   * @private {boolean}
-   */
-  this.isRemovableRoot_ = false;
+    /** @private {boolean} */
+    this.isRemovableRoot_ = false;
 
-  /**
-   * @private {VolumeInfo}
-   */
-  this.volumeInfo_ = null;
+    /** @private {?VolumeInfo} */
+    this.volumeInfo_ = null;
 
-  /**
-   * @private {!HTMLInputElement}
-   * @const
-   */
-  this.inputElement_ = /** @type {!HTMLInputElement} */
-      (document.createElement('input'));
-  this.inputElement_.type = 'text';
-  this.inputElement_.spellcheck = false;
-  this.inputElement_.addEventListener('keydown', this.onKeyDown_.bind(this));
-  this.inputElement_.addEventListener('blur', this.commitRename_.bind(this));
-  this.inputElement_.addEventListener('click', event => {
-    // Stop propagation of click event to prevent it being captured by directory
-    // item and current directory is changed to editing item.
-    event.stopPropagation();
-  });
-}
-
-/**
- * Returns input element.
- * @return {!HTMLInputElement}
- */
-DirectoryTreeNamingController.prototype.getInputElement = function() {
-  return this.inputElement_;
-};
-
-/**
- * Attaches naming controller to specified directory item and start rename.
- * @param {!DirectoryItem} directoryItem An html element of a node of the
- *     target.
- * @param {boolean} isRemovableRoot Indicates whether the target is removable
- *     node or not.
- * @param {VolumeInfo} volumeInfo A volume information about the target node.
- *     |volumeInfo| can be null if method is invoked on a folder that is in the
- *     tree view and is not root of an external drive.
- */
-DirectoryTreeNamingController.prototype.attachAndStart = function(
-    directoryItem, isRemovableRoot, volumeInfo) {
-  this.isRemovableRoot_ = isRemovableRoot;
-  this.volumeInfo_ = this.isRemovableRoot_ ? assert(volumeInfo) : null;
-
-  if (this.currentDirectoryItem_) {
-    return;
+    /** @private @const {!HTMLInputElement} */
+    this.inputElement_ = /** @type {!HTMLInputElement} */
+        (document.createElement('input'));
+    this.inputElement_.type = 'text';
+    this.inputElement_.spellcheck = false;
+    this.inputElement_.addEventListener('keydown', this.onKeyDown_.bind(this));
+    this.inputElement_.addEventListener('blur', this.commitRename_.bind(this));
+    this.inputElement_.addEventListener('click', event => {
+      // Stop propagation of click event to prevent it being captured by
+      // directory item and current directory is changed to editing item.
+      event.stopPropagation();
+    });
   }
 
-  this.currentDirectoryItem_ = directoryItem;
-  this.currentDirectoryItem_.setAttribute('renaming', true);
-
-  const renameInputElementPlaceholder =
-      this.currentDirectoryItem_.firstElementChild.getElementsByClassName(
-          'rename-placeholder');
-
-  if (this.isRemovableRoot_ && renameInputElementPlaceholder.length === 1) {
-    renameInputElementPlaceholder[0].appendChild(this.inputElement_);
-  } else {
-    this.currentDirectoryItem_.firstElementChild.appendChild(
-        this.inputElement_);
+  /**
+   * Returns input element.
+   * @return {!HTMLInputElement}
+   */
+  getInputElement() {
+    return this.inputElement_;
   }
 
-  this.inputElement_.value = this.currentDirectoryItem_.label;
-  this.inputElement_.select();
-  this.inputElement_.focus();
+  /**
+   * Attaches naming controller to specified directory item and start rename.
+   * @param {!DirectoryItem} directoryItem An html element of a node of the
+   *     target.
+   * @param {boolean} isRemovableRoot Indicates whether the target is removable
+   *     node or not.
+   * @param {VolumeInfo} volumeInfo A volume information about the target node.
+   *     |volumeInfo| can be null if method is invoked on a folder that is in
+   *     the tree view and is not root of an external drive.
+   */
+  attachAndStart(directoryItem, isRemovableRoot, volumeInfo) {
+    this.isRemovableRoot_ = isRemovableRoot;
+    this.volumeInfo_ = this.isRemovableRoot_ ? assert(volumeInfo) : null;
 
-  this.editting_ = true;
-};
+    if (this.currentDirectoryItem_) {
+      return;
+    }
 
-/**
- * Commits rename.
- * @private
- */
-DirectoryTreeNamingController.prototype.commitRename_ = function() {
-  if (!this.editting_) {
-    return;
-  }
-  this.editting_ = false;
+    this.currentDirectoryItem_ = directoryItem;
+    this.currentDirectoryItem_.setAttribute('renaming', true);
 
-  const entry = this.currentDirectoryItem_.entry;
-  const newName = this.inputElement_.value;
+    const renameInputElementPlaceholder =
+        this.currentDirectoryItem_.firstElementChild.getElementsByClassName(
+            'rename-placeholder');
 
-  // If new name is the same as current name or empty, do nothing.
-  if (newName === this.currentDirectoryItem_.label || newName.length == 0) {
-    this.detach_();
-    return;
+    if (this.isRemovableRoot_ && renameInputElementPlaceholder.length === 1) {
+      renameInputElementPlaceholder[0].appendChild(this.inputElement_);
+    } else {
+      this.currentDirectoryItem_.firstElementChild.appendChild(
+          this.inputElement_);
+    }
+
+    this.inputElement_.value = this.currentDirectoryItem_.label;
+    this.inputElement_.select();
+    this.inputElement_.focus();
+
+    this.editting_ = true;
   }
 
-  if (this.isRemovableRoot_) {
-    // Validate new name.
-    util.validateExternalDriveName(newName, assert(this.volumeInfo_))
+  /**
+   * Commits rename.
+   * @private
+   */
+  commitRename_() {
+    if (!this.editting_) {
+      return;
+    }
+    this.editting_ = false;
+
+    const entry = this.currentDirectoryItem_.entry;
+    const newName = this.inputElement_.value;
+
+    // If new name is the same as current name or empty, do nothing.
+    if (newName === this.currentDirectoryItem_.label || newName.length == 0) {
+      this.detach_();
+      return;
+    }
+
+    if (this.isRemovableRoot_) {
+      // Validate new name.
+      util.validateExternalDriveName(newName, assert(this.volumeInfo_))
+          .then(
+              this.performExternalDriveRename_.bind(this, entry, newName),
+              errorMessage => {
+                this.alertDialog_.show(
+                    /** @type {string} */ (errorMessage),
+                    this.detach_.bind(this));
+              });
+    } else {
+      // Validate new name.
+      new Promise(entry.getParent.bind(entry))
+          .then(parentEntry => {
+            return util.validateFileName(
+                parentEntry, newName,
+                !this.directoryModel_.getFileFilter().isHiddenFilesVisible());
+          })
+          .then(
+              this.performRename_.bind(this, entry, newName), errorMessage => {
+                this.alertDialog_.show(
+                    /** @type {string} */ (errorMessage),
+                    this.detach_.bind(this));
+              });
+    }
+  }
+
+  /**
+   * Performs rename operation.
+   * @param {!DirectoryEntry} entry
+   * @param {string} newName Validated name.
+   * @private
+   */
+  performRename_(entry, newName) {
+    const renamingCurrentDirectory =
+        util.isSameEntry(entry, this.directoryModel_.getCurrentDirEntry());
+    if (renamingCurrentDirectory) {
+      this.directoryModel_.setIgnoringCurrentDirectoryDeletion(
+          true /* ignore */);
+    }
+
+    // TODO(yawano): Rename might take time on some volumes. Optimistically show
+    // new name in the UI before actual rename is completed.
+    new Promise(util.rename.bind(null, entry, newName))
         .then(
-            this.performExternalDriveRename_.bind(this, entry, newName),
-            errorMessage => {
-              this.alertDialog_.show(
-                  /** @type {string} */ (errorMessage),
-                  this.detach_.bind(this));
+            newEntry => {
+              // Show new name before detaching input element to prevent showing
+              // old name.
+              const label =
+                  this.currentDirectoryItem_.firstElementChild.querySelector(
+                      '.label');
+              label.textContent = newName;
+
+              this.currentDirectoryItem_.entry = newEntry;
+              this.currentDirectoryItem_.updateSubDirectories(
+                  true /* recursive */);
+
+              this.detach_();
+
+              // If renamed directory was current directory, change it to new
+              // one.
+              if (renamingCurrentDirectory) {
+                this.directoryModel_.changeDirectoryEntry(
+                    newEntry,
+                    this.directoryModel_.setIgnoringCurrentDirectoryDeletion
+                        .bind(this.directoryModel_, false /* not ignore */));
+              }
+            },
+            error => {
+              this.directoryModel_.setIgnoringCurrentDirectoryDeletion(
+                  false /* not ignore*/);
+              this.detach_();
+
+              this.alertDialog_.show(util.getRenameErrorMessage(
+                  /** @type {DOMError} */ (error), entry, newName));
             });
-  } else {
-    // Validate new name.
-    new Promise(entry.getParent.bind(entry))
-        .then(parentEntry => {
-          return util.validateFileName(
-              parentEntry, newName,
-              !this.directoryModel_.getFileFilter().isHiddenFilesVisible());
-        })
-        .then(this.performRename_.bind(this, entry, newName), errorMessage => {
-          this.alertDialog_.show(
-              /** @type {string} */ (errorMessage), this.detach_.bind(this));
-        });
-  }
-};
-
-/**
- * Performs rename operation.
- * @param {!DirectoryEntry} entry
- * @param {string} newName Validated name.
- * @private
- */
-DirectoryTreeNamingController.prototype.performRename_ = function(
-    entry, newName) {
-  const renamingCurrentDirectory =
-      util.isSameEntry(entry, this.directoryModel_.getCurrentDirEntry());
-  if (renamingCurrentDirectory) {
-    this.directoryModel_.setIgnoringCurrentDirectoryDeletion(true /* ignore */);
   }
 
-  // TODO(yawano): Rename might take time on some volumes. Optimistically show
-  // new name in the UI before actual rename is completed.
-  new Promise(util.rename.bind(null, entry, newName))
-      .then(
-          newEntry => {
-            // Show new name before detaching input element to prevent showing
-            // old name.
-            const label =
-                this.currentDirectoryItem_.firstElementChild.querySelector(
-                    '.label');
-            label.textContent = newName;
+  /**
+   * Performs external drive rename operation.
+   * @param {!DirectoryEntry} entry
+   * @param {string} newName Validated name.
+   * @private
+   */
+  performExternalDriveRename_(entry, newName) {
+    // Invoke external drive rename
+    chrome.fileManagerPrivate.renameVolume(this.volumeInfo_.volumeId, newName);
+    // Show new name before detaching input element to prevent showing old
+    // name.
+    const label =
+        this.currentDirectoryItem_.firstElementChild.querySelector('.label');
+    label.textContent = newName;
 
-            this.currentDirectoryItem_.entry = newEntry;
-            this.currentDirectoryItem_.updateSubDirectories(
-                true /* recursive */);
-
-            this.detach_();
-
-            // If renamed directory was current directory, change it to new one.
-            if (renamingCurrentDirectory) {
-              this.directoryModel_.changeDirectoryEntry(
-                  newEntry,
-                  this.directoryModel_.setIgnoringCurrentDirectoryDeletion.bind(
-                      this.directoryModel_, false /* not ignore */));
-            }
-          },
-          error => {
-            this.directoryModel_.setIgnoringCurrentDirectoryDeletion(
-                false /* not ignore*/);
-            this.detach_();
-
-            this.alertDialog_.show(util.getRenameErrorMessage(
-                /** @type {DOMError} */ (error), entry, newName));
-          });
-};
-
-/**
- * Performs external drive rename operation.
- * @param {!DirectoryEntry} entry
- * @param {string} newName Validated name.
- * @private
- */
-DirectoryTreeNamingController.prototype.performExternalDriveRename_ = function(
-    entry, newName) {
-  // Invoke external drive rename
-  chrome.fileManagerPrivate.renameVolume(this.volumeInfo_.volumeId, newName);
-  // Show new name before detaching input element to prevent showing old
-  // name.
-  const label =
-      this.currentDirectoryItem_.firstElementChild.querySelector('.label');
-  label.textContent = newName;
-
-  this.detach_();
-};
-
-/**
- * Cancels rename.
- * @private
- */
-DirectoryTreeNamingController.prototype.cancelRename_ = function() {
-  if (!this.editting_) {
-    return;
-  }
-  this.editting_ = false;
-
-  this.detach_();
-};
-
-/**
- * Detaches controller from current directory item.
- * @private
- */
-DirectoryTreeNamingController.prototype.detach_ = function() {
-  assert(!!this.currentDirectoryItem_);
-
-  const renameInputElementPlaceholder =
-      this.currentDirectoryItem_.firstElementChild.getElementsByClassName(
-          'rename-placeholder');
-
-  if (this.isRemovableRoot_ && renameInputElementPlaceholder.length === 1) {
-    renameInputElementPlaceholder[0].removeChild(this.inputElement_);
-  } else {
-    this.currentDirectoryItem_.firstElementChild.removeChild(
-        this.inputElement_);
+    this.detach_();
   }
 
-  this.currentDirectoryItem_.removeAttribute('renaming');
-  this.currentDirectoryItem_ = null;
+  /**
+   * Cancels rename.
+   * @private
+   */
+  cancelRename_() {
+    if (!this.editting_) {
+      return;
+    }
+    this.editting_ = false;
 
-  // Restore focus to directory tree.
-  this.directoryTree_.focus();
-};
-
-/**
- * Handles keydown event.
- * @param {!Event} event
- * @private
- */
-DirectoryTreeNamingController.prototype.onKeyDown_ = function(event) {
-  // Ignore key events if event.keyCode is VK_PROCESSKEY(229).
-  // TODO(fukino): Remove this workaround once crbug.com/644140 is fixed.
-  if (event.keyCode === 229) {
-    return;
+    this.detach_();
   }
 
-  event.stopPropagation();
+  /**
+   * Detaches controller from current directory item.
+   * @private
+   */
+  detach_() {
+    assert(!!this.currentDirectoryItem_);
 
-  switch (util.getKeyModifiers(event) + event.key) {
-    case 'Escape':
-      this.cancelRename_();
-      event.preventDefault();
-      break;
+    const renameInputElementPlaceholder =
+        this.currentDirectoryItem_.firstElementChild.getElementsByClassName(
+            'rename-placeholder');
 
-    case 'Enter':
-      this.commitRename_();
-      event.preventDefault();
-      break;
+    if (this.isRemovableRoot_ && renameInputElementPlaceholder.length === 1) {
+      renameInputElementPlaceholder[0].removeChild(this.inputElement_);
+    } else {
+      this.currentDirectoryItem_.firstElementChild.removeChild(
+          this.inputElement_);
+    }
+
+    this.currentDirectoryItem_.removeAttribute('renaming');
+    this.currentDirectoryItem_ = null;
+
+    // Restore focus to directory tree.
+    this.directoryTree_.focus();
   }
-};
+
+  /**
+   * Handles keydown event.
+   * @param {!Event} event
+   * @private
+   */
+  onKeyDown_(event) {
+    // Ignore key events if event.keyCode is VK_PROCESSKEY(229).
+    // TODO(fukino): Remove this workaround once crbug.com/644140 is fixed.
+    if (event.keyCode === 229) {
+      return;
+    }
+
+    event.stopPropagation();
+
+    switch (util.getKeyModifiers(event) + event.key) {
+      case 'Escape':
+        this.cancelRename_();
+        event.preventDefault();
+        break;
+
+      case 'Enter':
+        this.commitRename_();
+        event.preventDefault();
+        break;
+    }
+  }
+}
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index 40719f3..e092d70 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -601,7 +601,7 @@
     this.toolbarController_ = new ToolbarController(
         this.ui_.toolbar, this.ui_.dialogNavigationList, this.ui_.listContainer,
         assert(this.ui_.locationLine), this.selectionHandler_,
-        this.directoryModel_);
+        this.directoryModel_, this.volumeManager_);
     this.emptyFolderController_ = new EmptyFolderController(
         this.ui_.emptyFolder, this.directoryModel_, this.ui_.alertDialog);
     this.actionsController_ = new ActionsController(
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index 2a46822..db884c6 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -272,42 +272,6 @@
 };
 
 /**
- * If entry is MyFiles/Downloads or MyFiles/PluginVm, we don't allow
- * cut/delete/rename.
- * @param {!VolumeManager} volumeManager
- * @param {(Entry|FakeEntry)} entry Entry or a fake entry.
- * @return {boolean}
- */
-CommandUtil.isReadOnly = (volumeManager, entry) => {
-  if (!entry) {
-    return false;
-  }
-  if (util.isFakeEntry(entry)) {
-    return false;
-  }
-
-  // If the entry is not a valid entry.
-  if (!volumeManager) {
-    return false;
-  }
-
-  const volumeInfo = volumeManager.getVolumeInfo(entry);
-  if (!volumeInfo) {
-    return false;
-  }
-
-  if (volumeInfo.volumeType === VolumeManagerCommon.RootType.DOWNLOADS) {
-    if (util.isMyFilesVolumeEnabled() && entry.fullPath === '/Downloads') {
-      return true;
-    }
-    if (util.isPluginVmEnabled() && entry.fullPath === '/PluginVm') {
-      return true;
-    }
-  }
-  return false;
-};
-
-/**
  * Returns whether all of the given entries have the given capability.
  *
  * @param {!Array<Entry>} entries List of entries to check capabilities for.
@@ -1065,7 +1029,7 @@
       return entries.some(entry => {
         const locationInfo = fileManager.volumeManager.getLocationInfo(entry);
         return (locationInfo && locationInfo.isReadOnly) ||
-            CommandUtil.isReadOnly(fileManager.volumeManager, entry);
+            util.isNonModifiable(fileManager.volumeManager, entry);
       });
     }
   };
@@ -1224,7 +1188,7 @@
       }
 
       // For MyFiles/Downloads and MyFiles/PluginVm we only allow copy.
-      if (isMove && CommandUtil.isReadOnly(volumeManager, entry)) {
+      if (isMove && util.isNonModifiable(volumeManager, entry)) {
         return false;
       }
 
@@ -1262,7 +1226,7 @@
       // For MyFiles/Downloads we only allow copy.
       if (isMove &&
           fileManager.getSelection().entries.some(
-              CommandUtil.isReadOnly.bind(null, volumeManager))) {
+              util.isNonModifiable.bind(null, volumeManager))) {
         return false;
       }
 
@@ -1294,7 +1258,7 @@
    */
   execute: function(event, fileManager) {
     const entry = CommandUtil.getCommandEntry(fileManager, event.target);
-    if (CommandUtil.isReadOnly(fileManager.volumeManager, entry)) {
+    if (util.isNonModifiable(fileManager.volumeManager, entry)) {
       return;
     }
     if (event.target instanceof DirectoryTree ||
@@ -1369,7 +1333,7 @@
         !CommandUtil.shouldShowMenuItemsForEntry(
             fileManager.volumeManager, entries[0]) ||
         entries.some(
-            CommandUtil.isReadOnly.bind(null, fileManager.volumeManager))) {
+            util.isNonModifiable.bind(null, fileManager.volumeManager))) {
       event.canExecute = false;
       event.command.setHidden(true);
       return;
diff --git a/ui/file_manager/file_manager/foreground/js/file_tasks.js b/ui/file_manager/file_manager/foreground/js/file_tasks.js
index 38a4acd..63c71e10 100644
--- a/ui/file_manager/file_manager/foreground/js/file_tasks.js
+++ b/ui/file_manager/file_manager/foreground/js/file_tasks.js
@@ -5,134 +5,1203 @@
 /**
  * Represents a collection of available tasks to execute for a specific list
  * of entries.
- *
- * @param {!VolumeManager} volumeManager
- * @param {!MetadataModel} metadataModel
- * @param {!DirectoryModel} directoryModel
- * @param {!FileManagerUI} ui
- * @param {!Array<!Entry>} entries
- * @param {!Array<?string>} mimeTypes
- * @param {!Array<!chrome.fileManagerPrivate.FileTask>} tasks
- * @param {chrome.fileManagerPrivate.FileTask} defaultTask
- * @param {!TaskHistory} taskHistory
- * @param {!NamingController} namingController
- * @param {!Crostini} crostini
- * @constructor
- * @struct
  */
-function FileTasks(
-    volumeManager, metadataModel, directoryModel, ui, entries, mimeTypes, tasks,
-    defaultTask, taskHistory, namingController, crostini) {
+class FileTasks {
   /**
-   * @private {!VolumeManager}
-   * @const
+   * @param {!VolumeManager} volumeManager
+   * @param {!MetadataModel} metadataModel
+   * @param {!DirectoryModel} directoryModel
+   * @param {!FileManagerUI} ui
+   * @param {!Array<!Entry>} entries
+   * @param {!Array<?string>} mimeTypes
+   * @param {!Array<!chrome.fileManagerPrivate.FileTask>} tasks
+   * @param {chrome.fileManagerPrivate.FileTask} defaultTask
+   * @param {!TaskHistory} taskHistory
+   * @param {!NamingController} namingController
+   * @param {!Crostini} crostini
    */
-  this.volumeManager_ = volumeManager;
+  constructor(
+      volumeManager, metadataModel, directoryModel, ui, entries, mimeTypes,
+      tasks, defaultTask, taskHistory, namingController, crostini) {
+    /** @private @const {!VolumeManager} */
+    this.volumeManager_ = volumeManager;
 
-  /**
-   * @private {!MetadataModel}
-   * @const
-   */
-  this.metadataModel_ = metadataModel;
+    /** @private @const {!MetadataModel} */
+    this.metadataModel_ = metadataModel;
 
-  /**
-   * @private {!DirectoryModel}
-   * @const
-   */
-  this.directoryModel_ = directoryModel;
+    /** @private @const {!DirectoryModel} */
+    this.directoryModel_ = directoryModel;
 
-  /**
-   * @private {!FileManagerUI}
-   * @const
-   */
-  this.ui_ = ui;
+    /** @private @const {!FileManagerUI} */
+    this.ui_ = ui;
 
-  /**
-   * @private {!Array<!Entry>}
-   * @const
-   */
-  this.entries_ = entries;
+    /** @private @const {!Array<!Entry>} */
+    this.entries_ = entries;
 
-  /**
-   * @private {!Array<?string>}
-   * @const
-   */
-  this.mimeTypes_ = mimeTypes;
+    /** @private @const {!Array<?string>} */
+    this.mimeTypes_ = mimeTypes;
 
-  /**
-   * @private {!Array<!chrome.fileManagerPrivate.FileTask>}
-   * @const
-   */
-  this.tasks_ = tasks;
+    /** @private @const {!Array<!chrome.fileManagerPrivate.FileTask>} */
+    this.tasks_ = tasks;
 
-  /**
-   * @private {chrome.fileManagerPrivate.FileTask}
-   * @const
-   */
-  this.defaultTask_ = defaultTask;
+    /** @private @const {chrome.fileManagerPrivate.FileTask} */
+    this.defaultTask_ = defaultTask;
 
-  /**
-   * @private {!TaskHistory}
-   * @const
-   */
-  this.taskHistory_ = taskHistory;
+    /** @private @const {!TaskHistory} */
+    this.taskHistory_ = taskHistory;
 
-  /**
-   * @private {!NamingController}
-   * @const
-   */
-  this.namingController_ = namingController;
+    /** @private @const {!NamingController} */
+    this.namingController_ = namingController;
 
-  /**
-   * @private {!Crostini}
-   * @const
-   */
-  this.crostini_ = crostini;
-}
+    /** @private @const {!Crostini} */
+    this.crostini_ = crostini;
+  }
 
-FileTasks.prototype = {
   /**
    * @return {!Array<!Entry>}
    */
   get entries() {
     return this.entries_;
   }
-};
+
+  /**
+   * Creates an instance of FileTasks for the specified list of entries with
+   * mime types.
+   *
+   * @param {!VolumeManager} volumeManager
+   * @param {!MetadataModel} metadataModel
+   * @param {!DirectoryModel} directoryModel
+   * @param {!FileManagerUI} ui
+   * @param {!Array<!Entry>} entries
+   * @param {!Array<?string>} mimeTypes
+   * @param {!TaskHistory} taskHistory
+   * @param {!NamingController} namingController
+   * @param {!Crostini} crostini
+   * @return {!Promise<!FileTasks>}
+   */
+  static create(
+      volumeManager, metadataModel, directoryModel, ui, entries, mimeTypes,
+      taskHistory, namingController, crostini) {
+    const tasksPromise = new Promise(fulfill => {
+      // getFileTasks supports only native entries.
+      entries = entries.filter(util.isNativeEntry);
+      if (entries.length === 0) {
+        fulfill([]);
+        return;
+      }
+      chrome.fileManagerPrivate.getFileTasks(entries, taskItems => {
+        if (chrome.runtime.lastError) {
+          console.error(
+              'Failed to fetch file tasks due to: ' +
+              chrome.runtime.lastError.message);
+          Promise.reject();
+          return;
+        }
+
+        // Linux package installation is currently only supported for a single
+        // file which is inside the Linux container, or in a shareable volume.
+        // TODO(timloh): Instead of filtering these out, we probably should
+        // show a dialog with an error message, similar to when attempting to
+        // run Crostini tasks with non-Crostini entries.
+        if (entries.length !== 1 ||
+            !(FileTasks.isCrostiniEntry(entries[0], volumeManager) ||
+              crostini.canSharePath(
+                  constants.DEFAULT_CROSTINI_VM, entries[0],
+                  false /* persist */))) {
+          taskItems = taskItems.filter(item => {
+            const taskParts = item.taskId.split('|');
+            const appId = taskParts[0];
+            const taskType = taskParts[1];
+            const actionId = taskParts[2];
+            return !(
+                appId === chrome.runtime.id && taskType === 'app' &&
+                actionId === 'install-linux-package');
+          });
+        }
+
+        // Filters out Pack with Zip Archiver task because it will be
+        // accessible via 'Zip selection' context menu button
+        taskItems = taskItems.filter(item => {
+          return item.taskId !== FileTasks.ZIP_ARCHIVER_ZIP_TASK_ID &&
+              item.taskId !== FileTasks.ZIP_ARCHIVER_ZIP_USING_TMP_TASK_ID;
+        });
+
+        fulfill(FileTasks.annotateTasks_(assert(taskItems), entries));
+      });
+    });
+
+    const defaultTaskPromise = tasksPromise.then(tasks => {
+      return FileTasks.getDefaultTask(tasks, taskHistory);
+    });
+
+    return Promise.all([tasksPromise, defaultTaskPromise]).then(args => {
+      return new FileTasks(
+          volumeManager, metadataModel, directoryModel, ui, entries, mimeTypes,
+          args[0], args[1], taskHistory, namingController, crostini);
+    });
+  }
+
+  /**
+   * Gets task items.
+   * @return {!Array<!chrome.fileManagerPrivate.FileTask>}
+   */
+  getTaskItems() {
+    return this.tasks_;
+  }
+
+  /**
+   * Gets tasks which are categorized as OPEN tasks.
+   * @return {!Array<!chrome.fileManagerPrivate.FileTask>}
+   */
+  getOpenTaskItems() {
+    return this.tasks_.filter(FileTasks.isOpenTask);
+  }
+
+  /**
+   * Gets tasks which are not categorized as OPEN tasks.
+   * @return {!Array<!chrome.fileManagerPrivate.FileTask>}
+   */
+  getNonOpenTaskItems() {
+    return this.tasks_.filter(task => !FileTasks.isOpenTask(task));
+  }
+
+  /**
+   * Opens the suggest file dialog.
+   *
+   * @param {function()} onSuccess Success callback.
+   * @param {function()} onCancelled User-cancelled callback.
+   * @param {function()} onFailure Failure callback.
+   */
+  openSuggestAppsDialog(onSuccess, onCancelled, onFailure) {
+    if (this.entries_.length !== 1) {
+      onFailure();
+      return;
+    }
+
+    const entry = this.entries_[0];
+    const mimeType = this.mimeTypes_[0];
+    const basename = entry.name;
+    const splitted = util.splitExtension(basename);
+    const extension = splitted[1];
+
+    // Returns with failure if the file has neither extension nor MIME type.
+    if (!extension && !mimeType) {
+      onFailure();
+      return;
+    }
+
+    const onDialogClosed = (result, itemId) => {
+      switch (result) {
+        case SuggestAppsDialog.Result.SUCCESS:
+          onSuccess();
+          break;
+        case SuggestAppsDialog.Result.FAILED:
+          onFailure();
+          break;
+        default:
+          onCancelled();
+      }
+    };
+
+    this.ui_.suggestAppsDialog.showByExtensionAndMime(
+        extension, mimeType, onDialogClosed);
+  }
+
+  /**
+   * Returns whether the system is currently offline.
+   *
+   * @param {!VolumeManager} volumeManager
+   * @return {boolean} True if the network status is offline.
+   * @private
+   */
+  static isOffline_(volumeManager) {
+    const connection = volumeManager.getDriveConnectionState();
+    return connection.type == VolumeManagerCommon.DriveConnectionType.OFFLINE &&
+        connection.reason ==
+        VolumeManagerCommon.DriveConnectionReason.NO_NETWORK;
+  }
+
+  /**
+   * Records a metric, as well as recording online and offline versions of it.
+   *
+   * @param {!VolumeManager} volumeManager
+   * @param {string} name Metric name.
+   * @param {!*} value Enum value.
+   * @param {!Array<*>} values Array of valid values.
+   */
+  static recordEnumWithOnlineAndOffline_(volumeManager, name, value, values) {
+    metrics.recordEnum(name, value, values);
+    if (FileTasks.isOffline_(volumeManager)) {
+      metrics.recordEnum(name + '.Offline', value, values);
+    } else {
+      metrics.recordEnum(name + '.Online', value, values);
+    }
+  }
+
+  /**
+   * Records trial of opening file grouped by extensions.
+   *
+   * @param {!VolumeManager} volumeManager
+   * @param {Array<!Entry>} entries The entries to be opened.
+   * @private
+   */
+  static recordViewingFileTypeUMA_(volumeManager, entries) {
+    for (let i = 0; i < entries.length; i++) {
+      const entry = entries[i];
+      let extension = FileType.getExtension(entry).toLowerCase();
+      if (FileTasks.UMA_INDEX_KNOWN_EXTENSIONS.indexOf(extension) < 0) {
+        extension = 'other';
+      }
+      FileTasks.recordEnumWithOnlineAndOffline_(
+          volumeManager, 'ViewingFileType', extension,
+          FileTasks.UMA_INDEX_KNOWN_EXTENSIONS);
+    }
+  }
+
+  /**
+   * Records trial of opening file grouped by root types.
+   *
+   * @param {!VolumeManager} volumeManager
+   * @param {?VolumeManagerCommon.RootType} rootType The type of the root where
+   *     entries are being opened.
+   * @private
+   */
+  static recordViewingRootTypeUMA_(volumeManager, rootType) {
+    if (rootType !== null) {
+      FileTasks.recordEnumWithOnlineAndOffline_(
+          volumeManager, 'ViewingRootType', rootType,
+          VolumeManagerCommon.RootTypesForUMA);
+    }
+  }
+
+  static recordZipHandlerUMA_(taskId) {
+    if (FileTasks.UMA_ZIP_HANDLER_TASK_IDS_.indexOf(taskId) != -1) {
+      metrics.recordEnum(
+          'ZipFileTask', taskId, FileTasks.UMA_ZIP_HANDLER_TASK_IDS_);
+    }
+  }
+
+  /**
+   * Records the type of dialog shown when using a crostini app to open a file.
+   * @param {!FileTasks.CrostiniShareDialogType} dialogType
+   * @private
+   */
+  static recordCrostiniShareDialogTypeUMA_(dialogType) {
+    metrics.recordEnum(
+        'CrostiniShareDialog', dialogType,
+        FileTasks.UMA_CROSTINI_SHARE_DIALOG_TYPES_);
+  }
+
+  /**
+   * Returns true if the taskId is for an internal task.
+   *
+   * @param {string} taskId Task identifier.
+   * @return {boolean} True if the task ID is for an internal task.
+   * @private
+   */
+  static isInternalTask_(taskId) {
+    const taskParts = taskId.split('|');
+    const appId = taskParts[0];
+    const taskType = taskParts[1];
+    const actionId = taskParts[2];
+    return (
+        appId === chrome.runtime.id && taskType === 'app' &&
+        (actionId === 'mount-archive' || actionId === 'install-linux-package'));
+  }
+
+  /**
+   * Returns true if the given task is categorized as an OPEN task.
+   *
+   * @param {!chrome.fileManagerPrivate.FileTask} task
+   * @return {boolean} True if the given task is an OPEN task.
+   */
+  static isOpenTask(task) {
+    // We consider following types of tasks as OPEN tasks.
+    // - Files app's internal tasks
+    // - file_handler tasks with OPEN_WITH verb
+    return !task.verb || task.verb == chrome.fileManagerPrivate.Verb.OPEN_WITH;
+  }
+
+  /**
+   * Annotates tasks returned from the API.
+   *
+   * @param {!Array<!chrome.fileManagerPrivate.FileTask>} tasks Input tasks from
+   *     the API.
+   * @param {!Array<!Entry>} entries List of entries for the tasks.
+   * @return {!Array<!chrome.fileManagerPrivate.FileTask>} Annotated tasks.
+   * @private
+   */
+  static annotateTasks_(tasks, entries) {
+    const result = [];
+    const id = chrome.runtime.id;
+    for (let i = 0; i < tasks.length; i++) {
+      const task = tasks[i];
+      const taskParts = task.taskId.split('|');
+
+      // Skip internal Files app's handlers.
+      if (taskParts[0] === id &&
+          (taskParts[2] === 'select' || taskParts[2] === 'open')) {
+        continue;
+      }
+
+      // Tweak images, titles of internal tasks.
+      if (taskParts[0] === id && taskParts[1] === 'app') {
+        if (taskParts[2] === 'mount-archive') {
+          task.iconType = 'archive';
+          task.title = loadTimeData.getString('MOUNT_ARCHIVE');
+          task.verb = undefined;
+        } else if (taskParts[2] === 'open-hosted-generic') {
+          if (entries.length > 1) {
+            task.iconType = 'generic';
+          } else {  // Use specific icon.
+            task.iconType = FileType.getIcon(entries[0]);
+          }
+          task.title = loadTimeData.getString('TASK_OPEN');
+          task.verb = undefined;
+        } else if (taskParts[2] === 'open-hosted-gdoc') {
+          task.iconType = 'gdoc';
+          task.title = loadTimeData.getString('TASK_OPEN_GDOC');
+          task.verb = undefined;
+        } else if (taskParts[2] === 'open-hosted-gsheet') {
+          task.iconType = 'gsheet';
+          task.title = loadTimeData.getString('TASK_OPEN_GSHEET');
+          task.verb = undefined;
+        } else if (taskParts[2] === 'open-hosted-gslides') {
+          task.iconType = 'gslides';
+          task.title = loadTimeData.getString('TASK_OPEN_GSLIDES');
+          task.verb = undefined;
+        } else if (taskParts[2] === 'install-linux-package') {
+          task.iconType = 'crostini';
+          task.title = loadTimeData.getString('TASK_INSTALL_LINUX_PACKAGE');
+          task.verb = undefined;
+        } else if (taskParts[2] === 'view-swf') {
+          // Do not render this task if disabled.
+          if (!loadTimeData.getBoolean('SWF_VIEW_ENABLED')) {
+            continue;
+          }
+          task.iconType = 'generic';
+          task.title = loadTimeData.getString('TASK_VIEW');
+          task.verb = undefined;
+        } else if (taskParts[2] === 'view-pdf') {
+          // Do not render this task if disabled.
+          if (!loadTimeData.getBoolean('PDF_VIEW_ENABLED')) {
+            continue;
+          }
+          task.iconType = 'pdf';
+          task.title = loadTimeData.getString('TASK_VIEW');
+          task.verb = undefined;
+        } else if (taskParts[2] === 'view-in-browser') {
+          task.iconType = 'generic';
+          task.title = loadTimeData.getString('TASK_VIEW');
+          task.verb = undefined;
+        }
+      }
+      if (!task.iconType && taskParts[1] === 'web-intent') {
+        task.iconType = 'generic';
+      }
+
+      // Add verb to title.
+      if (task.verb) {
+        let verbButtonLabel = '';
+        switch (task.verb) {
+          case chrome.fileManagerPrivate.Verb.ADD_TO:
+            verbButtonLabel = 'ADD_TO_VERB_BUTTON_LABEL';
+            break;
+          case chrome.fileManagerPrivate.Verb.PACK_WITH:
+            verbButtonLabel = 'PACK_WITH_VERB_BUTTON_LABEL';
+            break;
+          case chrome.fileManagerPrivate.Verb.SHARE_WITH:
+            // Even when the task has SHARE_WITH verb, we don't prefix the title
+            // with "Share with" when the task is from SEND/SEND_MULTIPLE intent
+            // handlers from Android apps, since the title can already have an
+            // appropriate verb.
+            if (!(taskParts[1] == 'arc' &&
+                  (taskParts[2] == 'send' ||
+                   taskParts[2] == 'send_multiple'))) {
+              verbButtonLabel = 'SHARE_WITH_VERB_BUTTON_LABEL';
+            }
+            break;
+          case chrome.fileManagerPrivate.Verb.OPEN_WITH:
+            verbButtonLabel = 'OPEN_WITH_VERB_BUTTON_LABEL';
+            break;
+          default:
+            console.error('Invalid task verb: ' + task.verb + '.');
+        }
+        if (verbButtonLabel) {
+          task.label = loadTimeData.getStringF(verbButtonLabel, task.title);
+        }
+      }
+
+      result.push(task);
+    }
+
+    return result;
+  }
+
+  /**
+   * @param {!Entry} entry
+   * @param {!VolumeManager} volumeManager
+   * @return {boolean} True if the entry is from crostini.
+   */
+  static isCrostiniEntry(entry, volumeManager) {
+    return volumeManager.getLocationInfo(entry).rootType ===
+        VolumeManagerCommon.RootType.CROSTINI;
+  }
+
+  /**
+   * Returns true if task requires entries to be shared before executing task.
+   * @param {!chrome.fileManagerPrivate.FileTask} task Task to run.
+   * @return {boolean} true if task requires entries to be shared.
+   */
+  static taskRequiresCrostiniSharing(task) {
+    const taskParts = task.taskId.split('|');
+    const taskType = taskParts[1];
+    const actionId = taskParts[2];
+    return taskType === 'crostini' || actionId === 'install-linux-package';
+  }
+
+  /**
+   * Checks if task is a crostini task and all entries are accessible to, or can
+   * be shared with crostini.  Shares files as required if possible and invokes
+   * callback, or shows Unable to Open error dialog and does not invoke
+   * callback.
+   * @param {!chrome.fileManagerPrivate.FileTask} task Task to run.
+   * @param {function()} callback Callback is called when all files (if any) are
+   *   accessible to crostini, else error dialog is shown.
+   * @private
+   */
+  maybeShareWithCrostiniOrShowDialog_(task, callback) {
+    // Check if this is a crostini task.
+    if (!FileTasks.taskRequiresCrostiniSharing(task)) {
+      return callback();
+    }
+
+    let showUnableToOpen = false;
+    const entriesToShare = [];
+
+    for (let i = 0; i < this.entries_.length; i++) {
+      const entry = this.entries_[i];
+      if (FileTasks.isCrostiniEntry(entry, this.volumeManager_) ||
+          this.crostini_.isPathShared(constants.DEFAULT_CROSTINI_VM, entry)) {
+        continue;
+      }
+      if (!this.crostini_.canSharePath(
+              constants.DEFAULT_CROSTINI_VM, entry, false /* persist */)) {
+        showUnableToOpen = true;
+        break;
+      }
+      entriesToShare.push(entry);
+    }
+
+    // Show unable to open alert dialog.
+    if (showUnableToOpen) {
+      this.ui_.alertDialog.showHtml(
+          strf('UNABLE_TO_OPEN_CROSTINI_TITLE', task.title),
+          strf('UNABLE_TO_OPEN_CROSTINI', task.title));
+      FileTasks.recordCrostiniShareDialogTypeUMA_(
+          FileTasks.CrostiniShareDialogType.UnableToOpen);
+      return;
+    }
+
+    // No sharing required.
+    if (entriesToShare.length === 0) {
+      FileTasks.recordCrostiniShareDialogTypeUMA_(
+          FileTasks.CrostiniShareDialogType.None);
+      return callback();
+    }
+
+    // Share then invoke callback.
+    FileTasks.recordCrostiniShareDialogTypeUMA_(
+        FileTasks.CrostiniShareDialogType.ShareBeforeOpen);
+    // Set persist to false when sharing paths to open with a crostini app.
+    chrome.fileManagerPrivate.sharePathsWithCrostini(
+        constants.DEFAULT_CROSTINI_VM, entriesToShare, false /* persist */,
+        () => {
+          // It is unexpected to get an error sharing any files since we have
+          // already validated that all selected files can be shared.
+          // But if it happens, log error, and do not execute callback.
+          if (chrome.runtime.lastError) {
+            return console.error(
+                'Error sharing with linux to execute: ' +
+                chrome.runtime.lastError.message);
+          }
+          // crbug.com/925973.  Do not register non-persisted shared paths since
+          // we can't be sure at any time that the VM has not restarted and they
+          // are still shared.
+          callback();
+        });
+  }
+
+  /**
+   * Executes default task.
+   *
+   * @param {function(boolean, Array<!Entry>)=} opt_callback Called when the
+   *     default task is executed, or the error is occurred.
+   */
+  executeDefault(opt_callback) {
+    FileTasks.recordViewingFileTypeUMA_(this.volumeManager_, this.entries_);
+    FileTasks.recordViewingRootTypeUMA_(
+        this.volumeManager_, this.directoryModel_.getCurrentRootType());
+    this.executeDefaultInternal_(opt_callback);
+  }
+
+  /**
+   * Executes default task.
+   *
+   * @param {function(boolean, Array<!Entry>)=} opt_callback Called when the
+   *     default task is executed, or the error is occurred.
+   * @private
+   */
+  executeDefaultInternal_(opt_callback) {
+    const callback = opt_callback || ((arg1, arg2) => {});
+
+    if (this.defaultTask_ !== null) {
+      this.executeInternal_(this.defaultTask_);
+      callback(true, this.entries_);
+      return;
+    }
+
+    const nonGenericTasks = this.tasks_.filter(t => !t.isGenericFileHandler);
+    // If there is only one task that is not a generic file handler, it should
+    // be executed as a default task. If there are multiple tasks that are not
+    // generic file handlers, and none of them are considered as default, we
+    // show a task picker to ask the user to choose one.
+    if (nonGenericTasks.length >= 2) {
+      this.showTaskPicker(
+          this.ui_.defaultTaskPicker, str('OPEN_WITH_BUTTON_LABEL'),
+          '', task => {
+            this.execute(task);
+          }, FileTasks.TaskPickerType.OpenWith);
+      return;
+    }
+
+    // We don't have tasks, so try to show a file in a browser tab.
+    // We only do that for single selection to avoid confusion.
+    if (this.entries_.length !== 1) {
+      return;
+    }
+
+    const filename = this.entries_[0].name;
+    const extension = util.splitExtension(filename)[1] || null;
+    const mimeType = this.mimeTypes_[0] || null;
+
+    const showAlert = () => {
+      let textMessageId;
+      let titleMessageId;
+      switch (extension) {
+        case '.exe':
+        case '.msi':
+          textMessageId = 'NO_TASK_FOR_EXECUTABLE';
+          break;
+        case '.dmg':
+          textMessageId = 'NO_TASK_FOR_DMG';
+          break;
+        case '.crx':
+          textMessageId = 'NO_TASK_FOR_CRX';
+          titleMessageId = 'NO_TASK_FOR_CRX_TITLE';
+          break;
+        default:
+          textMessageId = 'NO_TASK_FOR_FILE';
+      }
+
+      const webStoreUrl = webStoreUtils.createWebStoreLink(extension, mimeType);
+      const text =
+          strf(textMessageId, webStoreUrl, str('NO_TASK_FOR_FILE_URL'));
+      const title = titleMessageId ? str(titleMessageId) : filename;
+      this.ui_.alertDialog.showHtml(title, text, null, null, null);
+      callback(false, this.entries_);
+    };
+
+    const onViewFilesFailure = () => {
+      if (extension &&
+          (FileTasks.EXTENSIONS_TO_SKIP_SUGGEST_APPS_.indexOf(extension) !==
+               -1 ||
+           constants.EXECUTABLE_EXTENSIONS.indexOf(assert(extension)) !== -1)) {
+        showAlert();
+        return;
+      }
+
+      this.openSuggestAppsDialog(
+          () => {
+            FileTasks
+                .create(
+                    this.volumeManager_, this.metadataModel_,
+                    this.directoryModel_, this.ui_, this.entries_,
+                    this.mimeTypes_, this.taskHistory_, this.namingController_,
+                    this.crostini_)
+                .then(
+                    tasks => {
+                      tasks.executeDefault();
+                      callback(true, this.entries_);
+                    },
+                    () => {
+                      callback(false, this.entries_);
+                    });
+          },
+          () => {
+            callback(false, this.entries_);
+          },
+          showAlert);
+    };
+
+    const onViewFiles = result => {
+      switch (result) {
+        case 'opened':
+          callback(true, this.entries_);
+          break;
+        case 'message_sent':
+          util.isTeleported(window).then(teleported => {
+            if (teleported) {
+              this.ui_.showOpenInOtherDesktopAlert(this.entries_);
+            }
+          });
+          callback(true, this.entries_);
+          break;
+        case 'empty':
+          callback(true, this.entries_);
+          break;
+        case 'failed':
+          // Suppress the Unchecked runtime.lastError console message
+          if (chrome.runtime.lastError) {
+            console.debug(chrome.runtime.lastError.message);
+          }
+          onViewFilesFailure();
+          break;
+      }
+    };
+
+    this.checkAvailability_(() => {
+      const taskId = chrome.runtime.id + '|file|view-in-browser';
+      chrome.fileManagerPrivate.executeTask(taskId, this.entries_, onViewFiles);
+    });
+  }
+
+  /**
+   * Executes a single task.
+   *
+   * @param {chrome.fileManagerPrivate.FileTask} task FileTask.
+   */
+  execute(task) {
+    FileTasks.recordViewingFileTypeUMA_(this.volumeManager_, this.entries_);
+    FileTasks.recordViewingRootTypeUMA_(
+        this.volumeManager_, this.directoryModel_.getCurrentRootType());
+    this.executeInternal_(task);
+  }
+
+  /**
+   * The core implementation to execute a single task.
+   *
+   * @param {chrome.fileManagerPrivate.FileTask} task FileTask.
+   * @private
+   */
+  executeInternal_(task) {
+    this.checkAvailability_(() => {
+      this.maybeShareWithCrostiniOrShowDialog_(task, () => {
+        this.taskHistory_.recordTaskExecuted(task.taskId);
+        let msg;
+        if (this.entries.length === 1) {
+          msg = strf('OPEN_A11Y', this.entries_[0].name);
+        } else {
+          msg = strf('OPEN_A11Y_PLURAL', this.entries_.length);
+        }
+        this.ui_.speakA11yMessage(msg);
+        if (FileTasks.isInternalTask_(task.taskId)) {
+          this.executeInternalTask_(task.taskId);
+        } else {
+          FileTasks.recordZipHandlerUMA_(task.taskId);
+          chrome.fileManagerPrivate.executeTask(
+              task.taskId, this.entries_, (result) => {
+                if (result !== 'message_sent') {
+                  return;
+                }
+                util.isTeleported(window).then((teleported) => {
+                  if (teleported) {
+                    this.ui_.showOpenInOtherDesktopAlert(this.entries_);
+                  }
+                });
+              });
+        }
+      });
+    });
+  }
+
+  /**
+   * Ensures that the all files are available right now.
+   *
+   * Must not call before initialization.
+   * @param {function()} callback Called when checking is completed and all
+   *     files are available. Otherwise not called.
+   * @private
+   */
+  checkAvailability_(callback) {
+    const areAll = (entries, props, name) => {
+      // TODO(cmihail): Make files in directories available offline.
+      // See http://crbug.com/569767.
+      let okEntriesNum = 0;
+      for (let i = 0; i < entries.length; i++) {
+        // If got no properties, we safely assume that item is available.
+        if (props[i] && (props[i][name] || entries[i].isDirectory)) {
+          okEntriesNum++;
+        }
+      }
+      return okEntriesNum === props.length;
+    };
+
+    const containsDriveEntries = this.entries_.some(entry => {
+      const volumeInfo = this.volumeManager_.getVolumeInfo(entry);
+      return volumeInfo &&
+          volumeInfo.volumeType === VolumeManagerCommon.VolumeType.DRIVE;
+    });
+
+    // Availability is not checked for non-Drive files, as availableOffline, nor
+    // availableWhenMetered are not exposed for other types of volumes at this
+    // moment.
+    if (!containsDriveEntries) {
+      callback();
+      return;
+    }
+
+    const isDriveOffline =
+        this.volumeManager_.getDriveConnectionState().type ===
+        VolumeManagerCommon.DriveConnectionType.OFFLINE;
+
+    if (isDriveOffline) {
+      this.metadataModel_.get(this.entries_, ['availableOffline', 'hosted'])
+          .then(props => {
+            if (areAll(this.entries_, props, 'availableOffline')) {
+              callback();
+              return;
+            }
+
+            this.ui_.alertDialog.showHtml(
+                loadTimeData.getString('OFFLINE_HEADER'),
+                props[0].hosted ?
+                    loadTimeData.getStringF(
+                        this.entries_.length === 1 ?
+                            'HOSTED_OFFLINE_MESSAGE' :
+                            'HOSTED_OFFLINE_MESSAGE_PLURAL') :
+                    loadTimeData.getStringF(
+                        this.entries_.length === 1 ? 'OFFLINE_MESSAGE' :
+                                                     'OFFLINE_MESSAGE_PLURAL',
+                        loadTimeData.getString('OFFLINE_COLUMN_LABEL')),
+                null, null, null);
+          });
+      return;
+    }
+
+    const isOnMetered = this.volumeManager_.getDriveConnectionState().type ===
+        VolumeManagerCommon.DriveConnectionType.METERED;
+
+    if (isOnMetered) {
+      this.metadataModel_.get(this.entries_, ['availableWhenMetered', 'size'])
+          .then(props => {
+            if (areAll(this.entries_, props, 'availableWhenMetered')) {
+              callback();
+              return;
+            }
+
+            let sizeToDownload = 0;
+            for (let i = 0; i !== this.entries_.length; i++) {
+              if (!props[i].availableWhenMetered) {
+                sizeToDownload += props[i].size;
+              }
+            }
+            this.ui_.confirmDialog.show(
+                loadTimeData.getStringF(
+                    this.entries_.length === 1 ?
+                        'CONFIRM_MOBILE_DATA_USE' :
+                        'CONFIRM_MOBILE_DATA_USE_PLURAL',
+                    util.bytesToString(sizeToDownload)),
+                callback, null, null);
+          });
+      return;
+    }
+
+    callback();
+  }
+
+  /**
+   * Executes an internal task.
+   *
+   * @param {string} taskId The task id.
+   * @private
+   */
+  executeInternalTask_(taskId) {
+    const taskParts = taskId.split('|');
+    if (taskParts[2] === 'mount-archive') {
+      this.mountArchivesInternal_();
+      return;
+    }
+    if (taskParts[2] === 'install-linux-package') {
+      this.installLinuxPackageInternal_();
+      return;
+    }
+
+    console.error('The specified task is not a valid internal task: ' + taskId);
+  }
+
+  /**
+   * Install a Linux Package in the Linux container.
+   * @private
+   */
+  installLinuxPackageInternal_() {
+    assert(this.entries_.length === 1);
+    this.ui_.installLinuxPackageDialog.showInstallLinuxPackageDialog(
+        this.entries_[0]);
+  }
+
+  /**
+   * The core implementation of mounts archives.
+   * @private
+   */
+  mountArchivesInternal_() {
+    const tracker = this.directoryModel_.createDirectoryChangeTracker();
+    tracker.start();
+
+    // TODO(mtomasz): Move conversion from entry to url to custom bindings.
+    // crbug.com/345527.
+    const urls = util.entriesToURLs(this.entries_);
+    for (let index = 0; index < urls.length; ++index) {
+      // TODO(mtomasz): Pass Entry instead of URL.
+      this.volumeManager_.mountArchive(urls[index], volumeInfo => {
+        if (tracker.hasChanged) {
+          tracker.stop();
+          return;
+        }
+        volumeInfo.resolveDisplayRoot(
+            displayRoot => {
+              if (tracker.hasChanged) {
+                tracker.stop();
+                return;
+              }
+              this.directoryModel_.changeDirectoryEntry(displayRoot);
+            },
+            () => {
+              console.warn(
+                  'Failed to resolve the display root after mounting.');
+              tracker.stop();
+            });
+      }, ((url, error) => {
+           tracker.stop();
+           const path = util.extractFilePath(url);
+           const namePos = path.lastIndexOf('/');
+           this.ui_.alertDialog.show(
+               strf('ARCHIVE_MOUNT_FAILED', path.substr(namePos + 1), error),
+               null, null);
+         }).bind(null, urls[index]));
+    }
+  }
+
+  /**
+   * Displays the list of tasks in a open task picker combobutton and a share
+   * options menu.
+   *
+   * @param {!cr.ui.ComboButton} openCombobutton The open task picker
+   *     combobutton.
+   * @param {!cr.ui.MultiMenuButton} shareMenuButton Button for share options.
+   * @public
+   */
+  display(openCombobutton, shareMenuButton) {
+    const openTasks = [];
+    const otherTasks = [];
+    for (let i = 0; i < this.tasks_.length; i++) {
+      const task = this.tasks_[i];
+      if (FileTasks.isOpenTask(task)) {
+        openTasks.push(task);
+      } else {
+        otherTasks.push(task);
+      }
+    }
+    this.updateOpenComboButton_(openCombobutton, openTasks);
+    this.updateShareMenuButton_(shareMenuButton, otherTasks);
+  }
+
+  /**
+   * Setup a task picker combobutton based on the given tasks.
+   * @param {!cr.ui.ComboButton} combobutton
+   * @param {!Array<!chrome.fileManagerPrivate.FileTask>} tasks
+   */
+  updateOpenComboButton_(combobutton, tasks) {
+    combobutton.hidden = tasks.length == 0;
+    if (tasks.length == 0) {
+      return;
+    }
+
+    combobutton.clear();
+
+    // If there exist defaultTask show it on the combobutton.
+    if (this.defaultTask_) {
+      combobutton.defaultItem =
+          FileTasks.createComboButtonItem_(this.defaultTask_, str('TASK_OPEN'));
+    } else {
+      combobutton.defaultItem = {
+        type: FileTasks.TaskMenuButtonItemType.ShowMenu,
+        label: str('OPEN_WITH_BUTTON_LABEL')
+      };
+    }
+
+    // If there exist 2 or more available tasks, show them in context menu
+    // (including defaultTask). If only one generic task is available, we
+    // also show it in the context menu.
+    const items = this.createItems_(tasks);
+    if (items.length > 1 ||
+        (items.length === 1 && this.defaultTask_ === null)) {
+      for (let j = 0; j < items.length; j++) {
+        combobutton.addDropDownItem(items[j]);
+      }
+
+      // If there exist non generic task (i.e. defaultTask is set), we show
+      // an item to change default task.
+      if (this.defaultTask_) {
+        combobutton.addSeparator();
+        const changeDefaultMenuItem = combobutton.addDropDownItem({
+          type: FileTasks.TaskMenuButtonItemType.ChangeDefaultTask,
+          label: loadTimeData.getString('CHANGE_DEFAULT_MENU_ITEM')
+        });
+        changeDefaultMenuItem.classList.add('change-default');
+      }
+    }
+  }
+
+  /**
+   * Setup a menu button for sharing options based on the given tasks.
+   * @param {!cr.ui.MultiMenuButton} shareMenuButton
+   * @param {!Array<!chrome.fileManagerPrivate.FileTask>} tasks
+   */
+  updateShareMenuButton_(shareMenuButton, tasks) {
+    let driveShareCommand =
+        shareMenuButton.menu.querySelector('cr-menu-item[command="#share"]');
+    let driveShareCommandSeparator =
+        shareMenuButton.menu.querySelector('#drive-share-separator');
+    let moreActionsSeparator =
+        shareMenuButton.menu.querySelector('#more-actions-separator');
+
+    // Hide share icon for New Folder creation.  See https://crbug.com/571355.
+    shareMenuButton.hidden =
+        (driveShareCommand.disabled && tasks.length == 0) ||
+        this.namingController_.isRenamingInProgress();
+    moreActionsSeparator.hidden = true;
+
+    // Show the separator if Drive share command is enabled and there is at
+    // least one other share actions.
+    driveShareCommandSeparator.hidden =
+        driveShareCommand.disabled || tasks.length == 0;
+
+    // Temporarily remove the more actions item while the rest of the menu
+    // items are being cleared out so we don't lose it and make it hidden for
+    // now
+    let moreActions = shareMenuButton.menu.querySelector(
+        'cr-menu-item[command="#show-submenu"]');
+    moreActions.remove();
+    moreActions.setAttribute('hidden', '');
+    // Remove the separator as well
+    moreActionsSeparator.remove();
+
+    // Clear menu items except for drive share menu and a separator for it.
+    // As querySelectorAll() returns live NodeList, we need to copy elements to
+    // Array object to modify DOM in the for loop.
+    const itemsToRemove = [].slice.call(shareMenuButton.menu.querySelectorAll(
+        'cr-menu-item:not([command="#share"])'));
+    for (let i = 0; i < itemsToRemove.length; i++) {
+      const item = itemsToRemove[i];
+      item.parentNode.removeChild(item);
+    }
+    // Clear menu items in the overflow sub-menu since we'll repopulate it
+    // with any relevant items below.
+    if (shareMenuButton.overflow !== null) {
+      while (shareMenuButton.overflow.firstChild !== null) {
+        shareMenuButton.overflow.removeChild(
+            shareMenuButton.overflow.firstChild);
+      }
+    }
+
+    // Add menu items for the new tasks.
+    const items = this.createItems_(tasks);
+    let menu = /** @type {!cr.ui.Menu} */ (shareMenuButton.menu);
+    for (let i = 0; i < items.length; i++) {
+      // If we have at least 10 entries, split off into a sub-menu
+      if (i == NUM_TOP_LEVEL_ENTRIES && MAX_NON_SPLIT_ENTRIES <= items.length) {
+        moreActions.removeAttribute('hidden');
+        moreActionsSeparator.hidden = false;
+        menu = shareMenuButton.overflow;
+      }
+      const menuitem = menu.addMenuItem(items[i]);
+      cr.ui.decorate(menuitem, cr.ui.FilesMenuItem);
+      menuitem.data = items[i];
+      if (items[i].iconType) {
+        menuitem.style.backgroundImage = '';
+        menuitem.setAttribute('file-type-icon', items[i].iconType);
+      }
+    }
+    // Replace the more actions menu item and separator
+    shareMenuButton.menu.appendChild(moreActionsSeparator);
+    shareMenuButton.menu.appendChild(moreActions);
+  }
+
+  /**
+   * Creates sorted array of available task descriptions such as title and icon.
+   *
+   * @param {!Array<!chrome.fileManagerPrivate.FileTask>} tasks Tasks to create
+   *     items.
+   * @return {!Array<!FileTasks.ComboButtonItem>} Created array can be used to
+   *     feed combobox, menus and so on.
+   * @private
+   */
+  createItems_(tasks) {
+    const items = [];
+
+    // Create items.
+    for (let index = 0; index < tasks.length; index++) {
+      const task = tasks[index];
+      if (task === this.defaultTask_) {
+        const title =
+            task.title + ' ' + loadTimeData.getString('DEFAULT_TASK_LABEL');
+        items.push(FileTasks.createComboButtonItem_(task, title, true, true));
+      } else {
+        items.push(FileTasks.createComboButtonItem_(task));
+      }
+    }
+
+    // Sort items (Sort order: isDefault, lastExecutedTime, label).
+    items.sort((a, b) => {
+      // Sort by isDefaultTask.
+      const isDefault = (b.isDefault ? 1 : 0) - (a.isDefault ? 1 : 0);
+      if (isDefault !== 0) {
+        return isDefault;
+      }
+
+      // Sort by last-executed time.
+      const aTime = this.taskHistory_.getLastExecutedTime(a.task.taskId);
+      const bTime = this.taskHistory_.getLastExecutedTime(b.task.taskId);
+      if (aTime != bTime) {
+        return bTime - aTime;
+      }
+
+      // Sort by label.
+      return a.label.localeCompare(b.label);
+    });
+
+    return items;
+  }
+
+  /**
+   * Creates combobutton item based on task.
+   *
+   * @param {!chrome.fileManagerPrivate.FileTask} task Task to convert.
+   * @param {string=} opt_title Title.
+   * @param {boolean=} opt_bold Make a menu item bold.
+   * @param {boolean=} opt_isDefault Mark the item as default item.
+   * @return {!FileTasks.ComboButtonItem} Item appendable to combobutton
+   *     drop-down list.
+   * @private
+   */
+  static createComboButtonItem_(task, opt_title, opt_bold, opt_isDefault) {
+    return {
+      type: FileTasks.TaskMenuButtonItemType.RunTask,
+      label: opt_title || task.label || task.title,
+      iconUrl: task.iconUrl || '',
+      iconType: task.iconType || '',
+      task: task,
+      bold: opt_bold || false,
+      isDefault: opt_isDefault || false,
+      isGenericFileHandler: /** @type {boolean} */ (task.isGenericFileHandler)
+    };
+  }
+
+  /**
+   * Shows modal task picker dialog with currently available list of tasks.
+   *
+   * @param {cr.filebrowser.DefaultTaskDialog} taskDialog Task dialog to show
+   *     and update.
+   * @param {string} title Title to use.
+   * @param {string} message Message to use.
+   * @param {function(!chrome.fileManagerPrivate.FileTask)} onSuccess Callback
+   *     to pass selected task.
+   * @param {FileTasks.TaskPickerType} pickerType Task picker type.
+   */
+  showTaskPicker(taskDialog, title, message, onSuccess, pickerType) {
+    const tasks = pickerType == FileTasks.TaskPickerType.MoreActions ?
+        this.getNonOpenTaskItems() :
+        this.getOpenTaskItems();
+    let items = this.createItems_(tasks);
+    if (pickerType == FileTasks.TaskPickerType.ChangeDefault) {
+      items = items.filter(item => !item.isGenericFileHandler);
+    }
+
+    let defaultIdx = 0;
+    for (let j = 0; j < items.length; j++) {
+      if (this.defaultTask_ &&
+          items[j].task.taskId === this.defaultTask_.taskId) {
+        defaultIdx = j;
+      }
+    }
+
+    taskDialog.showDefaultTaskDialog(
+        title, message, items, defaultIdx, item => {
+          onSuccess(item.task);
+        });
+  }
+
+  /**
+   * Gets the default task from tasks. In case there is no such task (i.e. all
+   * tasks are generic file handlers), then return null.
+   *
+   * @param {!Array<!chrome.fileManagerPrivate.FileTask>} tasks The list of
+   *     tasks from where to choose the default task.
+   * @param {!TaskHistory} taskHistory
+   * @return {?chrome.fileManagerPrivate.FileTask} the default task, or null if
+   *     no default task found.
+   */
+  static getDefaultTask(tasks, taskHistory) {
+    // 1. Default app set for MIME or file extension by user, or built-in app.
+    for (let i = 0; i < tasks.length; i++) {
+      if (tasks[i].isDefault) {
+        return tasks[i];
+      }
+    }
+    const nonGenericTasks = tasks.filter(t => !t.isGenericFileHandler);
+    // 2. Most recently executed non-generic task.
+    const latest = nonGenericTasks[0];
+    if (latest && taskHistory.getLastExecutedTime(latest.taskId)) {
+      return latest;
+    }
+    // 3. Sole non-generic handler.
+    if (nonGenericTasks.length == 1) {
+      return nonGenericTasks[0];
+    }
+    return null;
+  }
+}
 
 /**
  * The app ID of the video player app.
- * @const
- * @type {string}
+ * @const {string}
  */
 FileTasks.VIDEO_PLAYER_ID = 'jcgeabjmjgoblfofpppfkcoakmfobdko';
 
 /**
  * The task id of the zip unpacker app.
- * @const
- * @type {string}
+ * @const {string}
  */
 FileTasks.ZIP_UNPACKER_TASK_ID = 'oedeeodfidgoollimchfdnbmhcpnklnd|app|zip';
 
 /**
  * The task id of unzip action of Zip Archiver app.
- * @const
- * @type {string}
+ * @const {string}
  */
 FileTasks.ZIP_ARCHIVER_UNZIP_TASK_ID =
     'dmboannefpncccogfdikhmhpmdnddgoe|app|open';
 
 /**
  * The task id of zip action of Zip Archiver app.
- * @const
- * @type {string}
+ * @const {string}
  */
 FileTasks.ZIP_ARCHIVER_ZIP_TASK_ID =
     'dmboannefpncccogfdikhmhpmdnddgoe|app|pack';
 
 /**
  * The task id of zip action of Zip Archiver app, using temporary dir as workdir
- * @const
- * @type {string}
+ * @const {string}
  */
 FileTasks.ZIP_ARCHIVER_ZIP_USING_TMP_TASK_ID =
     'dmboannefpncccogfdikhmhpmdnddgoe|app|pack_using_tmp';
@@ -159,158 +1228,12 @@
 };
 
 /**
- * Creates an instance of FileTasks for the specified list of entries with mime
- * types.
- *
- * @param {!VolumeManager} volumeManager
- * @param {!MetadataModel} metadataModel
- * @param {!DirectoryModel} directoryModel
- * @param {!FileManagerUI} ui
- * @param {!Array<!Entry>} entries
- * @param {!Array<?string>} mimeTypes
- * @param {!TaskHistory} taskHistory
- * @param {!NamingController} namingController
- * @param {!Crostini} crostini
- * @return {!Promise<!FileTasks>}
- */
-FileTasks.create =
-    (volumeManager, metadataModel, directoryModel, ui, entries, mimeTypes,
-     taskHistory, namingController, crostini) => {
-      const tasksPromise = new Promise(fulfill => {
-        // getFileTasks supports only native entries.
-        entries = entries.filter(util.isNativeEntry);
-        if (entries.length === 0) {
-          fulfill([]);
-          return;
-        }
-        chrome.fileManagerPrivate.getFileTasks(entries, taskItems => {
-          if (chrome.runtime.lastError) {
-            console.error(
-                'Failed to fetch file tasks due to: ' +
-                chrome.runtime.lastError.message);
-            Promise.reject();
-            return;
-          }
-
-          // Linux package installation is currently only supported for a single
-          // file which is inside the Linux container, or in a shareable volume.
-          // TODO(timloh): Instead of filtering these out, we probably should
-          // show a dialog with an error message, similar to when attempting to
-          // run Crostini tasks with non-Crostini entries.
-          if (entries.length !== 1 ||
-              !(FileTasks.isCrostiniEntry(entries[0], volumeManager) ||
-                crostini.canSharePath(
-                    constants.DEFAULT_CROSTINI_VM, entries[0],
-                    false /* persist */))) {
-            taskItems = taskItems.filter(item => {
-              const taskParts = item.taskId.split('|');
-              const appId = taskParts[0];
-              const taskType = taskParts[1];
-              const actionId = taskParts[2];
-              return !(
-                  appId === chrome.runtime.id && taskType === 'app' &&
-                  actionId === 'install-linux-package');
-            });
-          }
-
-          // Filters out Pack with Zip Archiver task because it will be
-          // accessible via 'Zip selection' context menu button
-          taskItems = taskItems.filter(item => {
-            return item.taskId !== FileTasks.ZIP_ARCHIVER_ZIP_TASK_ID &&
-                item.taskId !== FileTasks.ZIP_ARCHIVER_ZIP_USING_TMP_TASK_ID;
-          });
-
-          fulfill(FileTasks.annotateTasks_(assert(taskItems), entries));
-        });
-      });
-
-      const defaultTaskPromise = tasksPromise.then(tasks => {
-        return FileTasks.getDefaultTask(tasks, taskHistory);
-      });
-
-      return Promise.all([tasksPromise, defaultTaskPromise]).then(args => {
-        return new FileTasks(
-            volumeManager, metadataModel, directoryModel, ui, entries,
-            mimeTypes, args[0], args[1], taskHistory, namingController,
-            crostini);
-      });
-    };
-
-/**
- * Obtains the task items.
- * @return {!Array<!chrome.fileManagerPrivate.FileTask>}
- */
-FileTasks.prototype.getTaskItems = function() {
-  return this.tasks_;
-};
-
-/**
- * Obtain tasks which are categorized as OPEN tasks.
- * @return {!Array<!chrome.fileManagerPrivate.FileTask>}
- */
-FileTasks.prototype.getOpenTaskItems = function() {
-  return this.tasks_.filter(FileTasks.isOpenTask);
-};
-
-/**
- * Obtain tasks which are not categorized as OPEN tasks.
- * @return {!Array<!chrome.fileManagerPrivate.FileTask>}
- */
-FileTasks.prototype.getNonOpenTaskItems = function() {
-  return this.tasks_.filter(task => !FileTasks.isOpenTask(task));
-};
-
-/**
- * Opens the suggest file dialog.
- *
- * @param {function()} onSuccess Success callback.
- * @param {function()} onCancelled User-cancelled callback.
- * @param {function()} onFailure Failure callback.
- */
-FileTasks.prototype.openSuggestAppsDialog = function(
-    onSuccess, onCancelled, onFailure) {
-  if (this.entries_.length !== 1) {
-    onFailure();
-    return;
-  }
-
-  const entry = this.entries_[0];
-  const mimeType = this.mimeTypes_[0];
-  const basename = entry.name;
-  const splitted = util.splitExtension(basename);
-  const extension = splitted[1];
-
-  // Returns with failure if the file has neither extension nor MIME type.
-  if (!extension && !mimeType) {
-    onFailure();
-    return;
-  }
-
-  const onDialogClosed = (result, itemId) => {
-    switch (result) {
-      case SuggestAppsDialog.Result.SUCCESS:
-        onSuccess();
-        break;
-      case SuggestAppsDialog.Result.FAILED:
-        onFailure();
-        break;
-      default:
-        onCancelled();
-    }
-  };
-
-  this.ui_.suggestAppsDialog.showByExtensionAndMime(
-      extension, mimeType, onDialogClosed);
-};
-
-/**
  * The list of known extensions to record UMA.
  * Note: Because the data is recorded by the index, so new item shouldn't be
  * inserted.
  * Must match the ViewFileType entry in enums.xml.
  *
- * @const
- * @type {Array<string>}
+ * @const {Array<string>}
  */
 FileTasks.UMA_INDEX_KNOWN_EXTENSIONS = Object.freeze([
   'other',     '.3ga',         '.3gp',
@@ -340,9 +1263,7 @@
 
 /**
  * The list of extensions to skip the suggest app dialog.
- * @const
- * @type {Array<string>}
- * @private
+ * @private @const {Array<string>}
  */
 FileTasks.EXTENSIONS_TO_SKIP_SUGGEST_APPS_ = Object.freeze([
   '.crdownload',
@@ -362,80 +1283,6 @@
 ]);
 
 /**
- * Returns whether the system is currently offline.
- *
- * @param {!VolumeManager} volumeManager
- * @return {boolean} True if the network status is offline.
- * @private
- */
-FileTasks.isOffline_ = volumeManager => {
-  const connection = volumeManager.getDriveConnectionState();
-  return connection.type == VolumeManagerCommon.DriveConnectionType.OFFLINE &&
-      connection.reason == VolumeManagerCommon.DriveConnectionReason.NO_NETWORK;
-};
-
-/**
- * Records a metric, as well as recording online and offline versions of it.
- *
- * @param {!VolumeManager} volumeManager
- * @param {string} name Metric name.
- * @param {!*} value Enum value.
- * @param {!Array<*>} values Array of valid values.
- */
-FileTasks.recordEnumWithOnlineAndOffline_ =
-    (volumeManager, name, value, values) => {
-      metrics.recordEnum(name, value, values);
-      if (FileTasks.isOffline_(volumeManager)) {
-        metrics.recordEnum(name + '.Offline', value, values);
-      } else {
-        metrics.recordEnum(name + '.Online', value, values);
-      }
-    };
-
-/**
- * Records trial of opening file grouped by extensions.
- *
- * @param {!VolumeManager} volumeManager
- * @param {Array<!Entry>} entries The entries to be opened.
- * @private
- */
-FileTasks.recordViewingFileTypeUMA_ = (volumeManager, entries) => {
-  for (let i = 0; i < entries.length; i++) {
-    const entry = entries[i];
-    let extension = FileType.getExtension(entry).toLowerCase();
-    if (FileTasks.UMA_INDEX_KNOWN_EXTENSIONS.indexOf(extension) < 0) {
-      extension = 'other';
-    }
-    FileTasks.recordEnumWithOnlineAndOffline_(
-        volumeManager, 'ViewingFileType', extension,
-        FileTasks.UMA_INDEX_KNOWN_EXTENSIONS);
-  }
-};
-
-/**
- * Records trial of opening file grouped by root types.
- *
- * @param {!VolumeManager} volumeManager
- * @param {?VolumeManagerCommon.RootType} rootType The type of the root where
- *     entries are being opened.
- * @private
- */
-FileTasks.recordViewingRootTypeUMA_ = (volumeManager, rootType) => {
-  if (rootType !== null) {
-    FileTasks.recordEnumWithOnlineAndOffline_(
-        volumeManager, 'ViewingRootType', rootType,
-        VolumeManagerCommon.RootTypesForUMA);
-  }
-};
-
-FileTasks.recordZipHandlerUMA_ = taskId => {
-  if (FileTasks.UMA_ZIP_HANDLER_TASK_IDS_.indexOf(taskId) != -1) {
-    metrics.recordEnum(
-        'ZipFileTask', taskId, FileTasks.UMA_ZIP_HANDLER_TASK_IDS_);
-  }
-};
-
-/**
  * Crostini Share Dialog types.
  * Keep in sync with enums.xml FileManagerCrostiniShareDialogType.
  * @enum {string}
@@ -456,686 +1303,13 @@
   FileTasks.CrostiniShareDialogType.UnableToOpen,
 ]);
 
-
-/**
- * Records the type of dialog shown when using a crostini app to open a file.
- * @param {!FileTasks.CrostiniShareDialogType} dialogType
- * @private
- */
-FileTasks.recordCrostiniShareDialogTypeUMA_ = dialogType => {
-  metrics.recordEnum(
-      'CrostiniShareDialog', dialogType,
-      FileTasks.UMA_CROSTINI_SHARE_DIALOG_TYPES_);
-};
-
-/**
- * Returns true if the taskId is for an internal task.
- *
- * @param {string} taskId Task identifier.
- * @return {boolean} True if the task ID is for an internal task.
- * @private
- */
-FileTasks.isInternalTask_ = taskId => {
-  const taskParts = taskId.split('|');
-  const appId = taskParts[0];
-  const taskType = taskParts[1];
-  const actionId = taskParts[2];
-  return (
-      appId === chrome.runtime.id && taskType === 'app' &&
-      (actionId === 'mount-archive' || actionId === 'install-linux-package'));
-};
-
-/**
- * Returns true if the given task is categorized as an OPEN task.
- *
- * @param {!chrome.fileManagerPrivate.FileTask} task
- * @return {boolean} True if the given task is an OPEN task.
- */
-FileTasks.isOpenTask = task => {
-  // We consider following types of tasks as OPEN tasks.
-  // - Files app's internal tasks
-  // - file_handler tasks with OPEN_WITH verb
-  return !task.verb || task.verb == chrome.fileManagerPrivate.Verb.OPEN_WITH;
-};
-
-/**
- * Annotates tasks returned from the API.
- *
- * @param {!Array<!chrome.fileManagerPrivate.FileTask>} tasks Input tasks from
- *     the API.
- * @param {!Array<!Entry>} entries List of entries for the tasks.
- * @return {!Array<!chrome.fileManagerPrivate.FileTask>} Annotated tasks.
- * @private
- */
-FileTasks.annotateTasks_ = (tasks, entries) => {
-  const result = [];
-  const id = chrome.runtime.id;
-  for (let i = 0; i < tasks.length; i++) {
-    const task = tasks[i];
-    const taskParts = task.taskId.split('|');
-
-    // Skip internal Files app's handlers.
-    if (taskParts[0] === id &&
-        (taskParts[2] === 'select' || taskParts[2] === 'open')) {
-      continue;
-    }
-
-    // Tweak images, titles of internal tasks.
-    if (taskParts[0] === id && taskParts[1] === 'app') {
-      if (taskParts[2] === 'mount-archive') {
-        task.iconType = 'archive';
-        task.title = loadTimeData.getString('MOUNT_ARCHIVE');
-        task.verb = undefined;
-      } else if (taskParts[2] === 'open-hosted-generic') {
-        if (entries.length > 1) {
-          task.iconType = 'generic';
-        } else {  // Use specific icon.
-          task.iconType = FileType.getIcon(entries[0]);
-        }
-        task.title = loadTimeData.getString('TASK_OPEN');
-        task.verb = undefined;
-      } else if (taskParts[2] === 'open-hosted-gdoc') {
-        task.iconType = 'gdoc';
-        task.title = loadTimeData.getString('TASK_OPEN_GDOC');
-        task.verb = undefined;
-      } else if (taskParts[2] === 'open-hosted-gsheet') {
-        task.iconType = 'gsheet';
-        task.title = loadTimeData.getString('TASK_OPEN_GSHEET');
-        task.verb = undefined;
-      } else if (taskParts[2] === 'open-hosted-gslides') {
-        task.iconType = 'gslides';
-        task.title = loadTimeData.getString('TASK_OPEN_GSLIDES');
-        task.verb = undefined;
-      } else if (taskParts[2] === 'install-linux-package') {
-        task.iconType = 'crostini';
-        task.title = loadTimeData.getString('TASK_INSTALL_LINUX_PACKAGE');
-        task.verb = undefined;
-      } else if (taskParts[2] === 'view-swf') {
-        // Do not render this task if disabled.
-        if (!loadTimeData.getBoolean('SWF_VIEW_ENABLED')) {
-          continue;
-        }
-        task.iconType = 'generic';
-        task.title = loadTimeData.getString('TASK_VIEW');
-        task.verb = undefined;
-      } else if (taskParts[2] === 'view-pdf') {
-        // Do not render this task if disabled.
-        if (!loadTimeData.getBoolean('PDF_VIEW_ENABLED')) {
-          continue;
-        }
-        task.iconType = 'pdf';
-        task.title = loadTimeData.getString('TASK_VIEW');
-        task.verb = undefined;
-      } else if (taskParts[2] === 'view-in-browser') {
-        task.iconType = 'generic';
-        task.title = loadTimeData.getString('TASK_VIEW');
-        task.verb = undefined;
-      }
-    }
-    if (!task.iconType && taskParts[1] === 'web-intent') {
-      task.iconType = 'generic';
-    }
-
-    // Add verb to title.
-    if (task.verb) {
-      let verbButtonLabel = '';
-      switch (task.verb) {
-        case chrome.fileManagerPrivate.Verb.ADD_TO:
-          verbButtonLabel = 'ADD_TO_VERB_BUTTON_LABEL';
-          break;
-        case chrome.fileManagerPrivate.Verb.PACK_WITH:
-          verbButtonLabel = 'PACK_WITH_VERB_BUTTON_LABEL';
-          break;
-        case chrome.fileManagerPrivate.Verb.SHARE_WITH:
-          // Even when the task has SHARE_WITH verb, we don't prefix the title
-          // with "Share with" when the task is from SEND/SEND_MULTIPLE intent
-          // handlers from Android apps, since the title can already have an
-          // appropriate verb.
-          if (!(taskParts[1] == 'arc' &&
-                (taskParts[2] == 'send' || taskParts[2] == 'send_multiple'))) {
-            verbButtonLabel = 'SHARE_WITH_VERB_BUTTON_LABEL';
-          }
-          break;
-        case chrome.fileManagerPrivate.Verb.OPEN_WITH:
-          verbButtonLabel = 'OPEN_WITH_VERB_BUTTON_LABEL';
-          break;
-        default:
-          console.error('Invalid task verb: ' + task.verb + '.');
-      }
-      if (verbButtonLabel) {
-        task.label = loadTimeData.getStringF(verbButtonLabel, task.title);
-      }
-    }
-
-    result.push(task);
-  }
-
-  return result;
-};
-
-/**
- * @param {!Entry} entry
- * @param {!VolumeManager} volumeManager
- * @return {boolean} True if the entry is from crostini.
- */
-FileTasks.isCrostiniEntry = (entry, volumeManager) => {
-  return volumeManager.getLocationInfo(entry).rootType ===
-      VolumeManagerCommon.RootType.CROSTINI;
-};
-
-/**
- * Returns true if task requires entries to be shared before executing task.
- * @param {!chrome.fileManagerPrivate.FileTask} task Task to run.
- * @return {boolean} true if task requires entries to be shared.
- */
-FileTasks.taskRequiresCrostiniSharing = task => {
-  const taskParts = task.taskId.split('|');
-  const taskType = taskParts[1];
-  const actionId = taskParts[2];
-  return taskType === 'crostini' || actionId === 'install-linux-package';
-};
-
-/**
- * Checks if task is a crostini task and all entries are accessible to, or can
- * be shared with crostini.  Shares files as required if possible and invokes
- * callback, or shows Unable to Open error dialog and does not invoke callback.
- * @param {!chrome.fileManagerPrivate.FileTask} task Task to run.
- * @param {function()} callback Callback is called when all files (if any) are
- *   accessible to crostini, else error dialog is shown.
- * @private
- */
-FileTasks.prototype.maybeShareWithCrostiniOrShowDialog_ = function(
-    task, callback) {
-  // Check if this is a crostini task.
-  if (!FileTasks.taskRequiresCrostiniSharing(task)) {
-    return callback();
-  }
-
-  let showUnableToOpen = false;
-  const entriesToShare = [];
-
-  for (let i = 0; i < this.entries_.length; i++) {
-    const entry = this.entries_[i];
-    if (FileTasks.isCrostiniEntry(entry, this.volumeManager_) ||
-        this.crostini_.isPathShared(constants.DEFAULT_CROSTINI_VM, entry)) {
-      continue;
-    }
-    if (!this.crostini_.canSharePath(
-            constants.DEFAULT_CROSTINI_VM, entry, false /* persist */)) {
-      showUnableToOpen = true;
-      break;
-    }
-    entriesToShare.push(entry);
-  }
-
-  // Show unable to open alert dialog.
-  if (showUnableToOpen) {
-    this.ui_.alertDialog.showHtml(
-        strf('UNABLE_TO_OPEN_CROSTINI_TITLE', task.title),
-        strf('UNABLE_TO_OPEN_CROSTINI', task.title));
-    FileTasks.recordCrostiniShareDialogTypeUMA_(
-        FileTasks.CrostiniShareDialogType.UnableToOpen);
-    return;
-  }
-
-  // No sharing required.
-  if (entriesToShare.length === 0) {
-    FileTasks.recordCrostiniShareDialogTypeUMA_(
-        FileTasks.CrostiniShareDialogType.None);
-    return callback();
-  }
-
-  // Share then invoke callback.
-  FileTasks.recordCrostiniShareDialogTypeUMA_(
-      FileTasks.CrostiniShareDialogType.ShareBeforeOpen);
-  // Set persist to false when sharing paths to open with a crostini app.
-  chrome.fileManagerPrivate.sharePathsWithCrostini(
-      constants.DEFAULT_CROSTINI_VM, entriesToShare, false /* persist */,
-      () => {
-        // It is unexpected to get an error sharing any files since we have
-        // already validated that all selected files can be shared.
-        // But if it happens, log error, and do not execute callback.
-        if (chrome.runtime.lastError) {
-          return console.error(
-              'Error sharing with linux to execute: ' +
-              chrome.runtime.lastError.message);
-        }
-        // crbug.com/925973.  Do not register non-persisted shared paths since
-        // we can't be sure at any time that the VM has not restarted and they
-        // are still shared.
-        callback();
-      });
-};
-
-/**
- * Executes default task.
- *
- * @param {function(boolean, Array<!Entry>)=} opt_callback Called when the
- *     default task is executed, or the error is occurred.
- */
-FileTasks.prototype.executeDefault = function(opt_callback) {
-  FileTasks.recordViewingFileTypeUMA_(this.volumeManager_, this.entries_);
-  FileTasks.recordViewingRootTypeUMA_(
-      this.volumeManager_, this.directoryModel_.getCurrentRootType());
-  this.executeDefaultInternal_(opt_callback);
-};
-
-/**
- * Executes default task.
- *
- * @param {function(boolean, Array<!Entry>)=} opt_callback Called when the
- *     default task is executed, or the error is occurred.
- * @private
- */
-FileTasks.prototype.executeDefaultInternal_ = function(opt_callback) {
-  const callback = opt_callback || ((arg1, arg2) => {});
-
-  if (this.defaultTask_ !== null) {
-    this.executeInternal_(this.defaultTask_);
-    callback(true, this.entries_);
-    return;
-  }
-
-  const nonGenericTasks = this.tasks_.filter(t => !t.isGenericFileHandler);
-  // If there is only one task that is not a generic file handler, it should be
-  // executed as a default task. If there are multiple tasks that are not
-  // generic file handlers, and none of them are considered as default, we show
-  // a task picker to ask the user to choose one.
-  if (nonGenericTasks.length >= 2) {
-    this.showTaskPicker(
-        this.ui_.defaultTaskPicker, str('OPEN_WITH_BUTTON_LABEL'), '', task => {
-          this.execute(task);
-        }, FileTasks.TaskPickerType.OpenWith);
-    return;
-  }
-
-  // We don't have tasks, so try to show a file in a browser tab.
-  // We only do that for single selection to avoid confusion.
-  if (this.entries_.length !== 1) {
-    return;
-  }
-
-  const filename = this.entries_[0].name;
-  const extension = util.splitExtension(filename)[1] || null;
-  const mimeType = this.mimeTypes_[0] || null;
-
-  const showAlert = () => {
-    let textMessageId;
-    let titleMessageId;
-    switch (extension) {
-      case '.exe':
-      case '.msi':
-        textMessageId = 'NO_TASK_FOR_EXECUTABLE';
-        break;
-      case '.dmg':
-        textMessageId = 'NO_TASK_FOR_DMG';
-        break;
-      case '.crx':
-        textMessageId = 'NO_TASK_FOR_CRX';
-        titleMessageId = 'NO_TASK_FOR_CRX_TITLE';
-        break;
-      default:
-        textMessageId = 'NO_TASK_FOR_FILE';
-    }
-
-    const webStoreUrl = webStoreUtils.createWebStoreLink(extension, mimeType);
-    const text = strf(textMessageId, webStoreUrl, str('NO_TASK_FOR_FILE_URL'));
-    const title = titleMessageId ? str(titleMessageId) : filename;
-    this.ui_.alertDialog.showHtml(title, text, null, null, null);
-    callback(false, this.entries_);
-  };
-
-  const onViewFilesFailure = () => {
-    if (extension &&
-        (FileTasks.EXTENSIONS_TO_SKIP_SUGGEST_APPS_.indexOf(extension) !== -1 ||
-         constants.EXECUTABLE_EXTENSIONS.indexOf(assert(extension)) !== -1)) {
-      showAlert();
-      return;
-    }
-
-    this.openSuggestAppsDialog(
-        () => {
-          FileTasks
-              .create(
-                  this.volumeManager_, this.metadataModel_,
-                  this.directoryModel_, this.ui_, this.entries_,
-                  this.mimeTypes_, this.taskHistory_, this.namingController_,
-                  this.crostini_)
-              .then(
-                  tasks => {
-                    tasks.executeDefault();
-                    callback(true, this.entries_);
-                  },
-                  () => {
-                    callback(false, this.entries_);
-                  });
-        },
-        () => {
-          callback(false, this.entries_);
-        },
-        showAlert);
-  };
-
-  const onViewFiles = result => {
-    switch (result) {
-      case 'opened':
-        callback(true, this.entries_);
-        break;
-      case 'message_sent':
-        util.isTeleported(window).then(teleported => {
-          if (teleported) {
-            this.ui_.showOpenInOtherDesktopAlert(this.entries_);
-          }
-        });
-        callback(true, this.entries_);
-        break;
-      case 'empty':
-        callback(true, this.entries_);
-        break;
-      case 'failed':
-        // Suppress the Unchecked runtime.lastError console message
-        if (chrome.runtime.lastError) {
-          console.debug(chrome.runtime.lastError.message);
-        }
-        onViewFilesFailure();
-        break;
-    }
-  };
-
-  this.checkAvailability_(() => {
-    const taskId = chrome.runtime.id + '|file|view-in-browser';
-    chrome.fileManagerPrivate.executeTask(taskId, this.entries_, onViewFiles);
-  });
-};
-
-/**
- * Executes a single task.
- *
- * @param {chrome.fileManagerPrivate.FileTask} task FileTask.
- */
-FileTasks.prototype.execute = function(task) {
-  FileTasks.recordViewingFileTypeUMA_(this.volumeManager_, this.entries_);
-  FileTasks.recordViewingRootTypeUMA_(
-      this.volumeManager_, this.directoryModel_.getCurrentRootType());
-  this.executeInternal_(task);
-};
-
-/**
- * The core implementation to execute a single task.
- *
- * @param {chrome.fileManagerPrivate.FileTask} task FileTask.
- * @private
- */
-FileTasks.prototype.executeInternal_ = function(task) {
-  this.checkAvailability_(() => {
-    this.maybeShareWithCrostiniOrShowDialog_(task, () => {
-      this.taskHistory_.recordTaskExecuted(task.taskId);
-      let msg;
-      if (this.entries.length === 1) {
-        msg = strf('OPEN_A11Y', this.entries_[0].name);
-      } else {
-        msg = strf('OPEN_A11Y_PLURAL', this.entries_.length);
-      }
-      this.ui_.speakA11yMessage(msg);
-      if (FileTasks.isInternalTask_(task.taskId)) {
-        this.executeInternalTask_(task.taskId);
-      } else {
-        FileTasks.recordZipHandlerUMA_(task.taskId);
-        chrome.fileManagerPrivate.executeTask(
-            task.taskId, this.entries_, (result) => {
-              if (result !== 'message_sent') {
-                return;
-              }
-              util.isTeleported(window).then((teleported) => {
-                if (teleported) {
-                  this.ui_.showOpenInOtherDesktopAlert(this.entries_);
-                }
-              });
-            });
-      }
-    });
-  });
-};
-
-/**
- * Ensures that the all files are available right now.
- *
- * Must not call before initialization.
- * @param {function()} callback Called when checking is completed and all files
- *     are available. Otherwise not called.
- * @private
- */
-FileTasks.prototype.checkAvailability_ = function(callback) {
-  const areAll = (entries, props, name) => {
-    // TODO(cmihail): Make files in directories available offline.
-    // See http://crbug.com/569767.
-    let okEntriesNum = 0;
-    for (let i = 0; i < entries.length; i++) {
-      // If got no properties, we safely assume that item is available.
-      if (props[i] && (props[i][name] || entries[i].isDirectory)) {
-        okEntriesNum++;
-      }
-    }
-    return okEntriesNum === props.length;
-  };
-
-  const containsDriveEntries = this.entries_.some(entry => {
-    const volumeInfo = this.volumeManager_.getVolumeInfo(entry);
-    return volumeInfo &&
-        volumeInfo.volumeType === VolumeManagerCommon.VolumeType.DRIVE;
-  });
-
-  // Availability is not checked for non-Drive files, as availableOffline, nor
-  // availableWhenMetered are not exposed for other types of volumes at this
-  // moment.
-  if (!containsDriveEntries) {
-    callback();
-    return;
-  }
-
-  const isDriveOffline = this.volumeManager_.getDriveConnectionState().type ===
-      VolumeManagerCommon.DriveConnectionType.OFFLINE;
-
-  if (isDriveOffline) {
-    this.metadataModel_.get(this.entries_, ['availableOffline', 'hosted'])
-        .then(props => {
-          if (areAll(this.entries_, props, 'availableOffline')) {
-            callback();
-            return;
-          }
-
-          this.ui_.alertDialog.showHtml(
-              loadTimeData.getString('OFFLINE_HEADER'),
-              props[0].hosted ?
-                  loadTimeData.getStringF(
-                      this.entries_.length === 1 ?
-                          'HOSTED_OFFLINE_MESSAGE' :
-                          'HOSTED_OFFLINE_MESSAGE_PLURAL') :
-                  loadTimeData.getStringF(
-                      this.entries_.length === 1 ? 'OFFLINE_MESSAGE' :
-                                                   'OFFLINE_MESSAGE_PLURAL',
-                      loadTimeData.getString('OFFLINE_COLUMN_LABEL')),
-              null, null, null);
-        });
-    return;
-  }
-
-  const isOnMetered = this.volumeManager_.getDriveConnectionState().type ===
-      VolumeManagerCommon.DriveConnectionType.METERED;
-
-  if (isOnMetered) {
-    this.metadataModel_.get(this.entries_, ['availableWhenMetered', 'size'])
-        .then(props => {
-          if (areAll(this.entries_, props, 'availableWhenMetered')) {
-            callback();
-            return;
-          }
-
-          let sizeToDownload = 0;
-          for (let i = 0; i !== this.entries_.length; i++) {
-            if (!props[i].availableWhenMetered) {
-              sizeToDownload += props[i].size;
-            }
-          }
-          this.ui_.confirmDialog.show(
-              loadTimeData.getStringF(
-                  this.entries_.length === 1 ? 'CONFIRM_MOBILE_DATA_USE' :
-                                               'CONFIRM_MOBILE_DATA_USE_PLURAL',
-                  util.bytesToString(sizeToDownload)),
-              callback, null, null);
-        });
-    return;
-  }
-
-  callback();
-};
-
-/**
- * Executes an internal task.
- *
- * @param {string} taskId The task id.
- * @private
- */
-FileTasks.prototype.executeInternalTask_ = function(taskId) {
-  const taskParts = taskId.split('|');
-  if (taskParts[2] === 'mount-archive') {
-    this.mountArchivesInternal_();
-    return;
-  }
-  if (taskParts[2] === 'install-linux-package') {
-    this.installLinuxPackageInternal_();
-    return;
-  }
-
-  console.error('The specified task is not a valid internal task: ' + taskId);
-};
-
-/**
- * Install a Linux Package in the Linux container.
- * @private
- */
-FileTasks.prototype.installLinuxPackageInternal_ = function() {
-  assert(this.entries_.length === 1);
-  this.ui_.installLinuxPackageDialog.showInstallLinuxPackageDialog(
-      this.entries_[0]);
-};
-
-/**
- * The core implementation of mounts archives.
- * @private
- */
-FileTasks.prototype.mountArchivesInternal_ = function() {
-  const tracker = this.directoryModel_.createDirectoryChangeTracker();
-  tracker.start();
-
-  // TODO(mtomasz): Move conversion from entry to url to custom bindings.
-  // crbug.com/345527.
-  const urls = util.entriesToURLs(this.entries_);
-  for (let index = 0; index < urls.length; ++index) {
-    // TODO(mtomasz): Pass Entry instead of URL.
-    this.volumeManager_.mountArchive(urls[index], volumeInfo => {
-      if (tracker.hasChanged) {
-        tracker.stop();
-        return;
-      }
-      volumeInfo.resolveDisplayRoot(
-          displayRoot => {
-            if (tracker.hasChanged) {
-              tracker.stop();
-              return;
-            }
-            this.directoryModel_.changeDirectoryEntry(displayRoot);
-          },
-          () => {
-            console.warn('Failed to resolve the display root after mounting.');
-            tracker.stop();
-          });
-    }, ((url, error) => {
-         tracker.stop();
-         const path = util.extractFilePath(url);
-         const namePos = path.lastIndexOf('/');
-         this.ui_.alertDialog.show(
-             strf('ARCHIVE_MOUNT_FAILED', path.substr(namePos + 1), error),
-             null, null);
-       }).bind(null, urls[index]));
-  }
-};
-
-/**
- * Displays the list of tasks in a open task picker combobutton and a share
- * options menu.
- *
- * @param {!cr.ui.ComboButton} openCombobutton The open task picker combobutton.
- * @param {!cr.ui.MultiMenuButton} shareMenuButton Button for share options.
- * @public
- */
-FileTasks.prototype.display = function(openCombobutton, shareMenuButton) {
-  const openTasks = [];
-  const otherTasks = [];
-  for (let i = 0; i < this.tasks_.length; i++) {
-    const task = this.tasks_[i];
-    if (FileTasks.isOpenTask(task)) {
-      openTasks.push(task);
-    } else {
-      otherTasks.push(task);
-    }
-  }
-  this.updateOpenComboButton_(openCombobutton, openTasks);
-  this.updateShareMenuButton_(shareMenuButton, otherTasks);
-};
-
-/**
- * Setup a task picker combobutton based on the given tasks.
- * @param {!cr.ui.ComboButton} combobutton
- * @param {!Array<!chrome.fileManagerPrivate.FileTask>} tasks
- */
-FileTasks.prototype.updateOpenComboButton_ = function(combobutton, tasks) {
-  combobutton.hidden = tasks.length == 0;
-  if (tasks.length == 0) {
-    return;
-  }
-
-  combobutton.clear();
-
-  // If there exist defaultTask show it on the combobutton.
-  if (this.defaultTask_) {
-    combobutton.defaultItem =
-        this.createCombobuttonItem_(this.defaultTask_, str('TASK_OPEN'));
-  } else {
-    combobutton.defaultItem = {
-      type: FileTasks.TaskMenuButtonItemType.ShowMenu,
-      label: str('OPEN_WITH_BUTTON_LABEL')
-    };
-  }
-
-  // If there exist 2 or more available tasks, show them in context menu
-  // (including defaultTask). If only one generic task is available, we
-  // also show it in the context menu.
-  const items = this.createItems_(tasks);
-  if (items.length > 1 || (items.length === 1 && this.defaultTask_ === null)) {
-    for (let j = 0; j < items.length; j++) {
-      combobutton.addDropDownItem(items[j]);
-    }
-
-    // If there exist non generic task (i.e. defaultTask is set), we show
-    // an item to change default task.
-    if (this.defaultTask_) {
-      combobutton.addSeparator();
-      const changeDefaultMenuItem = combobutton.addDropDownItem({
-        type: FileTasks.TaskMenuButtonItemType.ChangeDefaultTask,
-        label: loadTimeData.getString('CHANGE_DEFAULT_MENU_ITEM')
-      });
-      changeDefaultMenuItem.classList.add('change-default');
-    }
-  }
-};
-
 /**
  * The number of menu-item entries in the top level menu
  * before we split and show the 'More actions' option
  * @const {number}
  */
 const NUM_TOP_LEVEL_ENTRIES = 6;
+
 /**
  * Don't split the menu if the number of entries is smaller
  * than this. e.g. with 7 entries it'd be poor to show a
@@ -1145,124 +1319,6 @@
 const MAX_NON_SPLIT_ENTRIES = 10;
 
 /**
- * Setup a menu button for sharing options based on the given tasks.
- * @param {!cr.ui.MultiMenuButton} shareMenuButton
- * @param {!Array<!chrome.fileManagerPrivate.FileTask>} tasks
- */
-FileTasks.prototype.updateShareMenuButton_ = function(shareMenuButton, tasks) {
-  let driveShareCommand =
-      shareMenuButton.menu.querySelector('cr-menu-item[command="#share"]');
-  let driveShareCommandSeparator =
-      shareMenuButton.menu.querySelector('#drive-share-separator');
-  let moreActionsSeparator =
-      shareMenuButton.menu.querySelector('#more-actions-separator');
-
-  // Hide share icon for New Folder creation.  See https://crbug.com/571355.
-  shareMenuButton.hidden = (driveShareCommand.disabled && tasks.length == 0) ||
-      this.namingController_.isRenamingInProgress();
-  moreActionsSeparator.hidden = true;
-
-  // Show the separator if Drive share command is enabled and there is at least
-  // one other share actions.
-  driveShareCommandSeparator.hidden =
-      driveShareCommand.disabled || tasks.length == 0;
-
-  // Temporarily remove the more actions item while the rest of the menu
-  // items are being cleared out so we don't lose it and make it hidden for now
-  let moreActions = shareMenuButton.menu.querySelector(
-      'cr-menu-item[command="#show-submenu"]');
-  moreActions.remove();
-  moreActions.setAttribute('hidden', '');
-  // Remove the separator as well
-  moreActionsSeparator.remove();
-
-  // Clear menu items except for drive share menu and a separator for it.
-  // As querySelectorAll() returns live NodeList, we need to copy elements to
-  // Array object to modify DOM in the for loop.
-  const itemsToRemove = [].slice.call(shareMenuButton.menu.querySelectorAll(
-      'cr-menu-item:not([command="#share"])'));
-  for (let i = 0; i < itemsToRemove.length; i++) {
-    const item = itemsToRemove[i];
-    item.parentNode.removeChild(item);
-  }
-  // Clear menu items in the overflow sub-menu since we'll repopulate it
-  // with any relevant items below.
-  if (shareMenuButton.overflow !== null) {
-    while (shareMenuButton.overflow.firstChild !== null) {
-      shareMenuButton.overflow.removeChild(shareMenuButton.overflow.firstChild);
-    }
-  }
-
-  // Add menu items for the new tasks.
-  const items = this.createItems_(tasks);
-  let menu = /** @type {!cr.ui.Menu} */ (shareMenuButton.menu);
-  for (let i = 0; i < items.length; i++) {
-    // If we have at least 10 entries, split off into a sub-menu
-    if (i == NUM_TOP_LEVEL_ENTRIES && MAX_NON_SPLIT_ENTRIES <= items.length) {
-      moreActions.removeAttribute('hidden');
-      moreActionsSeparator.hidden = false;
-      menu = shareMenuButton.overflow;
-    }
-    const menuitem = menu.addMenuItem(items[i]);
-    cr.ui.decorate(menuitem, cr.ui.FilesMenuItem);
-    menuitem.data = items[i];
-    if (items[i].iconType) {
-      menuitem.style.backgroundImage = '';
-      menuitem.setAttribute('file-type-icon', items[i].iconType);
-    }
-  }
-  // Replace the more actions menu item and separator
-  shareMenuButton.menu.appendChild(moreActionsSeparator);
-  shareMenuButton.menu.appendChild(moreActions);
-};
-
-/**
- * Creates sorted array of available task descriptions such as title and icon.
- *
- * @param {!Array<!chrome.fileManagerPrivate.FileTask>} tasks Tasks to create
- *     items.
- * @return {!Array<!FileTasks.ComboButtonItem>} Created array can be used to
- *     feed combobox, menus and so on.
- * @private
- */
-FileTasks.prototype.createItems_ = function(tasks) {
-  const items = [];
-
-  // Create items.
-  for (let index = 0; index < tasks.length; index++) {
-    const task = tasks[index];
-    if (task === this.defaultTask_) {
-      const title =
-          task.title + ' ' + loadTimeData.getString('DEFAULT_TASK_LABEL');
-      items.push(this.createCombobuttonItem_(task, title, true, true));
-    } else {
-      items.push(this.createCombobuttonItem_(task));
-    }
-  }
-
-  // Sort items (Sort order: isDefault, lastExecutedTime, label).
-  items.sort((a, b) => {
-    // Sort by isDefaultTask.
-    const isDefault = (b.isDefault ? 1 : 0) - (a.isDefault ? 1 : 0);
-    if (isDefault !== 0) {
-      return isDefault;
-    }
-
-    // Sort by last-executed time.
-    const aTime = this.taskHistory_.getLastExecutedTime(a.task.taskId);
-    const bTime = this.taskHistory_.getLastExecutedTime(b.task.taskId);
-    if (aTime != bTime) {
-      return bTime - aTime;
-    }
-
-    // Sort by label.
-    return a.label.localeCompare(b.label);
-  });
-
-  return items;
-};
-
-/**
  * @typedef {{
  *   type: !FileTasks.TaskMenuButtonItemType,
  *   label: string,
@@ -1275,92 +1331,3 @@
  * }}
  */
 FileTasks.ComboButtonItem;
-
-/**
- * Creates combobutton item based on task.
- *
- * @param {!chrome.fileManagerPrivate.FileTask} task Task to convert.
- * @param {string=} opt_title Title.
- * @param {boolean=} opt_bold Make a menu item bold.
- * @param {boolean=} opt_isDefault Mark the item as default item.
- * @return {!FileTasks.ComboButtonItem} Item appendable to combobutton drop-down
- *     list.
- * @private
- */
-FileTasks.prototype.createCombobuttonItem_ =
-    (task, opt_title, opt_bold, opt_isDefault) => {
-      return {
-        type: FileTasks.TaskMenuButtonItemType.RunTask,
-        label: opt_title || task.label || task.title,
-        iconUrl: task.iconUrl || '',
-        iconType: task.iconType || '',
-        task: task,
-        bold: opt_bold || false,
-        isDefault: opt_isDefault || false,
-        isGenericFileHandler: /** @type {boolean} */ (task.isGenericFileHandler)
-      };
-    };
-
-/**
- * Shows modal task picker dialog with currently available list of tasks.
- *
- * @param {cr.filebrowser.DefaultTaskDialog} taskDialog Task dialog to show and
- *     update.
- * @param {string} title Title to use.
- * @param {string} message Message to use.
- * @param {function(!chrome.fileManagerPrivate.FileTask)} onSuccess Callback to
- *     pass selected task.
- * @param {FileTasks.TaskPickerType} pickerType Task picker type.
- */
-FileTasks.prototype.showTaskPicker = function(
-    taskDialog, title, message, onSuccess, pickerType) {
-  const tasks = pickerType == FileTasks.TaskPickerType.MoreActions ?
-      this.getNonOpenTaskItems() :
-      this.getOpenTaskItems();
-  let items = this.createItems_(tasks);
-  if (pickerType == FileTasks.TaskPickerType.ChangeDefault) {
-    items = items.filter(item => !item.isGenericFileHandler);
-  }
-
-  let defaultIdx = 0;
-  for (let j = 0; j < items.length; j++) {
-    if (this.defaultTask_ &&
-        items[j].task.taskId === this.defaultTask_.taskId) {
-      defaultIdx = j;
-    }
-  }
-
-  taskDialog.showDefaultTaskDialog(title, message, items, defaultIdx, item => {
-    onSuccess(item.task);
-  });
-};
-
-/**
- * Gets the default task from tasks. In case there is no such task (i.e. all
- * tasks are generic file handlers), then return null.
- *
- * @param {!Array<!chrome.fileManagerPrivate.FileTask>} tasks The list of tasks
- *     from where to choose the default task.
- * @param {!TaskHistory} taskHistory
- * @return {?chrome.fileManagerPrivate.FileTask} the default task, or null if
- *     no default task found.
- */
-FileTasks.getDefaultTask = (tasks, taskHistory) => {
-  // 1. Default app set for MIME or file extension by user, or built-in app.
-  for (let i = 0; i < tasks.length; i++) {
-    if (tasks[i].isDefault) {
-      return tasks[i];
-    }
-  }
-  const nonGenericTasks = tasks.filter(t => !t.isGenericFileHandler);
-  // 2. Most recently executed non-generic task.
-  const latest = nonGenericTasks[0];
-  if (latest && taskHistory.getLastExecutedTime(latest.taskId)) {
-    return latest;
-  }
-  // 3. Sole non-generic handler.
-  if (nonGenericTasks.length == 1) {
-    return nonGenericTasks[0];
-  }
-  return null;
-};
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
index 48ec7bf..e44c6eef 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -725,7 +725,7 @@
   /**
    * Preloads an image thumbnail for the specified file entry.
    *
-   * @param {Entry} entry Entry to preload a thumbnail for.
+   * @param {!Entry} entry Entry to preload a thumbnail for.
    * @private
    */
   preloadThumbnailImage_(entry) {
diff --git a/ui/file_manager/file_manager/foreground/js/gear_menu_controller.js b/ui/file_manager/file_manager/foreground/js/gear_menu_controller.js
index 1a8526a9..aa1ed75a 100644
--- a/ui/file_manager/file_manager/foreground/js/gear_menu_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/gear_menu_controller.js
@@ -2,180 +2,157 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * @param {!cr.ui.MultiMenuButton} gearButton
- * @param {!FilesToggleRipple} toggleRipple
- * @param {!GearMenu} gearMenu
- * @param {!ProvidersMenu} providersMenu
- * @param {!DirectoryModel} directoryModel
- * @param {!CommandHandler} commandHandler
- * @param {!ProvidersModel} providersModel
- * @constructor
- * @struct
- */
-function GearMenuController(
-    gearButton, toggleRipple, gearMenu, providersMenu, directoryModel,
-    commandHandler, providersModel) {
+class GearMenuController {
   /**
-   * @type {!FilesToggleRipple}
-   * @const
-   * @private
+   * @param {!cr.ui.MultiMenuButton} gearButton
+   * @param {!FilesToggleRipple} toggleRipple
+   * @param {!GearMenu} gearMenu
+   * @param {!ProvidersMenu} providersMenu
+   * @param {!DirectoryModel} directoryModel
+   * @param {!CommandHandler} commandHandler
+   * @param {!ProvidersModel} providersModel
    */
-  this.toggleRipple_ = toggleRipple;
+  constructor(
+      gearButton, toggleRipple, gearMenu, providersMenu, directoryModel,
+      commandHandler, providersModel) {
+    /** @private @const {!FilesToggleRipple} */
+    this.toggleRipple_ = toggleRipple;
 
-  /**
-   * @type {!GearMenu}
-   * @const
-   * @private
-   */
-  this.gearMenu_ = gearMenu;
+    /** @private @const {!GearMenu} */
+    this.gearMenu_ = gearMenu;
 
-  /**
-   * @type {!ProvidersMenu}
-   * @const
-   * @private
-   */
-  this.providersMenu_ = providersMenu;
+    /** @private @const {!ProvidersMenu} */
+    this.providersMenu_ = providersMenu;
 
-  /**
-   * @type {!DirectoryModel}
-   * @const
-   * @private
-   */
-  this.directoryModel_ = directoryModel;
+    /** @private @const {!DirectoryModel} */
+    this.directoryModel_ = directoryModel;
 
-  /**
-   * @type {!CommandHandler}
-   * @const
-   * @private
-   */
-  this.commandHandler_ = commandHandler;
+    /** @private @const {!CommandHandler} */
+    this.commandHandler_ = commandHandler;
 
-  /**
-   * @type {!ProvidersModel}
-   * @const
-   * @private
-   */
-  this.providersModel_ = providersModel;
+    /** @private @const {!ProvidersModel} */
+    this.providersModel_ = providersModel;
 
-  gearButton.addEventListener('menushow', this.onShowGearMenu_.bind(this));
-  gearButton.addEventListener('menuhide', this.onHideGearMenu_.bind(this));
-  directoryModel.addEventListener(
-      'directory-changed', this.onDirectoryChanged_.bind(this));
-  chrome.fileManagerPrivate.onPreferencesChanged.addListener(
-      this.onPreferencesChanged_.bind(this));
-  this.onPreferencesChanged_();
-}
-
-/**
- * @private
- */
-GearMenuController.prototype.onShowGearMenu_ = function() {
-  this.toggleRipple_.activated = true;
-  this.refreshRemainingSpace_(false); /* Without loading caption. */
-  this.updateNewServiceItem();
-};
-
-/**
- * Update "New service" menu item to either directly show the Webstore dialog
- * when there isn't any service/FSP extension installed, or display the
- * providers menu with the currently installed extensions and also install new
- * service.
- *
- * @private
- */
-GearMenuController.prototype.updateNewServiceItem = function() {
-  this.providersModel_.getMountableProviders().then(providers => {
-    // Go straight to webstore to install the first provider.
-    let desiredMenu = '#install-new-extension';
-    let label = str('INSTALL_NEW_EXTENSION_LABEL');
-
-    const shouldDisplayProvidersMenu = providers.length > 0;
-    if (shouldDisplayProvidersMenu) {
-      // Open the providers menu with an installed provider and an install new
-      // provider option.
-      desiredMenu = '#new-service';
-      label = str('ADD_NEW_SERVICES_BUTTON_LABEL');
-      // Trigger an update of the providers submenu.
-      this.providersMenu_.updateSubMenu();
-    }
-
-    this.gearMenu_.setNewServiceCommand(desiredMenu, label);
-  });
-};
-
-/**
- * @private
- */
-GearMenuController.prototype.onHideGearMenu_ = function() {
-  this.toggleRipple_.activated = false;
-};
-
-/**
- * @param {Event} event
- * @private
- */
-GearMenuController.prototype.onDirectoryChanged_ = function(event) {
-  event = /** @type {DirectoryChangeEvent} */ (event);
-  if (event.volumeChanged) {
-    this.refreshRemainingSpace_(true);
-  }  // Show loading caption.
-};
-
-/**
- * Refreshes space info of the current volume.
- * @param {boolean} showLoadingCaption Whether show loading caption or not.
- * @private
- */
-GearMenuController.prototype.refreshRemainingSpace_ = function(
-    showLoadingCaption) {
-  const currentDirectory = this.directoryModel_.getCurrentDirEntry();
-  if (!currentDirectory || util.isRecentRoot(currentDirectory)) {
-    this.gearMenu_.setSpaceInfo(null, false);
-    return;
+    gearButton.addEventListener('menushow', this.onShowGearMenu_.bind(this));
+    gearButton.addEventListener('menuhide', this.onHideGearMenu_.bind(this));
+    directoryModel.addEventListener(
+        'directory-changed', this.onDirectoryChanged_.bind(this));
+    chrome.fileManagerPrivate.onPreferencesChanged.addListener(
+        this.onPreferencesChanged_.bind(this));
+    this.onPreferencesChanged_();
   }
 
-  const currentVolumeInfo = this.directoryModel_.getCurrentVolumeInfo();
-  if (!currentVolumeInfo) {
-    return;
+  /**
+   * @private
+   */
+  onShowGearMenu_() {
+    this.toggleRipple_.activated = true;
+    this.refreshRemainingSpace_(false); /* Without loading caption. */
+    this.updateNewServiceItem();
   }
 
-  // TODO(mtomasz): Add support for remaining space indication for provided
-  // file systems.
-  // TODO(fukino): Add support for remaining space indication for documents
-  // provider roots. crbug.com/953657.
-  if (currentVolumeInfo.volumeType == VolumeManagerCommon.VolumeType.PROVIDED ||
-      currentVolumeInfo.volumeType ==
-          VolumeManagerCommon.VolumeType.MEDIA_VIEW ||
-      currentVolumeInfo.volumeType ==
-          VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER ||
-      currentVolumeInfo.volumeType == VolumeManagerCommon.VolumeType.ARCHIVE) {
-    this.gearMenu_.setSpaceInfo(null, false);
-    return;
+  /**
+   * Update "New service" menu item to either directly show the Webstore dialog
+   * when there isn't any service/FSP extension installed, or display the
+   * providers menu with the currently installed extensions and also install new
+   * service.
+   *
+   * @private
+   */
+  updateNewServiceItem() {
+    this.providersModel_.getMountableProviders().then(providers => {
+      // Go straight to webstore to install the first provider.
+      let desiredMenu = '#install-new-extension';
+      let label = str('INSTALL_NEW_EXTENSION_LABEL');
+
+      const shouldDisplayProvidersMenu = providers.length > 0;
+      if (shouldDisplayProvidersMenu) {
+        // Open the providers menu with an installed provider and an install new
+        // provider option.
+        desiredMenu = '#new-service';
+        label = str('ADD_NEW_SERVICES_BUTTON_LABEL');
+        // Trigger an update of the providers submenu.
+        this.providersMenu_.updateSubMenu();
+      }
+
+      this.gearMenu_.setNewServiceCommand(desiredMenu, label);
+    });
   }
 
-  this.gearMenu_.setSpaceInfo(
-      new Promise(fulfill => {
-        chrome.fileManagerPrivate.getSizeStats(
-            currentVolumeInfo.volumeId, fulfill);
-      }),
-      true);
-};
+  /**
+   * @private
+   */
+  onHideGearMenu_() {
+    this.toggleRipple_.activated = false;
+  }
 
-/**
- * Handles preferences change and updates menu.
- * @private
- */
-GearMenuController.prototype.onPreferencesChanged_ = function() {
-  chrome.fileManagerPrivate.getPreferences(prefs => {
-    if (chrome.runtime.lastError) {
+  /**
+   * @param {Event} event
+   * @private
+   */
+  onDirectoryChanged_(event) {
+    event = /** @type {DirectoryChangeEvent} */ (event);
+    if (event.volumeChanged) {
+      this.refreshRemainingSpace_(true);
+    }  // Show loading caption.
+  }
+
+  /**
+   * Refreshes space info of the current volume.
+   * @param {boolean} showLoadingCaption Whether show loading caption or not.
+   * @private
+   */
+  refreshRemainingSpace_(showLoadingCaption) {
+    const currentDirectory = this.directoryModel_.getCurrentDirEntry();
+    if (!currentDirectory || util.isRecentRoot(currentDirectory)) {
+      this.gearMenu_.setSpaceInfo(null, false);
       return;
     }
 
-    if (prefs.cellularDisabled) {
-      this.gearMenu_.syncButton.setAttribute('checked', '');
-    } else {
-      this.gearMenu_.syncButton.removeAttribute('checked');
+    const currentVolumeInfo = this.directoryModel_.getCurrentVolumeInfo();
+    if (!currentVolumeInfo) {
+      return;
     }
-  });
-};
+
+    // TODO(mtomasz): Add support for remaining space indication for provided
+    // file systems.
+    // TODO(fukino): Add support for remaining space indication for documents
+    // provider roots. crbug.com/953657.
+    if (currentVolumeInfo.volumeType ==
+            VolumeManagerCommon.VolumeType.PROVIDED ||
+        currentVolumeInfo.volumeType ==
+            VolumeManagerCommon.VolumeType.MEDIA_VIEW ||
+        currentVolumeInfo.volumeType ==
+            VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER ||
+        currentVolumeInfo.volumeType ==
+            VolumeManagerCommon.VolumeType.ARCHIVE) {
+      this.gearMenu_.setSpaceInfo(null, false);
+      return;
+    }
+
+    this.gearMenu_.setSpaceInfo(
+        new Promise(fulfill => {
+          chrome.fileManagerPrivate.getSizeStats(
+              currentVolumeInfo.volumeId, fulfill);
+        }),
+        true);
+  }
+
+  /**
+   * Handles preferences change and updates menu.
+   * @private
+   */
+  onPreferencesChanged_() {
+    chrome.fileManagerPrivate.getPreferences(prefs => {
+      if (chrome.runtime.lastError) {
+        return;
+      }
+
+      if (prefs.cellularDisabled) {
+        this.gearMenu_.syncButton.setAttribute('checked', '');
+      } else {
+        this.gearMenu_.syncButton.removeAttribute('checked');
+      }
+    });
+  }
+}
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller.js b/ui/file_manager/file_manager/foreground/js/import_controller.js
index 8d1a3b01..b7a2339 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/import_controller.js
@@ -19,89 +19,442 @@
 /**
  * Class that orchestrates background activity and UI changes on
  * behalf of Cloud Import.
- *
- * @constructor
- * @struct
- *
- * @param {!importer.ControllerEnvironment} environment The class providing
- *     access to runtime environmental information, like the current directory,
- *     volume lookup and so-on.
- * @param {!importer.MediaScanner} scanner
- * @param {!importer.ImportRunner} importRunner
- * @param {!importer.CommandWidget} commandWidget
  */
-importer.ImportController = function(
-    environment, scanner, importRunner, commandWidget) {
-  /** @private {!importer.ControllerEnvironment} */
-  this.environment_ = environment;
+importer.ImportController = class {
+  /**
+   * @param {!importer.ControllerEnvironment} environment The class providing
+   *     access to runtime environmental information, like the current
+   * directory, volume lookup and so-on.
+   * @param {!importer.MediaScanner} scanner
+   * @param {!importer.ImportRunner} importRunner
+   * @param {!importer.CommandWidget} commandWidget
+   */
+  constructor(environment, scanner, importRunner, commandWidget) {
+    /** @private @const {!importer.ControllerEnvironment} */
+    this.environment_ = environment;
 
-  /** @private {!importer.ChromeLocalStorage} */
-  this.storage_ = importer.ChromeLocalStorage.getInstance();
+    /** @private @const {!importer.ChromeLocalStorage} */
+    this.storage_ = importer.ChromeLocalStorage.getInstance();
 
-  /** @private {!importer.ImportRunner} */
-  this.importRunner_ = importRunner;
+    /** @private @const {!importer.ImportRunner} */
+    this.importRunner_ = importRunner;
 
-  /** @private {!importer.MediaScanner} */
-  this.scanner_ = scanner;
+    /** @private @const {!importer.MediaScanner} */
+    this.scanner_ = scanner;
 
-  /** @private {!importer.CommandWidget} */
-  this.commandWidget_ = commandWidget;
+    /** @private @const {!importer.CommandWidget} */
+    this.commandWidget_ = commandWidget;
 
-  /** @type {!importer.ScanManager} */
-  this.scanManager_ = new importer.ScanManager(environment, scanner);
+    /** @private @const {!importer.ScanManager} */
+    this.scanManager_ = new importer.ScanManager(environment, scanner);
+
+    /**
+     * The active import task, if any.
+     * @private {?importer.TaskDetails_}
+     */
+    this.activeImport_ = null;
+
+    /**
+     * The previous import task, if any.
+     * @private {?importer.TaskDetails_}
+     */
+    this.previousImport_ = null;
+
+    /** @private {!importer.ActivityState} */
+    this.lastActivityState_ = importer.ActivityState.HIDDEN;
+
+    /**
+     * Whether the window was opened by plugging a media device and user hadn't
+     * navigated to other directories.
+     * @private {boolean}
+     */
+    this.isRightAfterPluggingMedia_ = false;
+
+    const listener = this.onScanEvent_.bind(this);
+    this.scanner_.addObserver(listener);
+    // Remove the observer when the foreground window is closed.
+    window.addEventListener('pagehide', () => {
+      this.scanner_.removeObserver(listener);
+    });
+
+    this.environment_.addWindowCloseListener(this.onWindowClosing_.bind(this));
+
+    this.environment_.addVolumeUnmountListener(
+        this.onVolumeUnmounted_.bind(this));
+
+    this.environment_.addDirectoryChangedListener(
+        this.onDirectoryChanged_.bind(this));
+
+    this.environment_.addSelectionChangedListener(
+        this.onSelectionChanged_.bind(this));
+
+    this.commandWidget_.addClickListener(this.onClick_.bind(this));
+
+    this.storage_.get(importer.Setting.HAS_COMPLETED_IMPORT, false)
+        .then(
+            /** @param {boolean} importCompleted If so, we hide the banner */
+            importCompleted => {
+              this.commandWidget_.setDetailsBannerVisible(!importCompleted);
+            });
+  }
 
   /**
-   * The active import task, if any.
-   * @private {?importer.TaskDetails_}
+   * @param {!importer.ScanEvent} event Command event.
+   * @param {importer.ScanResult} scan
+   * @private
    */
-  this.activeImport_ = null;
+  onScanEvent_(event, scan) {
+    if (!this.scanManager_.isActiveScan(scan)) {
+      return;
+    }
+
+    switch (event) {
+      case importer.ScanEvent.INVALIDATED:
+        this.onScanInvalidated_();
+      case importer.ScanEvent.FINALIZED:
+      case importer.ScanEvent.UPDATED:
+        this.checkState_(scan);
+        break;
+    }
+  }
+
+  /** @private */
+  onWindowClosing_() {
+    this.scanManager_.reset();  // Will cancel any active scans.
+  }
 
   /**
-   * The previous import task, if any.
-   * @private {?importer.TaskDetails_}
+   * @param {string} volumeId
+   * @private
    */
-  this.previousImport_ = null;
+  onVolumeUnmounted_(volumeId) {
+    if (this.activeImport_) {
+      this.activeImport_.task.requestCancel();
+      this.finalizeActiveImport_();
+      metrics.recordBoolean('ImportController.DeviceYanked');
+    }
+    this.scanManager_.reset();
+    this.checkState_();
+  }
 
   /**
-   * @private {!importer.ActivityState}
+   * @param {!Event} event
+   * @private
    */
-  this.lastActivityState_ = importer.ActivityState.HIDDEN;
+  onDirectoryChanged_(event) {
+    this.isRightAfterPluggingMedia_ = !event.previousDirEntry;
+
+    this.scanManager_.reset();
+    if (this.isCurrentDirectoryScannable_()) {
+      this.checkState_(
+          this.scanManager_.getDirectoryScan(importer.ScanMode.HISTORY));
+    } else {
+      this.checkState_();
+    }
+  }
+
+  /** @private */
+  onSelectionChanged_() {
+    // NOTE: Empty selection changed events can and will fire for a directory
+    // before we receive the the corresponding directory changed event and when
+    // the selection is empty. These are spurious events and we ignore them.
+    if (!this.scanManager_.hasSelectionScan() &&
+        this.environment_.getSelection().length === 0) {
+      return;
+    }
+
+    // NOTE: We clear the scan here, but don't immediately initiate
+    // a new scan. checkState will attempt to initialize the scan
+    // during normal processing.
+    // Also, in the case the selection is transitioning to empty,
+    // we want to reinstate the underlying directory scan (if
+    // in fact, one is possible).
+    this.scanManager_.clearSelectionScan();
+    if (this.environment_.getSelection().length === 0 &&
+        this.isCurrentDirectoryScannable_()) {
+      this.checkState_(
+          this.scanManager_.getDirectoryScan(importer.ScanMode.HISTORY));
+    } else {
+      this.checkState_();
+    }
+  }
 
   /**
-   * Whether the window was opened by plugging a media device and user hadn't
-   * navigated to other directories.
-   * @private {boolean}
+   * @param {!importer.MediaImportHandler.ImportTask} task
+   * @private
    */
-  this.isRightAfterPluggingMedia_ = false;
+  onImportFinished_(task) {
+    this.finalizeActiveImport_();
+    this.scanManager_.reset();
+    this.storage_.set(importer.Setting.HAS_COMPLETED_IMPORT, true);
+    this.commandWidget_.setDetailsBannerVisible(false);
+    this.checkState_();
+  }
 
-  const listener = this.onScanEvent_.bind(this);
-  this.scanner_.addObserver(listener);
-  // Remove the observer when the foreground window is closed.
-  window.addEventListener('pagehide', () => {
-    this.scanner_.removeObserver(listener);
-  });
+  /** @private */
+  onScanInvalidated_() {
+    this.scanManager_.reset();
+    if (this.environment_.getSelection().length === 0 &&
+        this.isCurrentDirectoryScannable_()) {
+      this.checkState_(
+          this.scanManager_.getDirectoryScan(importer.ScanMode.HISTORY));
+    } else {
+      this.checkState_();
+    }
+  }
 
-  this.environment_.addWindowCloseListener(this.onWindowClosing_.bind(this));
+  /**
+   * Does book keeping necessary to finalize the active task.
+   * @private
+   */
+  finalizeActiveImport_() {
+    console.assert(
+        !!this.activeImport_, 'Cannot finish import when none is running.');
+    this.previousImport_ = this.activeImport_;
+    this.activeImport_ = null;
+  }
 
-  this.environment_.addVolumeUnmountListener(
-      this.onVolumeUnmounted_.bind(this));
+  /**
+   * Handles button clicks emenating from the panel or toolbar.
+   * @param {!importer.ClickSource} source
+   */
+  onClick_(source) {
+    switch (source) {
+      case importer.ClickSource.MAIN:
+        if (this.lastActivityState_ === importer.ActivityState.READY) {
+          this.commandWidget_.performMainButtonRippleAnimation();
+          this.execute_();
+        } else {
+          this.commandWidget_.toggleDetails();
+        }
+        break;
+      case importer.ClickSource.IMPORT:
+        this.execute_();
+        break;
+      case importer.ClickSource.CANCEL:
+        this.cancel_();
+        break;
+      case importer.ClickSource.DESTINATION:
+        if (this.activeImport_) {
+          this.environment_.showImportDestination(this.activeImport_.started);
+        } else if (this.previousImport_) {
+          this.environment_.showImportDestination(this.previousImport_.started);
+        } else {
+          this.environment_.showImportRoot();
+        }
+        break;
+      case importer.ClickSource.SIDE:
+        // Intentionally unhandled...panel controls toggling of details panel.
+        break;
+      default:
+        assertNotReached('Unhandled click source: ' + source);
+    }
+  }
 
-  this.environment_.addDirectoryChangedListener(
-      this.onDirectoryChanged_.bind(this));
+  /**
+   * Executes import against the current context. Should only be called
+   * after the current context has been validated by a scan.
+   *
+   * @private
+   */
+  startImportTask_() {
+    console.assert(
+        !this.activeImport_,
+        'Cannot execute while an import task is already active.');
 
-  this.environment_.addSelectionChangedListener(
-      this.onSelectionChanged_.bind(this));
+    const scan = this.scanManager_.getActiveScan();
+    assert(scan != null);
 
-  this.commandWidget_.addClickListener(this.onClick_.bind(this));
+    const startDate = new Date();
+    const importTask = this.importRunner_.importFromScanResult(
+        scan, importer.Destination.GOOGLE_DRIVE,
+        this.environment_.getImportDestination(startDate));
 
-  this.storage_.get(importer.Setting.HAS_COMPLETED_IMPORT, false)
-      .then(
-          /**
-           * @param {boolean} importCompleted If so, we hide the banner
-           */
-          importCompleted => {
-            this.commandWidget_.setDetailsBannerVisible(!importCompleted);
-          });
+    this.activeImport_ = {scan: scan, task: importTask, started: startDate};
+    const taskFinished = this.onImportFinished_.bind(this, importTask);
+    importTask.whenFinished.then(taskFinished).catch(taskFinished);
+  }
+
+  /**
+   * Executes import against the current context. Should only be called
+   * when user clicked an import button after the current context has
+   * been validated by a scan.
+   *
+   * @private
+   */
+  execute_() {
+    this.startImportTask_();
+    this.checkState_();
+  }
+
+  /**
+   * Cancels the active task.
+   * @private
+   */
+  cancel_() {
+    if (this.activeImport_) {
+      this.activeImport_.task.requestCancel();
+      this.finalizeActiveImport_();
+      metrics.recordBoolean('ImportController.ImportCancelled');
+    }
+
+    this.scanManager_.reset();
+    this.checkState_();
+  }
+
+  /**
+   * Checks the environment and updates UI as needed.
+   * @param {importer.ScanResult=} opt_scan If supplied,
+   * @private
+   */
+  checkState_(opt_scan) {
+    // If there is no Google Drive mount, Drive may be disabled
+    // or the machine may be running in guest mode.
+    if (!this.environment_.isGoogleDriveMounted()) {
+      this.updateUi_(importer.ActivityState.HIDDEN);
+      return;
+    }
+
+    if (this.activeImport_) {
+      this.updateUi_(importer.ActivityState.IMPORTING, this.activeImport_.scan);
+      return;
+    }
+
+    // If we don't have an existing scan, we'll try to create
+    // one. When we do end up creating one (not getting
+    // one from the cache) it'll be empty...even if there is
+    // a current selection. This is because scans are
+    // resolved asynchronously. And we like it that way.
+    // We'll get notification when the scan is updated. When
+    // that happens, we'll be called back with opt_scan
+    // set to that scan....and subsequently skip over this
+    // block to update the UI.
+    if (!opt_scan) {
+      // NOTE, that tryScan_ lazily initializes scans...so if
+      // no scan is returned, no scan is possible for the
+      // current context.
+      const scan = this.tryScan_(importer.ScanMode.HISTORY);
+      // If no scan is created, then no scan is possible in
+      // the current context...so hide the UI.
+      if (!scan) {
+        this.updateUi_(importer.ActivityState.HIDDEN);
+      }
+      return;
+    }
+
+    // At this point we have an existing scan, and a relatively
+    // validate environment for an import...so we'll proceed
+    // with making updates to the UI.
+    if (!opt_scan.isFinal()) {
+      this.updateUi_(importer.ActivityState.SCANNING, opt_scan);
+      return;
+    }
+
+    if (opt_scan.getFileEntries().length === 0) {
+      if (opt_scan.getDuplicateFileEntries().length === 0) {
+        this.updateUi_(importer.ActivityState.NO_MEDIA);
+        return;
+      }
+      // Some scanned files found already exist in Drive.
+      // It means those files weren't marked as already backed up but need to
+      // be. Trigger sync for that purpose, but no files will actually be
+      // copied.
+      this.startImportTask_();
+      this.updateUi_(importer.ActivityState.IMPORTING, this.activeImport_.scan);
+      return;
+    }
+
+    // We have a final scan that is either too big, or juuuussttt right.
+    Promise
+        .all([
+          this.environment_.getFreeStorageSpace(
+              VolumeManagerCommon.VolumeType.DOWNLOADS),
+          this.environment_.getFreeStorageSpace(
+              VolumeManagerCommon.VolumeType.DRIVE),
+        ])
+        .then(/** @param {Array<number>} availableSpace in bytes */
+              availableSpace => {
+                // TODO(smckay): We might want to disqualify some small amount
+                // of local storage in this calculation on the assumption that
+                // we don't want to completely max out storage...even though
+                // synced files will eventually be evicted from the cache.
+                if (availableSpace[0] < opt_scan.getStatistics().sizeBytes) {
+                  // Doesn't fit in local space.
+                  this.updateUi_(
+                      importer.ActivityState.INSUFFICIENT_LOCAL_SPACE, opt_scan,
+                      availableSpace[0]);
+                  return;
+                }
+                if (availableSpace[1] !== -1 &&
+                    availableSpace[1] < opt_scan.getStatistics().sizeBytes) {
+                  // Could retrieve cloud quota and doesn't fit.
+                  this.updateUi_(
+                      importer.ActivityState.INSUFFICIENT_CLOUD_SPACE, opt_scan,
+                      availableSpace[1]);
+                  return;
+                }
+
+                // Enough space available!
+                this.updateUi_(
+                    importer.ActivityState.READY,  // to import...
+                    opt_scan);
+                if (this.isRightAfterPluggingMedia_) {
+                  this.isRightAfterPluggingMedia_ = false;
+                  this.commandWidget_.setDetailsVisible(true);
+                }
+              })
+        .catch(error => {
+          // If an error occurs, it will appear to scan forever - hide the
+          // cloud backup option in that case.
+          importer.getLogger().catcher('import-controller-check-state')(error);
+          this.updateUi_(importer.ActivityState.HIDDEN);
+        });
+  }
+
+  /**
+   * @param {importer.ActivityState} activityState
+   * @param {importer.ScanResult=} opt_scan
+   * @param {number=} opt_destinationSizeBytes specifies the destination size in
+   *     bytes in case of space issues.
+   * @private
+   */
+  updateUi_(activityState, opt_scan, opt_destinationSizeBytes) {
+    this.lastActivityState_ = activityState;
+    this.commandWidget_.update(
+        activityState, opt_scan, opt_destinationSizeBytes);
+  }
+
+  /**
+   * @return {boolean} true if the current directory is scan eligible.
+   * @private
+   */
+  isCurrentDirectoryScannable_() {
+    const directory = this.environment_.getCurrentDirectory();
+    return !!directory &&
+        importer.isMediaDirectory(directory, this.environment_.volumeManager);
+  }
+
+  /**
+   * Attempts to scan the current context.
+   *
+   * @param {importer.ScanMode} mode How to detect new files.
+   * @return {importer.ScanResult} A scan object,
+   *     or null if scan is not possible in current context.
+   * @private
+   */
+  tryScan_(mode) {
+    const entries = this.environment_.getSelection();
+    if (entries.length) {
+      if (entries.every(importer.isEligibleEntry.bind(
+              null, this.environment_.volumeManager))) {
+        return this.scanManager_.getSelectionScan(entries, mode);
+      }
+    } else if (this.isCurrentDirectoryScannable_()) {
+      return this.scanManager_.getDirectoryScan(mode);
+    }
+
+    return null;
+  }
 };
 
 /**
@@ -112,409 +465,52 @@
  *   task: !importer.MediaImportHandler.ImportTask,
  *   started: !Date
  * }}
+ *
  * @private
  */
 importer.TaskDetails_;
 
 /**
- * @param {!importer.ScanEvent} event Command event.
- * @param {importer.ScanResult} scan
- *
- * @private
- */
-importer.ImportController.prototype.onScanEvent_ = function(event, scan) {
-  if (!this.scanManager_.isActiveScan(scan)) {
-    return;
-  }
-
-  switch (event) {
-    case importer.ScanEvent.INVALIDATED:
-      this.onScanInvalidated_();
-    case importer.ScanEvent.FINALIZED:
-    case importer.ScanEvent.UPDATED:
-      this.checkState_(scan);
-      break;
-  }
-};
-
-/** @private */
-importer.ImportController.prototype.onWindowClosing_ = function() {
-  this.scanManager_.reset();  // Will cancel any active scans.
-};
-
-/**
- * @param {string} volumeId
- * @private
- */
-importer.ImportController.prototype.onVolumeUnmounted_ = function(volumeId) {
-  if (this.activeImport_) {
-    this.activeImport_.task.requestCancel();
-    this.finalizeActiveImport_();
-    metrics.recordBoolean('ImportController.DeviceYanked');
-  }
-  this.scanManager_.reset();
-  this.checkState_();
-};
-
-/**
- * @param {!Event} event
- * @private
- */
-importer.ImportController.prototype.onDirectoryChanged_ = function(event) {
-  this.isRightAfterPluggingMedia_ = !event.previousDirEntry;
-
-  this.scanManager_.reset();
-  if (this.isCurrentDirectoryScannable_()) {
-    this.checkState_(
-        this.scanManager_.getDirectoryScan(importer.ScanMode.HISTORY));
-  } else {
-    this.checkState_();
-  }
-};
-
-/** @private */
-importer.ImportController.prototype.onSelectionChanged_ = function() {
-  // NOTE: Empty selection changed events can and will fire for a directory
-  // before we receive the the corresponding directory changed event
-  // and when the selection is empty. These are spurios events
-  // and we ignore them.
-  if (!this.scanManager_.hasSelectionScan() &&
-      this.environment_.getSelection().length === 0) {
-    return;
-  }
-
-  // NOTE: We clear the scan here, but don't immediately initiate
-  // a new scan. checkState will attempt to initialize the scan
-  // during normal processing.
-  // Also, in the case the selection is transitioning to empty,
-  // we want to reinstate the underlying directory scan (if
-  // in fact, one is possible).
-  this.scanManager_.clearSelectionScan();
-  if (this.environment_.getSelection().length === 0 &&
-      this.isCurrentDirectoryScannable_()) {
-    this.checkState_(
-        this.scanManager_.getDirectoryScan(importer.ScanMode.HISTORY));
-  } else {
-    this.checkState_();
-  }
-};
-
-/**
- * @param {!importer.MediaImportHandler.ImportTask} task
- * @private
- */
-importer.ImportController.prototype.onImportFinished_ = function(task) {
-  this.finalizeActiveImport_();
-  this.scanManager_.reset();
-  this.storage_.set(importer.Setting.HAS_COMPLETED_IMPORT, true);
-  this.commandWidget_.setDetailsBannerVisible(false);
-  this.checkState_();
-};
-
-/** @private */
-importer.ImportController.prototype.onScanInvalidated_ = function() {
-  this.scanManager_.reset();
-  if (this.environment_.getSelection().length === 0 &&
-      this.isCurrentDirectoryScannable_()) {
-    this.checkState_(
-        this.scanManager_.getDirectoryScan(importer.ScanMode.HISTORY));
-  } else {
-    this.checkState_();
-  }
-};
-
-/**
- * Does book keeping necessary to finalize the active task.
- * @private
- */
-importer.ImportController.prototype.finalizeActiveImport_ = function() {
-  console.assert(
-      !!this.activeImport_, 'Cannot finish import when none is running.');
-  this.previousImport_ = this.activeImport_;
-  this.activeImport_ = null;
-};
-
-/**
- * Handles button clicks emenating from the panel or toolbar.
- * @param {!importer.ClickSource} source
- */
-importer.ImportController.prototype.onClick_ = function(source) {
-  switch (source) {
-    case importer.ClickSource.MAIN:
-      if (this.lastActivityState_ === importer.ActivityState.READY) {
-        this.commandWidget_.performMainButtonRippleAnimation();
-        this.execute_();
-      } else {
-        this.commandWidget_.toggleDetails();
-      }
-      break;
-    case importer.ClickSource.IMPORT:
-      this.execute_();
-      break;
-    case importer.ClickSource.CANCEL:
-      this.cancel_();
-      break;
-    case importer.ClickSource.DESTINATION:
-      if (this.activeImport_) {
-        this.environment_.showImportDestination(this.activeImport_.started);
-      } else if (this.previousImport_) {
-        this.environment_.showImportDestination(this.previousImport_.started);
-      } else {
-        this.environment_.showImportRoot();
-      }
-      break;
-    case importer.ClickSource.SIDE:
-      // Intentionally unhandled...panel controls toggling of details panel.
-      break;
-    default:
-      assertNotReached('Unhandled click source: ' + source);
-  }
-};
-
-/**
- * Executes import against the current context. Should only be called
- * after the current context has been validated by a scan.
- *
- * @private
- */
-importer.ImportController.prototype.startImportTask_ = function() {
-  console.assert(
-      !this.activeImport_,
-      'Cannot execute while an import task is already active.');
-
-  const scan = this.scanManager_.getActiveScan();
-  assert(scan != null);
-
-  const startDate = new Date();
-  const importTask = this.importRunner_.importFromScanResult(
-      scan, importer.Destination.GOOGLE_DRIVE,
-      this.environment_.getImportDestination(startDate));
-
-  this.activeImport_ = {scan: scan, task: importTask, started: startDate};
-  const taskFinished = this.onImportFinished_.bind(this, importTask);
-  importTask.whenFinished.then(taskFinished).catch(taskFinished);
-};
-
-/**
- * Executes import against the current context. Should only be called
- * when user clicked an import button after the current context has
- * been validated by a scan.
- *
- * @private
- */
-importer.ImportController.prototype.execute_ = function() {
-  this.startImportTask_();
-  this.checkState_();
-};
-
-/**
- * Cancels the active task.
- * @private
- */
-importer.ImportController.prototype.cancel_ = function() {
-  if (this.activeImport_) {
-    this.activeImport_.task.requestCancel();
-    this.finalizeActiveImport_();
-    metrics.recordBoolean('ImportController.ImportCancelled');
-  }
-
-  this.scanManager_.reset();
-  this.checkState_();
-};
-
-/**
- * Checks the environment and updates UI as needed.
- * @param {importer.ScanResult=} opt_scan If supplied,
- * @private
- */
-importer.ImportController.prototype.checkState_ = function(opt_scan) {
-  // If there is no Google Drive mount, Drive may be disabled
-  // or the machine may be running in guest mode.
-  if (!this.environment_.isGoogleDriveMounted()) {
-    this.updateUi_(importer.ActivityState.HIDDEN);
-    return;
-  }
-
-  if (this.activeImport_) {
-    this.updateUi_(importer.ActivityState.IMPORTING, this.activeImport_.scan);
-    return;
-  }
-
-  // If we don't have an existing scan, we'll try to create
-  // one. When we do end up creating one (not getting
-  // one from the cache) it'll be empty...even if there is
-  // a current selection. This is because scans are
-  // resolved asynchronously. And we like it that way.
-  // We'll get notification when the scan is updated. When
-  // that happens, we'll be called back with opt_scan
-  // set to that scan....and subsequently skip over this
-  // block to update the UI.
-  if (!opt_scan) {
-    // NOTE, that tryScan_ lazily initializes scans...so if
-    // no scan is returned, no scan is possible for the
-    // current context.
-    const scan = this.tryScan_(importer.ScanMode.HISTORY);
-    // If no scan is created, then no scan is possible in
-    // the current context...so hide the UI.
-    if (!scan) {
-      this.updateUi_(importer.ActivityState.HIDDEN);
-    }
-    return;
-  }
-
-  // At this point we have an existing scan, and a relatively
-  // validate environment for an import...so we'll proceed
-  // with making updates to the UI.
-  if (!opt_scan.isFinal()) {
-    this.updateUi_(importer.ActivityState.SCANNING, opt_scan);
-    return;
-  }
-
-  if (opt_scan.getFileEntries().length === 0) {
-    if (opt_scan.getDuplicateFileEntries().length === 0) {
-      this.updateUi_(importer.ActivityState.NO_MEDIA);
-      return;
-    }
-    // Some scanned files found already exist in Drive.
-    // It means those files weren't marked as already backed up but need to be.
-    // Trigger sync for that purpose, but no files will actually be copied.
-    this.startImportTask_();
-    this.updateUi_(importer.ActivityState.IMPORTING, this.activeImport_.scan);
-    return;
-  }
-
-  // We have a final scan that is either too big, or juuuussttt right.
-  Promise
-      .all([
-        this.environment_.getFreeStorageSpace(
-            VolumeManagerCommon.VolumeType.DOWNLOADS),
-        this.environment_.getFreeStorageSpace(
-            VolumeManagerCommon.VolumeType.DRIVE),
-      ])
-      .then(/** @param {Array<number>} availableSpace in bytes */
-            availableSpace => {
-              // TODO(smckay): We might want to disqualify some small amount of
-              // local storage in this calculation on the assumption that we
-              // don't want to completely max out storage...even though synced
-              // files will eventually be evicted from the cache.
-              if (availableSpace[0] < opt_scan.getStatistics().sizeBytes) {
-                // Doesn't fit in local space.
-                this.updateUi_(
-                    importer.ActivityState.INSUFFICIENT_LOCAL_SPACE, opt_scan,
-                    availableSpace[0]);
-                return;
-              }
-              if (availableSpace[1] !== -1 &&
-                  availableSpace[1] < opt_scan.getStatistics().sizeBytes) {
-                // Could retrieve cloud quota and doesn't fit.
-                this.updateUi_(
-                    importer.ActivityState.INSUFFICIENT_CLOUD_SPACE, opt_scan,
-                    availableSpace[1]);
-                return;
-              }
-
-              // Enough space available!
-              this.updateUi_(
-                  importer.ActivityState.READY,  // to import...
-                  opt_scan);
-              if (this.isRightAfterPluggingMedia_) {
-                this.isRightAfterPluggingMedia_ = false;
-                this.commandWidget_.setDetailsVisible(true);
-              }
-            })
-      .catch(error => {
-        // If an error occurs, it will appear to scan forever - hide the
-        // cloud backup option in that case.
-        importer.getLogger().catcher('import-controller-check-state')(error);
-        this.updateUi_(importer.ActivityState.HIDDEN);
-      });
-};
-
-/**
- * @param {importer.ActivityState} activityState
- * @param {importer.ScanResult=} opt_scan
- * @param {number=} opt_destinationSizeBytes specifies the destination size in
- *     bytes in case of space issues.
- * @private
- */
-importer.ImportController.prototype.updateUi_ = function(
-    activityState, opt_scan, opt_destinationSizeBytes) {
-  this.lastActivityState_ = activityState;
-  this.commandWidget_.update(activityState, opt_scan, opt_destinationSizeBytes);
-};
-
-/**
- * @return {boolean} true if the current directory is scan eligible.
- * @private
- */
-importer.ImportController.prototype.isCurrentDirectoryScannable_ = function() {
-  const directory = this.environment_.getCurrentDirectory();
-  return !!directory &&
-      importer.isMediaDirectory(directory, this.environment_.volumeManager);
-};
-
-/**
- * Attempts to scan the current context.
- *
- * @param {importer.ScanMode} mode How to detect new files.
- * @return {importer.ScanResult} A scan object,
- *     or null if scan is not possible in current context.
- * @private
- */
-importer.ImportController.prototype.tryScan_ = function(mode) {
-  const entries = this.environment_.getSelection();
-  if (entries.length) {
-    if (entries.every(importer.isEligibleEntry.bind(
-            null, this.environment_.volumeManager))) {
-      return this.scanManager_.getSelectionScan(entries, mode);
-    }
-  } else if (this.isCurrentDirectoryScannable_()) {
-    return this.scanManager_.getDirectoryScan(mode);
-  }
-
-  return null;
-};
-
-/**
  * Class that adapts from the new non-command button to the old
  * command style interface.
  *
  * @interface
  */
-importer.CommandWidget = function() {};
+importer.CommandWidget = class {
+  /**
+   * Installs a listener that gets called when the user wants to initiate
+   * import.
+   *
+   * @param {function(!importer.ClickSource)} listener
+   */
+  addClickListener(listener) {}
 
-/**
- * Install a listener that get's called when the user wants to initiate
- * import.
- *
- * @param {function(!importer.ClickSource)} listener
- */
-importer.CommandWidget.prototype.addClickListener;
+  /**
+   * @param {importer.ActivityState} activityState
+   * @param {importer.ScanResult=} opt_scan
+   * @param {number=} opt_destinationSizeBytes specifies the destination size in
+   *     bytes in case of space issues.
+   */
+  update(activityState, opt_scan, opt_destinationSizeBytes) {}
 
-/**
- * @param {importer.ActivityState} activityState
- * @param {importer.ScanResult=} opt_scan
- * @param {number=} opt_destinationSizeBytes specifies the destination size in
- *     bytes in case of space issues.
- */
-importer.CommandWidget.prototype.update;
+  /**
+   * Directly sets the visibility of the details panel.
+   * @param {boolean} visible
+   */
+  setDetailsVisible(visible) {}
 
-/**
- * Directly sets the visibility of the details panel.
- *
- * @param {boolean} visible
- */
-importer.CommandWidget.prototype.setDetailsVisible;
+  /** Ripples the main button when it's clicked. */
+  performMainButtonRippleAnimation() {}
 
-/**
- * Toggles the visibility state of the details panel.
- */
-importer.CommandWidget.prototype.toggleDetails;
+  /** Toggles the visibility state of the details panel. */
+  toggleDetails() {}
 
-/**
- * Sets the details banner visibility.
- */
-importer.CommandWidget.prototype.setDetailsBannerVisible;
+  /**
+   * Sets the details banner visibility.
+   * @param {boolean} visible
+   */
+  setDetailsBannerVisible(visible) {}
+};
 
 /**
  * @enum {string}
@@ -530,508 +526,494 @@
 /**
  * Runtime implementation of CommandWidget.
  *
- * @constructor
  * @implements {importer.CommandWidget}
- * @struct
  */
-importer.RuntimeCommandWidget = function() {
-  /** @private {HTMLElement} */
-  this.detailsPanel_ = /** @type {HTMLElement} */ (
-      document.querySelector('#cloud-import-details'));
-  this.detailsPanel_.addEventListener(
-      'transitionend', this.onDetailsTransitionEnd_.bind(this), false);
+importer.RuntimeCommandWidget = class {
+  constructor() {
+    /** @private @const {!HTMLElement} */
+    this.detailsPanel_ = queryRequiredElement('#cloud-import-details');
+    this.detailsPanel_.addEventListener(
+        'transitionend', this.onDetailsTransitionEnd_.bind(this), false);
 
-  // Any clicks on document outside of the details panel
-  // result in the panel being hidden.
-  document.onclick = this.onDetailsFocusLost_.bind(this);
+    // Any clicks on document outside of the details panel
+    // result in the panel being hidden.
+    document.onclick = this.onDetailsFocusLost_.bind(this);
 
-  // Stop further propagation of click events.
-  // This allows us to listen for *any other* clicks
-  // to hide the panel.
-  this.detailsPanel_.onclick = event => {
-    event.stopPropagation();
-  };
-
-  /** @private {!Element} */
-  this.comboButton_ = getRequiredElement('cloud-import-combo-button');
-
-  /** @private {!Element} */
-  this.mainButton_ =
-      queryRequiredElement('#cloud-import-button', this.comboButton_);
-  this.mainButton_.onclick =
-      this.onButtonClicked_.bind(this, importer.ClickSource.MAIN);
-
-  /** @private {!PaperRipple}*/
-  this.mainButtonRipple_ =
-      /** @type {!PaperRipple} */ (
-          queryRequiredElement('.ripples > paper-ripple', this.comboButton_));
-
-  /** @private {Element} */
-  this.sideButton_ =
-      queryRequiredElement('#cloud-import-details-button', this.comboButton_);
-  this.sideButton_.onclick =
-      this.onButtonClicked_.bind(this, importer.ClickSource.SIDE);
-
-  /** @private {!FilesToggleRipple} */
-  this.sideButtonRipple_ =
-      /** @type {!FilesToggleRipple} */ (queryRequiredElement(
-          '.ripples > files-toggle-ripple', this.comboButton_));
-
-  /** @private {Element} */
-  this.importButton_ =
-      document.querySelector('#cloud-import-details paper-button.import');
-  this.importButton_.onclick =
-      this.onButtonClicked_.bind(this, importer.ClickSource.IMPORT);
-
-  /** @private {Element} */
-  this.cancelButton_ =
-      document.querySelector('#cloud-import-details paper-button.cancel');
-  this.cancelButton_.onclick =
-      this.onButtonClicked_.bind(this, importer.ClickSource.CANCEL);
-
-  /** @private {Element} */
-  this.statusContent_ =
-      document.querySelector('#cloud-import-details .status .content');
-  this.statusContent_.onclick =
-      this.onButtonClicked_.bind(this, importer.ClickSource.DESTINATION);
-
-  /** @private {Element} */
-  this.toolbarIcon_ = document.querySelector('#cloud-import-button iron-icon');
-  this.statusIcon_ =
-      document.querySelector('#cloud-import-details .status iron-icon');
-
-  /** @private {Element} */
-  this.detailsBanner_ = document.querySelector('#cloud-import-details .banner');
-
-  /** @private {Element} */
-  this.progressContainer_ =
-      document.querySelector('#cloud-import-details .progress');
-  /** @private {Element} */
-  this.progressBar_ =
-      document.querySelector('#cloud-import-details .progress .value');
-
-  /** @private {function(!importer.ClickSource)} */
-  this.clickListener_;
-
-  document.addEventListener('keydown', this.onKeyDown_.bind(this));
-
-  /** @private{number} */
-  this.cloudImportButtonTabIndex_ =
-      document.querySelector('button#cloud-import-button').tabIndex;
-};
-
-/**
- * Handles document scoped key-down events.
- * @param {Event} event Key event.
- * @private
- */
-importer.RuntimeCommandWidget.prototype.onKeyDown_ = function(event) {
-  switch (util.getKeyModifiers(event) + event.key) {
-    case 'Escape':
-      this.setDetailsVisible(false);
-  }
-};
-
-/**
- * Ensures that a transitionend event gets sent out after a transition.  Similar
- * to ensureTransitionEndEvent (see ui/webui/resources/js/util.js) but sends a
- * standard-compliant rather than a webkit event.
- *
- * @param {!HTMLElement} element
- * @param {number} timeout In milliseconds.
- */
-importer.RuntimeCommandWidget.ensureTransitionEndEvent = (element, timeout) => {
-  let fired = false;
-  element.addEventListener('transitionend', function f(e) {
-    element.removeEventListener('transitionend', f);
-    fired = true;
-  });
-  // Use a timeout of 400 ms.
-  window.setTimeout(() => {
-    if (!fired) {
-      cr.dispatchSimpleEvent(element, 'transitionend', true);
-    }
-  }, timeout);
-};
-
-/** @override */
-importer.RuntimeCommandWidget.prototype.addClickListener = function(listener) {
-  this.clickListener_ = listener;
-};
-
-/**
- * @param {!importer.ClickSource} source
- * @param {Event} event Click event.
- * @private
- */
-importer.RuntimeCommandWidget.prototype.onButtonClicked_ = function(
-    source, event) {
-  console.assert(!!this.clickListener_, 'Listener not set.');
-
-  // Clear focus from the toolbar button after it is clicked.
-  if (source === importer.ClickSource.MAIN) {
-    this.mainButton_.blur();
-  }
-
-  switch (source) {
-    case importer.ClickSource.MAIN:
-    case importer.ClickSource.IMPORT:
-    case importer.ClickSource.CANCEL:
-      this.clickListener_(source);
-      break;
-    case importer.ClickSource.DESTINATION:
-      // NOTE: The element identified by class 'destination-link'
-      // comes and goes as the message in the UI changes.
-      // For this reason we add a click listener on the *container*
-      // and when handling clicks, check to see if the source
-      // was an element marked up to look like a link.
-      if (event.target.classList.contains('destination-link')) {
-        this.clickListener_(source);
-      }
-    case importer.ClickSource.SIDE:
-      this.toggleDetails();
-      break;
-    default:
-      assertNotReached('Unhandled click source: ' + source);
-  }
-
-  event.stopPropagation();
-};
-
-importer.RuntimeCommandWidget.prototype.performMainButtonRippleAnimation =
-    function() {
-  this.mainButtonRipple_.simulatedRipple();
-};
-
-/** @override */
-importer.RuntimeCommandWidget.prototype.toggleDetails = function() {
-  this.setDetailsVisible(this.detailsPanel_.className === 'hidden');
-};
-
-/** @override */
-importer.RuntimeCommandWidget.prototype.setDetailsBannerVisible = function(
-    visible) {
-  this.detailsBanner_.hidden = !visible;
-};
-
-/**
- * @param {boolean} visible
- */
-importer.RuntimeCommandWidget.prototype.setDetailsVisible = function(visible) {
-  this.sideButtonRipple_.activated = visible;
-
-  if (visible) {
-    // Align the detail panel horizontally to the dropdown button.
-    if (document.documentElement.getAttribute('dir') === 'rtl') {
-      const anchorLeft = this.comboButton_.getBoundingClientRect().left;
-      if (anchorLeft) {
-        this.detailsPanel_.style.left = anchorLeft + 'px';
-      }
-    } else {
-      const availableWidth = document.body.getBoundingClientRect().width;
-      const anchorRight = this.comboButton_.getBoundingClientRect().right;
-      if (anchorRight) {
-        this.detailsPanel_.style.right = (availableWidth - anchorRight) + 'px';
-      }
-    }
-
-    this.detailsPanel_.hidden = false;
-
-    // The following line is a hacky way to force the container to lay out
-    // contents so that the transition is triggered.
-    // This line MUST appear before clearing the classname.
-    this.detailsPanel_.scrollTop += 0;
-
-    this.detailsPanel_.className = '';
-  } else {
-    this.detailsPanel_.className = 'hidden';
-    // transition duration is 200ms. Let's wait for 400ms.
-    importer.RuntimeCommandWidget.ensureTransitionEndEvent(
-        /** @type {!HTMLElement} */ (this.detailsPanel_), 400);
-  }
-};
-
-/** @private */
-importer.RuntimeCommandWidget.prototype.onDetailsTransitionEnd_ = function() {
-  if (this.detailsPanel_.className === 'hidden') {
-    // if we simply make the panel invisible (via opacity)
-    // it'll still be sitting there grabing mouse events
-    // and so-on. So we *hide* hide it.
-    this.detailsPanel_.hidden = true;
-  }
-};
-
-/** @private */
-importer.RuntimeCommandWidget.prototype.onDetailsFocusLost_ = function() {
-  this.setDetailsVisible(false);
-};
-
-/**
- * Overwrites the tabIndexes of all anchors under the given element.
- *
- * @param {Element} root The root node of all nodes to process.
- * @param {number} newTabIndex The tabindex value to be given to <a> elements.
- * @private
- */
-importer.RuntimeCommandWidget.prototype.updateTabindexOfAnchors_ =
-    (root, newTabIndex) => {
-      const anchors = root.querySelectorAll('a');
-      anchors.forEach(element => {
-        element.tabIndex = newTabIndex;
-      });
+    // Stop further propagation of click events.
+    // This allows us to listen for *any other* clicks
+    // to hide the panel.
+    this.detailsPanel_.onclick = event => {
+      event.stopPropagation();
     };
 
-/** @override */
-importer.RuntimeCommandWidget.prototype.update = function(
-    activityState, opt_scan, opt_destinationSizeBytes) {
-  let photosText;
-  if (opt_scan) {
-    const detectedFilesCount = opt_scan.getFileEntries().length;
-    if (detectedFilesCount) {
-      photosText = detectedFilesCount == 1 ?
-          strf('CLOUD_IMPORT_ONE_FILE') :
-          strf('CLOUD_IMPORT_MULTIPLE_FILES', detectedFilesCount);
-    } else {
-      photosText = '';
+    /** @private @const {!HTMLElement} */
+    this.comboButton_ = getRequiredElement('cloud-import-combo-button');
+
+    /** @private @const {!HTMLElement} */
+    this.mainButton_ =
+        queryRequiredElement('#cloud-import-button', this.comboButton_);
+    this.mainButton_.onclick =
+        this.onButtonClicked_.bind(this, importer.ClickSource.MAIN);
+
+    /** @private @const {!PaperRipple}*/
+    this.mainButtonRipple_ =
+        /** @type {!PaperRipple} */ (
+            queryRequiredElement('.ripples > paper-ripple', this.comboButton_));
+
+    /** @private @const {!HTMLElement} */
+    this.sideButton_ =
+        queryRequiredElement('#cloud-import-details-button', this.comboButton_);
+    this.sideButton_.onclick =
+        this.onButtonClicked_.bind(this, importer.ClickSource.SIDE);
+
+    /** @private @const {!FilesToggleRipple} */
+    this.sideButtonRipple_ =
+        /** @type {!FilesToggleRipple} */ (queryRequiredElement(
+            '.ripples > files-toggle-ripple', this.comboButton_));
+
+    /** @private @const {!HTMLElement} */
+    this.importButton_ =
+        queryRequiredElement('#cloud-import-details paper-button.import');
+    this.importButton_.onclick =
+        this.onButtonClicked_.bind(this, importer.ClickSource.IMPORT);
+
+    /** @private @const {!HTMLElement} */
+    this.cancelButton_ =
+        queryRequiredElement('#cloud-import-details paper-button.cancel');
+    this.cancelButton_.onclick =
+        this.onButtonClicked_.bind(this, importer.ClickSource.CANCEL);
+
+    /** @private @const {!HTMLElement} */
+    this.statusContent_ =
+        queryRequiredElement('#cloud-import-details .status .content');
+    this.statusContent_.onclick =
+        this.onButtonClicked_.bind(this, importer.ClickSource.DESTINATION);
+
+    /** @private @const {!HTMLElement} */
+    this.toolbarIcon_ = queryRequiredElement('#cloud-import-button iron-icon');
+
+    /** @private @const {!HTMLElement} */
+    this.statusIcon_ =
+        queryRequiredElement('#cloud-import-details .status iron-icon');
+
+    /** @private @const {!HTMLElement} */
+    this.detailsBanner_ = queryRequiredElement('#cloud-import-details .banner');
+
+    /** @private @const {!HTMLElement} */
+    this.progressContainer_ =
+        queryRequiredElement('#cloud-import-details .progress');
+
+    /** @private @const {!HTMLElement} */
+    this.progressBar_ =
+        queryRequiredElement('#cloud-import-details .progress .value');
+
+    /** @private {function(!importer.ClickSource)} */
+    this.clickListener_;
+
+    document.addEventListener('keydown', this.onKeyDown_.bind(this));
+
+    /** @private @const{number} */
+    this.cloudImportButtonTabIndex_ =
+        queryRequiredElement('button#cloud-import-button').tabIndex;
+  }
+
+  /**
+   * Handles document scoped key-down events.
+   * @param {Event} event Key event.
+   * @private
+   */
+  onKeyDown_(event) {
+    switch (util.getKeyModifiers(event) + event.key) {
+      case 'Escape':
+        this.setDetailsVisible(false);
     }
   }
-  switch (activityState) {
-    case importer.ActivityState.HIDDEN:
-      this.setDetailsVisible(false);
 
-      this.comboButton_.hidden = true;
+  /**
+   * Ensures that a transitionend event gets sent out after a transition.
+   * Similar to ensureTransitionEndEvent (see ui/webui/resources/js/util.js) but
+   * sends a standard-compliant rather than a webkit event.
+   *
+   * @param {!HTMLElement} element
+   * @param {number} timeout In milliseconds.
+   */
+  static ensureTransitionEndEvent(element, timeout) {
+    let fired = false;
+    element.addEventListener('transitionend', function f(e) {
+      element.removeEventListener('transitionend', f);
+      fired = true;
+    });
+    // Use a timeout of 400 ms.
+    window.setTimeout(() => {
+      if (!fired) {
+        cr.dispatchSimpleEvent(element, 'transitionend', true);
+      }
+    }, timeout);
+  }
 
-      this.toolbarIcon_.setAttribute('icon', 'files:cloud-off');
-      this.statusIcon_.setAttribute('icon', 'files:cloud-off');
+  /** @override */
+  addClickListener(listener) {
+    this.clickListener_ = listener;
+  }
 
-      break;
+  /**
+   * @param {!importer.ClickSource} source
+   * @param {Event} event Click event.
+   * @private
+   */
+  onButtonClicked_(source, event) {
+    console.assert(!!this.clickListener_, 'Listener not set.');
 
-    case importer.ActivityState.IMPORTING:
-      console.assert(!!opt_scan, 'Scan not defined, but is required.');
-      this.setDetailsVisible(false);
+    // Clear focus from the toolbar button after it is clicked.
+    if (source === importer.ClickSource.MAIN) {
+      this.mainButton_.blur();
+    }
 
-      this.mainButton_.setAttribute(
-          'aria-label', strf('CLOUD_IMPORT_TOOLTIP_IMPORTING', photosText));
-      this.statusContent_.innerHTML =
-          strf('CLOUD_IMPORT_STATUS_IMPORTING', photosText);
-
-      this.comboButton_.hidden = false;
-      this.importButton_.hidden = true;
-      this.cancelButton_.hidden = false;
-      this.progressContainer_.hidden = true;
-
-      this.toolbarIcon_.setAttribute('icon', 'files:autorenew');
-      this.statusIcon_.setAttribute('icon', 'files:autorenew');
-
-      break;
-
-    case importer.ActivityState.INSUFFICIENT_CLOUD_SPACE:
-    case importer.ActivityState.INSUFFICIENT_LOCAL_SPACE:
-      console.assert(!!opt_scan, 'Scan not defined, but is required.');
-
-      {
-        let attrLabel;
-        let messageLabel;
-        if (activityState === importer.ActivityState.INSUFFICIENT_CLOUD_SPACE) {
-          attrLabel = 'CLOUD_IMPORT_TOOLTIP_INSUFFICIENT_CLOUD_SPACE';
-          messageLabel = 'CLOUD_IMPORT_STATUS_INSUFFICIENT_CLOUD_SPACE';
-        } else {
-          attrLabel = 'CLOUD_IMPORT_TOOLTIP_INSUFFICIENT_LOCAL_SPACE';
-          messageLabel = 'CLOUD_IMPORT_STATUS_INSUFFICIENT_LOCAL_SPACE';
+    switch (source) {
+      case importer.ClickSource.MAIN:
+      case importer.ClickSource.IMPORT:
+      case importer.ClickSource.CANCEL:
+        this.clickListener_(source);
+        break;
+      case importer.ClickSource.DESTINATION:
+        // NOTE: The element identified by class 'destination-link'
+        // comes and goes as the message in the UI changes.
+        // For this reason we add a click listener on the *container*
+        // and when handling clicks, check to see if the source
+        // was an element marked up to look like a link.
+        if (event.target.classList.contains('destination-link')) {
+          this.clickListener_(source);
         }
-        this.mainButton_.setAttribute('aria-label', strf(attrLabel));
-        this.statusContent_.innerHTML = strf(
-            messageLabel, photosText,
-            util.bytesToString(
-                opt_scan.getStatistics().sizeBytes - opt_destinationSizeBytes));
+      case importer.ClickSource.SIDE:
+        this.toggleDetails();
+        break;
+      default:
+        assertNotReached('Unhandled click source: ' + source);
+    }
+
+    event.stopPropagation();
+  }
+
+  /** @override */
+  performMainButtonRippleAnimation() {
+    this.mainButtonRipple_.simulatedRipple();
+  }
+
+  /** @override */
+  toggleDetails() {
+    this.setDetailsVisible(this.detailsPanel_.className === 'hidden');
+  }
+
+  /** @override */
+  setDetailsBannerVisible(visible) {
+    this.detailsBanner_.hidden = !visible;
+  }
+
+  /** @param {boolean} visible */
+  setDetailsVisible(visible) {
+    this.sideButtonRipple_.activated = visible;
+
+    if (visible) {
+      // Align the detail panel horizontally to the dropdown button.
+      if (document.documentElement.getAttribute('dir') === 'rtl') {
+        const anchorLeft = this.comboButton_.getBoundingClientRect().left;
+        if (anchorLeft) {
+          this.detailsPanel_.style.left = anchorLeft + 'px';
+        }
+      } else {
+        const availableWidth = document.body.getBoundingClientRect().width;
+        const anchorRight = this.comboButton_.getBoundingClientRect().right;
+        if (anchorRight) {
+          this.detailsPanel_.style.right =
+              (availableWidth - anchorRight) + 'px';
+        }
       }
 
-      this.comboButton_.hidden = false;
-      this.importButton_.hidden = true;
-      this.cancelButton_.hidden = true;
-      this.progressContainer_.hidden = true;
+      this.detailsPanel_.hidden = false;
 
-      this.toolbarIcon_.setAttribute('icon', 'files:cloud-off');
-      this.statusIcon_.setAttribute('icon', 'files:photo');
-      break;
+      // The following line is a hacky way to force the container to lay out
+      // contents so that the transition is triggered.
+      // This line MUST appear before clearing the classname.
+      this.detailsPanel_.scrollTop += 0;
 
-    case importer.ActivityState.NO_MEDIA:
-      this.mainButton_.setAttribute(
-          'aria-label', str('CLOUD_IMPORT_TOOLTIP_NO_MEDIA'));
-      this.statusContent_.innerHTML = str('CLOUD_IMPORT_STATUS_NO_MEDIA');
-
-      this.comboButton_.hidden = false;
-      this.importButton_.hidden = true;
-      this.cancelButton_.hidden = true;
-      this.progressContainer_.hidden = true;
-
-      this.toolbarIcon_.setAttribute('icon', 'files:cloud-done');
-      this.statusIcon_.setAttribute('icon', 'files:cloud-done');
-      break;
-
-    case importer.ActivityState.READY:
-      console.assert(!!opt_scan, 'Scan not defined, but is required.');
-
-      this.mainButton_.setAttribute(
-          'aria-label', strf('CLOUD_IMPORT_TOOLTIP_READY', photosText));
-      this.statusContent_.innerHTML =
-          strf('CLOUD_IMPORT_STATUS_READY', photosText);
-
-      this.comboButton_.hidden = false;
-      this.importButton_.hidden = false;
-      this.cancelButton_.hidden = true;
-      this.progressContainer_.hidden = true;
-
-      this.toolbarIcon_.setAttribute('icon', 'files:cloud-upload');
-      this.statusIcon_.setAttribute('icon', 'files:photo');
-      break;
-
-    case importer.ActivityState.SCANNING:
-      console.assert(!!opt_scan, 'Scan not defined, but is required.');
-
-      this.mainButton_.setAttribute(
-          'aria-label', str('CLOUD_IMPORT_TOOLTIP_SCANNING'));
-      this.statusContent_.innerHTML =
-          strf('CLOUD_IMPORT_STATUS_SCANNING', photosText);
-
-      this.comboButton_.hidden = false;
-      this.importButton_.hidden = true;
-      // TODO(smckay): implement cancellation for scanning.
-      this.cancelButton_.hidden = true;
-      this.progressContainer_.hidden = false;
-
-      const stats = opt_scan.getStatistics();
-      this.progressBar_.style.width = stats.progress + '%';
-
-      this.toolbarIcon_.setAttribute('icon', 'files:autorenew');
-      this.statusIcon_.setAttribute('icon', 'files:autorenew');
-      break;
-
-    default:
-      assertNotReached('Unrecognized response id: ' + activityState);
-  }
-  // Make all anchors synthesized from the localized text focusable.
-  if (this.cloudImportButtonTabIndex_) {
-    this.updateTabindexOfAnchors_(
-        this.statusContent_, this.cloudImportButtonTabIndex_);
-  }
-};
-
-/**
- * A cache for ScanResults.
- *
- * @constructor
- * @struct
- *
- * @param {!importer.ControllerEnvironment} environment
- * @param {!importer.MediaScanner} scanner
- */
-importer.ScanManager = function(environment, scanner) {
-  /** @private {!importer.ControllerEnvironment} */
-  this.environment_ = environment;
-
-  /** @private {!importer.MediaScanner} */
-  this.scanner_ = scanner;
-
-  /**
-   * The active files scan, if any.
-   *
-   * @private {importer.ScanResult}
-   */
-  this.selectionScan_ = null;
-
-  /**
-   * The active directory scan, if any.
-   *
-   * @private {importer.ScanResult}
-   */
-  this.directoryScan_ = null;
-};
-
-/**
- * Cancels and forgets all scans.
- */
-importer.ScanManager.prototype.reset = function() {
-  this.clearSelectionScan();
-  this.clearDirectoryScan();
-};
-
-/**
- * @return {boolean} True if we have an existing selection scan.
- */
-importer.ScanManager.prototype.hasSelectionScan = function() {
-  return !!this.selectionScan_;
-};
-
-/**
- * Cancels and forgets the current selection scan, if any.
- */
-importer.ScanManager.prototype.clearSelectionScan = function() {
-  if (this.selectionScan_) {
-    this.selectionScan_.cancel();
-  }
-  this.selectionScan_ = null;
-};
-
-/**
- * Cancels and forgets the current directory scan, if any.
- */
-importer.ScanManager.prototype.clearDirectoryScan = function() {
-  if (this.directoryScan_) {
-    this.directoryScan_.cancel();
-  }
-  this.directoryScan_ = null;
-};
-
-/**
- * @return {importer.ScanResult} Current active scan, or null
- * if none.
- */
-importer.ScanManager.prototype.getActiveScan = function() {
-  return this.selectionScan_ || this.directoryScan_;
-};
-
-/**
- * @param {importer.ScanResult} scan
- * @return {boolean} True if scan is the active scan...meaning the current
- *     selection scan or the scan for the current directory.
- */
-importer.ScanManager.prototype.isActiveScan = function(scan) {
-  return scan === this.selectionScan_ || scan === this.directoryScan_;
-};
-
-/**
- * Returns the existing selection scan or a new one for the supplied
- * selection.
- *
- * @param {!Array<!FileEntry>} entries
- * @param {!importer.ScanMode} mode
- *
- * @return {!importer.ScanResult}
- */
-importer.ScanManager.prototype.getSelectionScan = function(entries, mode) {
-  console.assert(
-      !this.selectionScan_,
-      'Cannot create new selection scan with another in the cache.');
-  this.selectionScan_ = this.scanner_.scanFiles(entries, mode);
-  return this.selectionScan_;
-};
-
-/**
- * Returns a scan for the directory.
- *
- * @param {!importer.ScanMode} mode
- * @return {importer.ScanResult}
- */
-importer.ScanManager.prototype.getDirectoryScan = function(mode) {
-  if (!this.directoryScan_) {
-    const directory = this.environment_.getCurrentDirectory();
-    if (directory) {
-      this.directoryScan_ = this.scanner_.scanDirectory(
-          /** @type {!DirectoryEntry} */ (directory), mode);
+      this.detailsPanel_.className = '';
+    } else {
+      this.detailsPanel_.className = 'hidden';
+      // transition duration is 200ms. Let's wait for 400ms.
+      importer.RuntimeCommandWidget.ensureTransitionEndEvent(
+          /** @type {!HTMLElement} */ (this.detailsPanel_), 400);
     }
   }
-  return this.directoryScan_;
+
+  /** @private */
+  onDetailsTransitionEnd_() {
+    if (this.detailsPanel_.className === 'hidden') {
+      // if we simply make the panel invisible (via opacity)
+      // it'll still be sitting there grabbing mouse events
+      // and so-on. So we *hide* it.
+      this.detailsPanel_.hidden = true;
+    }
+  }
+
+  /** @private */
+  onDetailsFocusLost_() {
+    this.setDetailsVisible(false);
+  }
+
+  /**
+   * Overwrites the tabIndexes of all anchors under the given element.
+   *
+   * @param {Element} root The root node of all nodes to process.
+   * @param {number} newTabIndex The tabindex value to be given to <a> elements.
+   * @private
+   */
+  static updateTabIndexOfAnchors_(root, newTabIndex) {
+    const anchors = root.querySelectorAll('a');
+    anchors.forEach(element => {
+      element.tabIndex = newTabIndex;
+    });
+  }
+
+  /** @override */
+  update(activityState, opt_scan, opt_destinationSizeBytes) {
+    let photosText;
+    if (opt_scan) {
+      const detectedFilesCount = opt_scan.getFileEntries().length;
+      if (detectedFilesCount) {
+        photosText = detectedFilesCount == 1 ?
+            strf('CLOUD_IMPORT_ONE_FILE') :
+            strf('CLOUD_IMPORT_MULTIPLE_FILES', detectedFilesCount);
+      } else {
+        photosText = '';
+      }
+    }
+
+    switch (activityState) {
+      case importer.ActivityState.HIDDEN:
+        this.setDetailsVisible(false);
+
+        this.comboButton_.hidden = true;
+
+        this.toolbarIcon_.setAttribute('icon', 'files:cloud-off');
+        this.statusIcon_.setAttribute('icon', 'files:cloud-off');
+
+        break;
+
+      case importer.ActivityState.IMPORTING:
+        console.assert(!!opt_scan, 'Scan not defined, but is required.');
+        this.setDetailsVisible(false);
+
+        this.mainButton_.setAttribute(
+            'aria-label', strf('CLOUD_IMPORT_TOOLTIP_IMPORTING', photosText));
+        this.statusContent_.innerHTML =
+            strf('CLOUD_IMPORT_STATUS_IMPORTING', photosText);
+
+        this.comboButton_.hidden = false;
+        this.importButton_.hidden = true;
+        this.cancelButton_.hidden = false;
+        this.progressContainer_.hidden = true;
+
+        this.toolbarIcon_.setAttribute('icon', 'files:autorenew');
+        this.statusIcon_.setAttribute('icon', 'files:autorenew');
+
+        break;
+
+      case importer.ActivityState.INSUFFICIENT_CLOUD_SPACE:
+      case importer.ActivityState.INSUFFICIENT_LOCAL_SPACE:
+        console.assert(!!opt_scan, 'Scan not defined, but is required.');
+
+        {
+          let attrLabel;
+          let messageLabel;
+          if (activityState ===
+              importer.ActivityState.INSUFFICIENT_CLOUD_SPACE) {
+            attrLabel = 'CLOUD_IMPORT_TOOLTIP_INSUFFICIENT_CLOUD_SPACE';
+            messageLabel = 'CLOUD_IMPORT_STATUS_INSUFFICIENT_CLOUD_SPACE';
+          } else {
+            attrLabel = 'CLOUD_IMPORT_TOOLTIP_INSUFFICIENT_LOCAL_SPACE';
+            messageLabel = 'CLOUD_IMPORT_STATUS_INSUFFICIENT_LOCAL_SPACE';
+          }
+          this.mainButton_.setAttribute('aria-label', strf(attrLabel));
+          this.statusContent_.innerHTML = strf(
+              messageLabel, photosText,
+              util.bytesToString(
+                  opt_scan.getStatistics().sizeBytes -
+                  opt_destinationSizeBytes));
+        }
+
+        this.comboButton_.hidden = false;
+        this.importButton_.hidden = true;
+        this.cancelButton_.hidden = true;
+        this.progressContainer_.hidden = true;
+
+        this.toolbarIcon_.setAttribute('icon', 'files:cloud-off');
+        this.statusIcon_.setAttribute('icon', 'files:photo');
+        break;
+
+      case importer.ActivityState.NO_MEDIA:
+        this.mainButton_.setAttribute(
+            'aria-label', str('CLOUD_IMPORT_TOOLTIP_NO_MEDIA'));
+        this.statusContent_.innerHTML = str('CLOUD_IMPORT_STATUS_NO_MEDIA');
+
+        this.comboButton_.hidden = false;
+        this.importButton_.hidden = true;
+        this.cancelButton_.hidden = true;
+        this.progressContainer_.hidden = true;
+
+        this.toolbarIcon_.setAttribute('icon', 'files:cloud-done');
+        this.statusIcon_.setAttribute('icon', 'files:cloud-done');
+        break;
+
+      case importer.ActivityState.READY:
+        console.assert(!!opt_scan, 'Scan not defined, but is required.');
+
+        this.mainButton_.setAttribute(
+            'aria-label', strf('CLOUD_IMPORT_TOOLTIP_READY', photosText));
+        this.statusContent_.innerHTML =
+            strf('CLOUD_IMPORT_STATUS_READY', photosText);
+
+        this.comboButton_.hidden = false;
+        this.importButton_.hidden = false;
+        this.cancelButton_.hidden = true;
+        this.progressContainer_.hidden = true;
+
+        this.toolbarIcon_.setAttribute('icon', 'files:cloud-upload');
+        this.statusIcon_.setAttribute('icon', 'files:photo');
+        break;
+
+      case importer.ActivityState.SCANNING:
+        console.assert(!!opt_scan, 'Scan not defined, but is required.');
+
+        this.mainButton_.setAttribute(
+            'aria-label', str('CLOUD_IMPORT_TOOLTIP_SCANNING'));
+        this.statusContent_.innerHTML =
+            strf('CLOUD_IMPORT_STATUS_SCANNING', photosText);
+
+        this.comboButton_.hidden = false;
+        this.importButton_.hidden = true;
+        // TODO(smckay): implement cancellation for scanning.
+        this.cancelButton_.hidden = true;
+        this.progressContainer_.hidden = false;
+
+        const stats = opt_scan.getStatistics();
+        this.progressBar_.style.width = stats.progress + '%';
+
+        this.toolbarIcon_.setAttribute('icon', 'files:autorenew');
+        this.statusIcon_.setAttribute('icon', 'files:autorenew');
+        break;
+
+      default:
+        assertNotReached('Unrecognized response id: ' + activityState);
+    }
+
+    // Make all anchors synthesized from the localized text focusable.
+    if (this.cloudImportButtonTabIndex_) {
+      importer.RuntimeCommandWidget.updateTabIndexOfAnchors_(
+          this.statusContent_, this.cloudImportButtonTabIndex_);
+    }
+  }
+};
+
+/** A cache for ScanResults. */
+importer.ScanManager = class {
+  /**
+   * @param {!importer.ControllerEnvironment} environment
+   * @param {!importer.MediaScanner} scanner
+   */
+  constructor(environment, scanner) {
+    /** @private {!importer.ControllerEnvironment} */
+    this.environment_ = environment;
+
+    /** @private @const {!importer.MediaScanner} */
+    this.scanner_ = scanner;
+
+    /**
+     * The active files scan, if any.
+     * @private {?importer.ScanResult}
+     */
+    this.selectionScan_ = null;
+
+    /**
+     * The active directory scan, if any.
+     * @private {?importer.ScanResult}
+     */
+    this.directoryScan_ = null;
+  }
+
+  /** Cancels and forgets all scans. */
+  reset() {
+    this.clearSelectionScan();
+    this.clearDirectoryScan();
+  }
+
+  /** @return {boolean} True if we have an existing selection scan. */
+  hasSelectionScan() {
+    return !!this.selectionScan_;
+  }
+
+  /** Cancels and forgets the current selection scan, if any. */
+  clearSelectionScan() {
+    if (this.selectionScan_) {
+      this.selectionScan_.cancel();
+    }
+    this.selectionScan_ = null;
+  }
+
+  /** Cancels and forgets the current directory scan, if any. */
+  clearDirectoryScan() {
+    if (this.directoryScan_) {
+      this.directoryScan_.cancel();
+    }
+    this.directoryScan_ = null;
+  }
+
+  /** @return {importer.ScanResult} Current active scan, or null if none. */
+  getActiveScan() {
+    return this.selectionScan_ || this.directoryScan_;
+  }
+
+  /**
+   * @param {importer.ScanResult} scan
+   * @return {boolean} True if scan is the active scan...meaning the current
+   *     selection scan or the scan for the current directory.
+   */
+  isActiveScan(scan) {
+    return scan === this.selectionScan_ || scan === this.directoryScan_;
+  }
+
+  /**
+   * Returns the existing selection scan or a new one for the supplied
+   * selection.
+   *
+   * @param {!Array<!FileEntry>} entries
+   * @param {!importer.ScanMode} mode
+   *
+   * @return {!importer.ScanResult}
+   */
+  getSelectionScan(entries, mode) {
+    console.assert(
+        !this.selectionScan_,
+        'Cannot create new selection scan with another in the cache.');
+    this.selectionScan_ = this.scanner_.scanFiles(entries, mode);
+    return this.selectionScan_;
+  }
+
+  /**
+   * Returns a scan for the directory.
+   *
+   * @param {!importer.ScanMode} mode
+   * @return {importer.ScanResult}
+   */
+  getDirectoryScan(mode) {
+    if (!this.directoryScan_) {
+      const directory = this.environment_.getCurrentDirectory();
+      if (directory) {
+        this.directoryScan_ = this.scanner_.scanDirectory(
+            /** @type {!DirectoryEntry} */ (directory), mode);
+      }
+    }
+    return this.directoryScan_;
+  }
 };
 
 /**
@@ -1041,288 +1023,279 @@
  *
  * @interface
  */
-importer.ControllerEnvironment = function() {};
+importer.ControllerEnvironment = class {
+  constructor() {
+    /** @type {!VolumeManager} */
+    this.volumeManager;
+  }
 
-/** @type {!VolumeManager} */
-importer.ControllerEnvironment.prototype.volumeManager;
+  /**
+   * Returns the current file selection, if any. May be empty.
+   * @return {!Array<!Entry>}
+   */
+  getSelection() {}
 
-/**
- * Returns the current file selection, if any. May be empty.
- * @return {!Array<!Entry>}
- */
-importer.ControllerEnvironment.prototype.getSelection;
+  /**
+   * Returns the directory entry for the current directory.
+   * @return {DirectoryEntry|FilesAppEntry}
+   */
+  getCurrentDirectory() {}
 
-/**
- * Returns the directory entry for the current directory.
- * @return {DirectoryEntry|FilesAppEntry}
- */
-importer.ControllerEnvironment.prototype.getCurrentDirectory;
+  /**
+   * @param {!DirectoryEntry} entry
+   */
+  setCurrentDirectory(entry) {}
 
-/**
- * @param {!DirectoryEntry} entry
- */
-importer.ControllerEnvironment.prototype.setCurrentDirectory;
+  /**
+   * Obtains a volume info containing the passed entry.
+   * @param {!Entry|!FilesAppEntry} entry Entry on the volume to be
+   *     returned. Can be fake.
+   * @return {VolumeInfo} The VolumeInfo instance or null if not found.
+   */
+  getVolumeInfo(entry) {}
 
-/**
- * Obtains a volume info containing the passed entry.
- * @param {!Entry|!FilesAppEntry} entry Entry on the volume to be
- *     returned. Can be fake.
- * @return {VolumeInfo} The VolumeInfo instance or null if not found.
- */
-importer.ControllerEnvironment.prototype.getVolumeInfo;
+  /**
+   * Returns true if the Drive mount is present.
+   * @return {boolean}
+   */
+  isGoogleDriveMounted() {}
 
-/**
- * Returns true if the Drive mount is present.
- * @return {boolean}
- */
-importer.ControllerEnvironment.prototype.isGoogleDriveMounted;
+  /**
+   * Returns the available space for the given volume in bytes, or -1
+   * if the the available space is not checkable.
+   * Rejects if the volume is not mounted.
+   * @param {!VolumeManagerCommon.VolumeType} volumeType
+   * @return {!Promise<number>}
+   */
+  getFreeStorageSpace(volumeType) {}
 
-/**
- * Returns the available space for the given volume in bytes, or -1
- * if the the available space is not checkable.
- * Rejects if the volume is not mounted.
- * @param {!VolumeManagerCommon.VolumeType} volumeType
- * @return {!Promise<number>}
- */
-importer.ControllerEnvironment.prototype.getFreeStorageSpace;
+  /**
+   * Installs a 'window closed' listener. Listener is called just
+   * just before the window is closed. Any business must be
+   * done synchronously.
+   * @param {function()} listener
+   */
+  addWindowCloseListener(listener) {}
 
-/**
- * Installs a 'window closed' listener. Listener is called just
- * just before the window is closed. Any business must be
- * done synchronously.
- * @param {function()} listener
- */
-importer.ControllerEnvironment.prototype.addWindowCloseListener;
+  /**
+   * Installs an 'unmount' listener. Listener is called with
+   * the corresponding volume id when a volume is unmounted.
+   * @param {function(string)} listener
+   */
+  addVolumeUnmountListener(listener) {}
 
-/**
- * Installs an 'unmount' listener. Listener is called with
- * the corresponding volume id when a volume is unmounted.
- * @param {function(string)} listener
- */
-importer.ControllerEnvironment.prototype.addVolumeUnmountListener;
+  /**
+   * Installs an 'directory-changed' listener. Listener is called when
+   * the directory changed.
+   * @param {function(!Event)} listener
+   */
+  addDirectoryChangedListener(listener) {}
 
-/**
- * Installs an 'directory-changed' listener. Listener is called when
- * the directory changed.
- * @param {function(!Event)} listener
- */
-importer.ControllerEnvironment.prototype.addDirectoryChangedListener;
+  /**
+   * Installs an 'selection-changed' listener. Listener is called when
+   * user selected files is changed.
+   * @param {function()} listener
+   */
+  addSelectionChangedListener(listener) {}
 
-/**
- * Installs an 'selection-changed' listener. Listener is called when
- * user selected files is changed.
- * @param {function()} listener
- */
-importer.ControllerEnvironment.prototype.addSelectionChangedListener;
+  /**
+   * Reveals the import root directory (the parent of all import destinations)
+   * in a new Files app window.
+   * E.g. "Chrome OS Cloud backup". Creates it if it doesn't exist.
+   *
+   * @return {!Promise} Resolves when the folder has been shown.
+   */
+  showImportRoot() {}
 
-/**
- * Reveals the import root directory (the parent of all import destinations)
- * in a new Files app window.
- * E.g. "Chrome OS Cloud backup". Creates it if it doesn't exist.
- *
- * @return {!Promise} Resolves when the folder has been shown.
- */
-importer.ControllerEnvironment.prototype.showImportRoot;
+  /**
+   * Returns the date-stamped import destination directory in a new
+   * Files app window. E.g. "2015-12-04".
+   * Creates it if it doesn't exist.
+   *
+   * @param {!Date} date The import date
+   * @return {!Promise<!DirectoryEntry>} Resolves when the folder is available.
+   */
+  getImportDestination(date) {}
 
-/**
- * Returns the date-stamped import destination directory in a new
- * Files app window. E.g. "2015-12-04".
- * Creates it if it doesn't exist.
- *
- * @param {!Date} date The import date
- *
- * @return {!Promise<!DirectoryEntry>} Resolves when the folder is available.
- */
-importer.ControllerEnvironment.prototype.getImportDestination;
-
-/**
- * Reveals the date-stamped import destination directory in a new
- * Files app window. E.g. "2015-12-04".
- * Creates it if it doesn't exist.
- *
- * @param {!Date} date The import date
- *
- * @return {!Promise} Resolves when the folder has been shown.
- */
-importer.ControllerEnvironment.prototype.showImportDestination;
+  /**
+   * Reveals the date-stamped import destination directory in a new
+   * Files app window. E.g. "2015-12-04".
+   * Creates it if it doesn't exist.
+   *
+   * @param {!Date} date The import date
+   * @return {!Promise} Resolves when the folder has been shown.
+   */
+  showImportDestination(date) {}
+};
 
 /**
  * Class providing access to various pieces of information in the
  * FileManager environment, like the current directory, volumeinfo lookup
  * By hiding file manager we make it easy to test importer.ImportController.
  *
- * @constructor
  * @implements {importer.ControllerEnvironment}
- *
- * @param {!CommandHandlerDeps} fileManager
- * @param {!FileSelectionHandler} selectionHandler
  */
-importer.RuntimeControllerEnvironment = function(
-    fileManager, selectionHandler) {
-  this.volumeManager = fileManager.volumeManager;
+importer.RuntimeControllerEnvironment = class {
+  /**
+   * @param {!CommandHandlerDeps} fileManager
+   * @param {!FileSelectionHandler} selectionHandler
+   */
+  constructor(fileManager, selectionHandler) {
+    this.volumeManager = fileManager.volumeManager;
 
-  /** @private {!CommandHandlerDeps} */
-  this.fileManager_ = fileManager;
+    /** @private {!CommandHandlerDeps} */
+    this.fileManager_ = fileManager;
 
-  /** @private {!FileSelectionHandler} */
-  this.selectionHandler_ = selectionHandler;
-};
+    /** @private {!FileSelectionHandler} */
+    this.selectionHandler_ = selectionHandler;
+  }
 
-/** @override */
-importer.RuntimeControllerEnvironment.prototype.getSelection = function() {
-  return this.fileManager_.getSelection().entries;
-};
+  /** @override */
+  getSelection() {
+    return this.fileManager_.getSelection().entries;
+  }
 
-/** @override */
-importer.RuntimeControllerEnvironment.prototype.getCurrentDirectory =
-    function() {
-  return this.fileManager_.getCurrentDirectoryEntry();
-};
+  /** @override */
+  getCurrentDirectory() {
+    return this.fileManager_.getCurrentDirectoryEntry();
+  }
 
-/** @override */
-importer.RuntimeControllerEnvironment.prototype.setCurrentDirectory = function(
-    entry) {
-  this.fileManager_.directoryModel.activateDirectoryEntry(entry);
-};
+  /** @override */
+  setCurrentDirectory(entry) {
+    this.fileManager_.directoryModel.activateDirectoryEntry(entry);
+  }
 
-/** @override */
-importer.RuntimeControllerEnvironment.prototype.getVolumeInfo = function(
-    entry) {
-  return this.fileManager_.volumeManager.getVolumeInfo(entry);
-};
+  /** @override */
+  getVolumeInfo(entry) {
+    return this.fileManager_.volumeManager.getVolumeInfo(entry);
+  }
 
-/** @override */
-importer.RuntimeControllerEnvironment.prototype.isGoogleDriveMounted =
-    function() {
-  const drive = this.fileManager_.volumeManager.getCurrentProfileVolumeInfo(
-      VolumeManagerCommon.VolumeType.DRIVE);
-  return !!drive;
-};
+  /** @override */
+  isGoogleDriveMounted() {
+    const drive = this.fileManager_.volumeManager.getCurrentProfileVolumeInfo(
+        VolumeManagerCommon.VolumeType.DRIVE);
+    return !!drive;
+  }
 
-/** @override */
-importer.RuntimeControllerEnvironment.prototype.getFreeStorageSpace = function(
-    volumeType) {
-  // VolumeInfo will exist, because if it doesn't, the scan would never have
-  // been initiated (see importer.ImportController.prototype.checkState_)
-  const volumeInfo = assert(
-      this.fileManager_.volumeManager.getCurrentProfileVolumeInfo(volumeType));
-  return new Promise((resolve, reject) => {
-    chrome.fileManagerPrivate.getSizeStats(volumeInfo.volumeId, stats => {
-      if (chrome.runtime.lastError) {
-        reject(
-            'Failed to ascertain available free space: ' +
-            chrome.runtime.lastError.message);
-        return;
-      }
-      if (!stats) {
-        resolve(-1);
-      }
-      resolve(stats.remainingSize);
+  /** @override */
+  getFreeStorageSpace(volumeType) {
+    // VolumeInfo will exist, because if it doesn't, the scan would never have
+    // been initiated (see importer.ImportController.prototype.checkState_)
+    const volumeInfo =
+        assert(this.fileManager_.volumeManager.getCurrentProfileVolumeInfo(
+            volumeType));
+    return new Promise((resolve, reject) => {
+      chrome.fileManagerPrivate.getSizeStats(volumeInfo.volumeId, stats => {
+        if (chrome.runtime.lastError) {
+          reject(
+              'Failed to ascertain available free space: ' +
+              chrome.runtime.lastError.message);
+          return;
+        }
+        if (!stats) {
+          resolve(-1);
+        }
+        resolve(stats.remainingSize);
+      });
     });
-  });
-};
+  }
 
-/** @override */
-importer.RuntimeControllerEnvironment.prototype.addWindowCloseListener =
-    listener => {
-      window.addEventListener('pagehide', listener);
-    };
+  /** @override */
+  addWindowCloseListener(listener) {
+    window.addEventListener('pagehide', listener);
+  }
 
-/** @override */
-importer.RuntimeControllerEnvironment.prototype.addVolumeUnmountListener =
-    listener => {
-      // TODO(smckay): remove listeners when the page is torn down.
-      chrome.fileManagerPrivate.onMountCompleted.addListener(
-          /**
-           * @param {!chrome.fileManagerPrivate.MountCompletedEvent} event
-           * @this {importer.RuntimeControllerEnvironment}
-           */
-          event => {
-            if (event.eventType === 'unmount') {
-              listener(event.volumeMetadata.volumeId);
-            }
-          });
-    };
+  /** @override */
+  addVolumeUnmountListener(listener) {
+    // TODO(smckay): remove listeners when the page is torn down.
+    chrome.fileManagerPrivate.onMountCompleted.addListener(
+        /**
+         * @param {!chrome.fileManagerPrivate.MountCompletedEvent} event
+         * @this {importer.RuntimeControllerEnvironment}
+         */
+        event => {
+          if (event.eventType === 'unmount') {
+            listener(event.volumeMetadata.volumeId);
+          }
+        });
+  }
 
-/** @override */
-importer.RuntimeControllerEnvironment.prototype.addDirectoryChangedListener =
-    function(listener) {
-  // TODO(smckay): remove listeners when the page is torn down.
-  this.fileManager_.directoryModel.addEventListener(
-      'directory-changed', listener);
-};
+  /** @override */
+  addDirectoryChangedListener(listener) {
+    // TODO(smckay): remove listeners when the page is torn down.
+    this.fileManager_.directoryModel.addEventListener(
+        'directory-changed', listener);
+  }
 
-/** @override */
-importer.RuntimeControllerEnvironment.prototype.addSelectionChangedListener =
-    function(listener) {
-  this.selectionHandler_.addEventListener(
-      FileSelectionHandler.EventType.CHANGE_THROTTLED, listener);
-};
+  /** @override */
+  addSelectionChangedListener(listener) {
+    this.selectionHandler_.addEventListener(
+        FileSelectionHandler.EventType.CHANGE_THROTTLED, listener);
+  }
 
-/**
- * Reveals the directory to the user in the Files app, either creating
- * a new window, or focusing if already open in a window.
- *
- * @param {!DirectoryEntry} directory
- * @private
- */
-importer.RuntimeControllerEnvironment.prototype.revealDirectory_ = function(
-    directory) {
-  this.fileManager_.backgroundPage.launcher.launchFileManager(
-      {currentDirectoryURL: directory.toURL()},
-      /* App ID */ undefined);
-};
+  /**
+   * Reveals the directory to the user in the Files app, either creating
+   * a new window, or focusing if already open in a window.
+   * @param {!DirectoryEntry} directory
+   * @private
+   */
+  revealDirectory_(directory) {
+    this.fileManager_.backgroundPage.launcher.launchFileManager(
+        {currentDirectoryURL: directory.toURL()},
+        /* App ID */ undefined);
+  }
 
-/**
- * Retrieves the user's drive root.
- * @return {!Promise<!DirectoryEntry>}
- * @private
- */
-importer.RuntimeControllerEnvironment.prototype.getDriveRoot_ = function() {
-  const drive = this.fileManager_.volumeManager.getCurrentProfileVolumeInfo(
-      VolumeManagerCommon.VolumeType.DRIVE);
-  return /** @type {!Promise<!DirectoryEntry>} */ (drive.resolveDisplayRoot());
-};
+  /**
+   * Retrieves the user's drive root.
+   * @return {!Promise<!DirectoryEntry>}
+   * @private
+   */
+  getDriveRoot_() {
+    const drive = this.fileManager_.volumeManager.getCurrentProfileVolumeInfo(
+        VolumeManagerCommon.VolumeType.DRIVE);
+    return /** @type {!Promise<!DirectoryEntry>} */ (
+        drive.resolveDisplayRoot());
+  }
 
-/**
- * Fetches (creating if necessary) the import destination subdirectory.
- * @return {!Promise<!DirectoryEntry>}
- * @private
- */
-importer.RuntimeControllerEnvironment.prototype.demandCloudFolder_ = root => {
-  return importer.demandChildDirectory(
-      root, str('CLOUD_IMPORT_DESTINATION_FOLDER'));
-};
+  /**
+   * Fetches (creating if necessary) the import destination subdirectory.
+   * @return {!Promise<!DirectoryEntry>}
+   * @private
+   */
+  demandCloudFolder_(root) {
+    return importer.demandChildDirectory(
+        root, str('CLOUD_IMPORT_DESTINATION_FOLDER'));
+  }
 
-/** @override */
-importer.RuntimeControllerEnvironment.prototype.showImportRoot = function() {
-  return this.getDriveRoot_()
-      .then(this.demandCloudFolder_.bind(this))
-      .then(this.revealDirectory_.bind(this))
-      .catch(importer.getLogger().catcher('import-root-provision-and-reveal'));
-};
+  /** @override */
+  showImportRoot() {
+    return this.getDriveRoot_()
+        .then(this.demandCloudFolder_.bind(this))
+        .then(this.revealDirectory_.bind(this))
+        .catch(
+            importer.getLogger().catcher('import-root-provision-and-reveal'));
+  }
 
-/** @override */
-importer.RuntimeControllerEnvironment.prototype.getImportDestination = function(
-    date) {
-  return this.getDriveRoot_()
-      .then(this.demandCloudFolder_.bind(this))
-      .then(
-          /**
-           * @param {!DirectoryEntry} root
-           * @return {!Promise<!DirectoryEntry>}
-           */
-          root => {
-            return importer.demandChildDirectory(
-                root, importer.getDirectoryNameForDate(date));
-          })
-      .catch(importer.getLogger().catcher('import-destination-provision'));
-};
+  /** @override */
+  getImportDestination(date) {
+    return this.getDriveRoot_()
+        .then(this.demandCloudFolder_.bind(this))
+        .then(
+            /**
+             * @param {!DirectoryEntry} root
+             * @return {!Promise<!DirectoryEntry>}
+             */
+            root => {
+              return importer.demandChildDirectory(
+                  root, importer.getDirectoryNameForDate(date));
+            })
+        .catch(importer.getLogger().catcher('import-destination-provision'));
+  }
 
-/** @override */
-importer.RuntimeControllerEnvironment.prototype.showImportDestination =
-    function(date) {
-  return this.getImportDestination(date)
-      .then(this.revealDirectory_.bind(this))
-      .catch(importer.getLogger().catcher('import-destination-reveal'));
+  /** @override */
+  showImportDestination(date) {
+    return this.getImportDestination(date)
+        .then(this.revealDirectory_.bind(this))
+        .catch(importer.getLogger().catcher('import-destination-reveal'));
+  }
 };
diff --git a/ui/file_manager/file_manager/foreground/js/last_modified_controller.js b/ui/file_manager/file_manager/foreground/js/last_modified_controller.js
index 33fe4b4..0b0f6c7 100644
--- a/ui/file_manager/file_manager/foreground/js/last_modified_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/last_modified_controller.js
@@ -4,36 +4,35 @@
 
 /**
  * Controls last modified column in the file table.
- * @param {!FileTable} fileTable File table UI.
- * @param {!DirectoryModel} directoryModel Directory model.
- * @constructor
- * @struct
  */
-function LastModifiedController(fileTable, directoryModel) {
+class LastModifiedController {
   /**
-   * @private {!FileTable}
+   * @param {!FileTable} fileTable File table UI.
+   * @param {!DirectoryModel} directoryModel Directory model.
    */
-  this.fileTable_ = fileTable;
+  constructor(fileTable, directoryModel) {
+    /** @private @const {!FileTable} */
+    this.fileTable_ = fileTable;
+
+    /** @private @const {!DirectoryModel} */
+    this.directoryModel_ = directoryModel;
+
+    this.directoryModel_.addEventListener(
+        'scan-started', this.onScanStarted_.bind(this));
+  }
 
   /**
-   * @private {!DirectoryModel}
+   * Handles directory scan start.
+   * @private
    */
-  this.directoryModel_ = directoryModel;
-
-  this.directoryModel_.addEventListener(
-      'scan-started', this.onScanStarted_.bind(this));
+  onScanStarted_() {
+    // If the current directory is Recent root, request FileTable to use
+    // modificationByMeTime instead of modificationTime in last modified column.
+    const useModificationByMeTime =
+        this.directoryModel_.getCurrentRootType() ===
+        VolumeManagerCommon.RootType.RECENT;
+    this.fileTable_.setUseModificationByMeTime(useModificationByMeTime);
+    this.directoryModel_.getFileList().setUseModificationByMeTime(
+        useModificationByMeTime);
+  }
 }
-
-/**
- * Handles directory scan start.
- * @private
- */
-LastModifiedController.prototype.onScanStarted_ = function() {
-  // If the current directory is Recent root, request FileTable to use
-  // modificationByMeTime instead of modificationTime in last modified column.
-  const useModificationByMeTime = this.directoryModel_.getCurrentRootType() ===
-      VolumeManagerCommon.RootType.RECENT;
-  this.fileTable_.setUseModificationByMeTime(useModificationByMeTime);
-  this.directoryModel_.getFileList().setUseModificationByMeTime(
-      useModificationByMeTime);
-};
diff --git a/ui/file_manager/file_manager/foreground/js/metadata_update_controller.js b/ui/file_manager/file_manager/foreground/js/metadata_update_controller.js
index fd621cc..2042e6de 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata_update_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata_update_controller.js
@@ -4,124 +4,114 @@
 
 /**
  * Controller for list contents update.
- * @param {!ListContainer} listContainer
- * @param {!DirectoryModel} directoryModel
- * @param {!MetadataModel} metadataModel
- * @param {!FileMetadataFormatter} fileMetadataFormatter
- * @constructor
- * @struct
  */
-function MetadataUpdateController(
-    listContainer, directoryModel, metadataModel, fileMetadataFormatter) {
+class MetadataUpdateController {
   /**
-   * @private {!DirectoryModel}
-   * @const
+   * @param {!ListContainer} listContainer
+   * @param {!DirectoryModel} directoryModel
+   * @param {!MetadataModel} metadataModel
+   * @param {!FileMetadataFormatter} fileMetadataFormatter
    */
-  this.directoryModel_ = directoryModel;
+  constructor(
+      listContainer, directoryModel, metadataModel, fileMetadataFormatter) {
+    /** @private @const {!DirectoryModel} */
+    this.directoryModel_ = directoryModel;
+
+    /** @private @const {!MetadataModel} */
+    this.metadataModel_ = metadataModel;
+
+    /** @private @const {!ListContainer} */
+    this.listContainer_ = listContainer;
+
+    /** @private @const {!FileMetadataFormatter} */
+    this.fileMetadataFormatter_ = fileMetadataFormatter;
+
+    chrome.fileManagerPrivate.onPreferencesChanged.addListener(
+        this.onPreferencesChanged_.bind(this));
+    this.onPreferencesChanged_();
+    metadataModel.addEventListener(
+        'update', this.onCachedMetadataUpdate_.bind(this));
+
+    // Update metadata to change 'Today' and 'Yesterday' dates.
+    const today = new Date();
+    today.setHours(0);
+    today.setMinutes(0);
+    today.setSeconds(0);
+    today.setMilliseconds(0);
+    setTimeout(
+        this.dailyUpdateModificationTime_.bind(this),
+        today.getTime() + MetadataUpdateController.MILLISECONDS_IN_DAY_ -
+            Date.now() + 1000);
+  }
 
   /**
-   * @private {!MetadataModel}
-   * @const
+   * Clears metadata cache for the current directory and its decendents.
    */
-  this.metadataModel_ = metadataModel;
+  refreshCurrentDirectoryMetadata() {
+    const entries = this.directoryModel_.getFileList().slice();
+    const directoryEntry = this.directoryModel_.getCurrentDirEntry();
+    if (!directoryEntry) {
+      return;
+    }
+
+    // TODO(dgozman): refresh content metadata only when modificationTime
+    // changed.
+    const isFakeEntry = util.isFakeEntry(directoryEntry);
+    const changedEntries =
+        (isFakeEntry ? [] : [directoryEntry]).concat(entries);
+    this.metadataModel_.notifyEntriesChanged(changedEntries);
+
+    // We don't pass callback here. When new metadata arrives, we have an
+    // observer registered to update the UI.
+    this.metadataModel_.get(
+        changedEntries, this.directoryModel_.getPrefetchPropertyNames());
+  }
 
   /**
-   * @private {!ListContainer}
-   * @const
+   * Handles local metadata changes in the currect directory.
+   * @param {Event} event Change event.
+   * @private
    */
-  this.listContainer_ = listContainer;
+  onCachedMetadataUpdate_(event) {
+    // TODO(hirono): Specify property name instead of metadata type.
+    this.listContainer_.currentView.updateListItemsMetadata(
+        'filesystem', event.entries);
+    this.listContainer_.currentView.updateListItemsMetadata(
+        'external', event.entries);
+  }
 
   /**
-   * @private {!FileMetadataFormatter}
-   * @const
+   * @private
    */
-  this.fileMetadataFormatter_ = fileMetadataFormatter;
+  dailyUpdateModificationTime_() {
+    const entries = /** @type {!Array<!Entry>} */ (
+        this.directoryModel_.getFileList().slice());
+    this.metadataModel_.get(entries, ['modificationTime']).then(() => {
+      this.listContainer_.currentView.updateListItemsMetadata(
+          'filesystem', entries);
+    });
+    setTimeout(
+        this.dailyUpdateModificationTime_.bind(this),
+        MetadataUpdateController.MILLISECONDS_IN_DAY_);
+  }
 
-  chrome.fileManagerPrivate.onPreferencesChanged.addListener(
-      this.onPreferencesChanged_.bind(this));
-  this.onPreferencesChanged_();
-  metadataModel.addEventListener(
-      'update', this.onCachedMetadataUpdate_.bind(this));
-
-  // Update metadata to change 'Today' and 'Yesterday' dates.
-  const today = new Date();
-  today.setHours(0);
-  today.setMinutes(0);
-  today.setSeconds(0);
-  today.setMilliseconds(0);
-  setTimeout(
-      this.dailyUpdateModificationTime_.bind(this),
-      today.getTime() + MetadataUpdateController.MILLISECONDS_IN_DAY_ -
-          Date.now() + 1000);
+  /**
+   * @private
+   */
+  onPreferencesChanged_() {
+    chrome.fileManagerPrivate.getPreferences(prefs => {
+      const use12hourClock = !prefs.use24hourClock;
+      this.fileMetadataFormatter_.setDateTimeFormat(use12hourClock);
+      // TODO(oka): Remove these two lines, and add fileMetadataFormatter to
+      // constructor for each field instead.
+      this.listContainer_.table.setDateTimeFormat(use12hourClock);
+      this.refreshCurrentDirectoryMetadata();
+    });
+  }
 }
 
 /**
  * Number of milliseconds in a day.
- * @const {number}
+ * @private @const {number}
  */
 MetadataUpdateController.MILLISECONDS_IN_DAY_ = 24 * 60 * 60 * 1000;
-
-/**
- * Clears metadata cache for the current directory and its decendents.
- */
-MetadataUpdateController.prototype.refreshCurrentDirectoryMetadata =
-    function() {
-  const entries = this.directoryModel_.getFileList().slice();
-  const directoryEntry = this.directoryModel_.getCurrentDirEntry();
-  if (!directoryEntry) {
-    return;
-  }
-
-  // TODO(dgozman): refresh content metadata only when modificationTime
-  // changed.
-  const isFakeEntry = util.isFakeEntry(directoryEntry);
-  const changedEntries = (isFakeEntry ? [] : [directoryEntry]).concat(entries);
-  this.metadataModel_.notifyEntriesChanged(changedEntries);
-
-  // We don't pass callback here. When new metadata arrives, we have an
-  // observer registered to update the UI.
-  this.metadataModel_.get(
-      changedEntries, this.directoryModel_.getPrefetchPropertyNames());
-};
-
-/**
- * Handles local metadata changes in the currect directory.
- * @param {Event} event Change event.
- * @private
- */
-MetadataUpdateController.prototype.onCachedMetadataUpdate_ = function(event) {
-  // TODO(hirono): Specify property name instead of metadata type.
-  this.listContainer_.currentView.updateListItemsMetadata(
-      'filesystem', event.entries);
-  this.listContainer_.currentView.updateListItemsMetadata(
-      'external', event.entries);
-};
-
-/**
- * @private
- */
-MetadataUpdateController.prototype.dailyUpdateModificationTime_ = function() {
-  const entries = /** @type {!Array<!Entry>} */ (
-      this.directoryModel_.getFileList().slice());
-  this.metadataModel_.get(entries, ['modificationTime']).then(() => {
-    this.listContainer_.currentView.updateListItemsMetadata(
-        'filesystem', entries);
-  });
-  setTimeout(
-      this.dailyUpdateModificationTime_.bind(this),
-      MetadataUpdateController.MILLISECONDS_IN_DAY_);
-};
-
-/**
- * @private
- */
-MetadataUpdateController.prototype.onPreferencesChanged_ = function() {
-  chrome.fileManagerPrivate.getPreferences(prefs => {
-    const use12hourClock = !prefs.use24hourClock;
-    this.fileMetadataFormatter_.setDateTimeFormat(use12hourClock);
-    // TODO(oka): Remove these two lines, and add fileMetadataFormatter to
-    // constructor for each field instead.
-    this.listContainer_.table.setDateTimeFormat(use12hourClock);
-    this.refreshCurrentDirectoryMetadata();
-  });
-};
diff --git a/ui/file_manager/file_manager/foreground/js/naming_controller.js b/ui/file_manager/file_manager/foreground/js/naming_controller.js
index d534e4b..d9dc155a 100644
--- a/ui/file_manager/file_manager/foreground/js/naming_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/naming_controller.js
@@ -4,362 +4,339 @@
 
 /**
  * Controller to handle naming.
- *
- * @param {!ListContainer} listContainer
- * @param {!cr.ui.dialogs.AlertDialog} alertDialog
- * @param {!cr.ui.dialogs.ConfirmDialog} confirmDialog
- * @param {!DirectoryModel} directoryModel
- * @param {!FileFilter} fileFilter
- * @param {!FileSelectionHandler} selectionHandler
- * @constructor
- * @struct
  */
-function NamingController(
-    listContainer, alertDialog, confirmDialog, directoryModel, fileFilter,
-    selectionHandler) {
+class NamingController {
   /**
-   * @type {!ListContainer}
-   * @const
-   * @private
+   * @param {!ListContainer} listContainer
+   * @param {!cr.ui.dialogs.AlertDialog} alertDialog
+   * @param {!cr.ui.dialogs.ConfirmDialog} confirmDialog
+   * @param {!DirectoryModel} directoryModel
+   * @param {!FileFilter} fileFilter
+   * @param {!FileSelectionHandler} selectionHandler
    */
-  this.listContainer_ = listContainer;
+  constructor(
+      listContainer, alertDialog, confirmDialog, directoryModel, fileFilter,
+      selectionHandler) {
+    /** @private @const {!ListContainer} */
+    this.listContainer_ = listContainer;
+
+    /** @private @const {!cr.ui.dialogs.AlertDialog} */
+    this.alertDialog_ = alertDialog;
+
+    /** @private @const {!cr.ui.dialogs.ConfirmDialog} */
+    this.confirmDialog_ = confirmDialog;
+
+    /** @private @const {!DirectoryModel} */
+    this.directoryModel_ = directoryModel;
+
+    /** @private @const {!FileFilter} */
+    this.fileFilter_ = fileFilter;
+
+    /** @private @const {!FileSelectionHandler} */
+    this.selectionHandler_ = selectionHandler;
+
+    // Register events.
+    this.listContainer_.renameInput.addEventListener(
+        'keydown', this.onRenameInputKeyDown_.bind(this));
+    this.listContainer_.renameInput.addEventListener(
+        'blur', this.onRenameInputBlur_.bind(this));
+  }
 
   /**
-   * @type {!cr.ui.dialogs.AlertDialog}
-   * @const
-   * @private
+   * Verifies the user entered name for file or folder to be created or
+   * renamed to. See also util.validateFileName.
+   *
+   * @param {!DirectoryEntry} parentEntry The URL of the parent directory entry.
+   * @param {string} name New file or folder name.
+   * @param {function(boolean)} onDone Function to invoke when user closes the
+   *    warning box or immediately if file name is correct. If the name was
+   *    valid it is passed true, and false otherwise.
    */
-  this.alertDialog_ = alertDialog;
-
-  /**
-   * @type {!cr.ui.dialogs.ConfirmDialog}
-   * @const
-   * @private
-   */
-  this.confirmDialog_ = confirmDialog;
-
-  /**
-   * @type {!DirectoryModel}
-   * @const
-   * @private
-   */
-  this.directoryModel_ = directoryModel;
-
-  /**
-   * @type {!FileFilter}
-   * @const
-   * @private
-   */
-  this.fileFilter_ = fileFilter;
-
-  /**
-   * @type {!FileSelectionHandler}
-   * @const
-   * @private
-   */
-  this.selectionHandler_ = selectionHandler;
-
-  // Register events.
-  this.listContainer_.renameInput.addEventListener(
-      'keydown', this.onRenameInputKeyDown_.bind(this));
-  this.listContainer_.renameInput.addEventListener(
-      'blur', this.onRenameInputBlur_.bind(this));
-}
-
-/**
- * Verifies the user entered name for file or folder to be created or
- * renamed to. See also util.validateFileName.
- *
- * @param {!DirectoryEntry} parentEntry The URL of the parent directory entry.
- * @param {string} name New file or folder name.
- * @param {function(boolean)} onDone Function to invoke when user closes the
- *    warning box or immediatelly if file name is correct. If the name was
- *    valid it is passed true, and false otherwise.
- */
-NamingController.prototype.validateFileName = function(
-    parentEntry, name, onDone) {
-  const fileNameErrorPromise = util.validateFileName(
-      parentEntry, name, !this.fileFilter_.isHiddenFilesVisible());
-  fileNameErrorPromise
-      .then(
-          onDone.bind(null, true),
-          message => {
-            this.alertDialog_.show(
-                /** @type {string} */ (message), onDone.bind(null, false));
-          })
-      .catch(error => {
-        console.error(error.stack || error);
-      });
-};
-
-/**
- * @param {string} filename
- * @return {Promise<string>}
- */
-NamingController.prototype.validateFileNameForSaving = function(filename) {
-  const directory =
-      /** @type {DirectoryEntry} */ (this.directoryModel_.getCurrentDirEntry());
-  const currentDirUrl = directory.toURL().replace(/\/?$/, '/');
-  const fileUrl = currentDirUrl + encodeURIComponent(filename);
-
-  return new Promise(this.validateFileName.bind(this, directory, filename))
-      .then(isValid => {
-        if (!isValid) {
-          return Promise.reject('Invalid filename.');
-        }
-
-        if (directory && util.isFakeEntry(directory)) {
-          // Can't save a file into a fake directory.
-          return Promise.reject('Cannot save into fake entry.');
-        }
-
-        return new Promise(
-            directory.getFile.bind(directory, filename, {create: false}));
-      })
-      .then(
-          () => {
-            // An existing file is found. Show confirmation dialog to
-            // overwrite it. If the user select "OK" on the dialog, save it.
-            return new Promise((fulfill, reject) => {
-              this.confirmDialog_.show(
-                  strf('CONFIRM_OVERWRITE_FILE', filename),
-                  fulfill.bind(null, fileUrl), reject.bind(null, 'Cancelled'),
-                  () => {});
-            });
-          },
-          error => {
-            if (error.name == util.FileError.NOT_FOUND_ERR) {
-              // The file does not exist, so it should be ok to create a
-              // new file.
-              return fileUrl;
-            }
-
-            if (error.name == util.FileError.TYPE_MISMATCH_ERR) {
-              // An directory is found.
-              // Do not allow to overwrite directory.
+  validateFileName(parentEntry, name, onDone) {
+    const fileNameErrorPromise = util.validateFileName(
+        parentEntry, name, !this.fileFilter_.isHiddenFilesVisible());
+    fileNameErrorPromise
+        .then(
+            onDone.bind(null, true),
+            message => {
               this.alertDialog_.show(
-                  strf('DIRECTORY_ALREADY_EXISTS', filename));
+                  /** @type {string} */ (message), onDone.bind(null, false));
+            })
+        .catch(error => {
+          console.error(error.stack || error);
+        });
+  }
+
+  /**
+   * @param {string} filename
+   * @return {Promise<string>}
+   */
+  validateFileNameForSaving(filename) {
+    const directory =
+        /** @type {DirectoryEntry} */ (
+            this.directoryModel_.getCurrentDirEntry());
+    const currentDirUrl = directory.toURL().replace(/\/?$/, '/');
+    const fileUrl = currentDirUrl + encodeURIComponent(filename);
+
+    return new Promise(this.validateFileName.bind(this, directory, filename))
+        .then(isValid => {
+          if (!isValid) {
+            return Promise.reject('Invalid filename.');
+          }
+
+          if (directory && util.isFakeEntry(directory)) {
+            // Can't save a file into a fake directory.
+            return Promise.reject('Cannot save into fake entry.');
+          }
+
+          return new Promise(
+              directory.getFile.bind(directory, filename, {create: false}));
+        })
+        .then(
+            () => {
+              // An existing file is found. Show confirmation dialog to
+              // overwrite it. If the user select "OK" on the dialog, save it.
+              return new Promise((fulfill, reject) => {
+                this.confirmDialog_.show(
+                    strf('CONFIRM_OVERWRITE_FILE', filename),
+                    fulfill.bind(null, fileUrl), reject.bind(null, 'Cancelled'),
+                    () => {});
+              });
+            },
+            error => {
+              if (error.name == util.FileError.NOT_FOUND_ERR) {
+                // The file does not exist, so it should be ok to create a
+                // new file.
+                return fileUrl;
+              }
+
+              if (error.name == util.FileError.TYPE_MISMATCH_ERR) {
+                // An directory is found.
+                // Do not allow to overwrite directory.
+                this.alertDialog_.show(
+                    strf('DIRECTORY_ALREADY_EXISTS', filename));
+                return Promise.reject(error);
+              }
+
+              // Unexpected error.
+              console.error('File save failed: ' + error.code);
               return Promise.reject(error);
-            }
-
-            // Unexpected error.
-            console.error('File save failed: ' + error.code);
-            return Promise.reject(error);
-          });
-};
-
-/**
- * @return {boolean}
- */
-NamingController.prototype.isRenamingInProgress = function() {
-  return !!this.listContainer_.renameInput.currentEntry;
-};
-
-NamingController.prototype.initiateRename = function() {
-  const item = this.listContainer_.currentList.ensureLeadItemExists();
-  if (!item) {
-    return;
-  }
-  const label = item.querySelector('.filename-label');
-  const input = this.listContainer_.renameInput;
-  const currentEntry =
-      this.listContainer_.currentList.dataModel.item(item.listIndex);
-
-  input.value = label.textContent;
-  item.setAttribute('renaming', '');
-  label.parentNode.appendChild(input);
-  input.focus();
-
-  const selectionEnd = input.value.lastIndexOf('.');
-  if (currentEntry.isFile && selectionEnd !== -1) {
-    input.selectionStart = 0;
-    input.selectionEnd = selectionEnd;
-  } else {
-    input.select();
+            });
   }
 
-  // This has to be set late in the process so we don't handle spurious
-  // blur events.
-  input.currentEntry = currentEntry;
-  this.listContainer_.startBatchUpdates();
-};
-
-/**
- * Restores the item which is being renamed while refreshing the file list. Do
- * nothing if no item is being renamed or such an item disappeared.
- *
- * While refreshing file list it gets repopulated with new file entries.
- * There is not a big difference whether DOM items stay the same or not.
- * Except for the item that the user is renaming.
- */
-NamingController.prototype.restoreItemBeingRenamed = function() {
-  if (!this.isRenamingInProgress()) {
-    return;
+  /**
+   * @return {boolean}
+   */
+  isRenamingInProgress() {
+    return !!this.listContainer_.renameInput.currentEntry;
   }
 
-  const dm = this.directoryModel_;
-  const leadIndex = dm.getFileListSelection().leadIndex;
-  if (leadIndex < 0) {
-    return;
+  initiateRename() {
+    const item = this.listContainer_.currentList.ensureLeadItemExists();
+    if (!item) {
+      return;
+    }
+    const label = item.querySelector('.filename-label');
+    const input = this.listContainer_.renameInput;
+    const currentEntry =
+        this.listContainer_.currentList.dataModel.item(item.listIndex);
+
+    input.value = label.textContent;
+    item.setAttribute('renaming', '');
+    label.parentNode.appendChild(input);
+    input.focus();
+
+    const selectionEnd = input.value.lastIndexOf('.');
+    if (currentEntry.isFile && selectionEnd !== -1) {
+      input.selectionStart = 0;
+      input.selectionEnd = selectionEnd;
+    } else {
+      input.select();
+    }
+
+    // This has to be set late in the process so we don't handle spurious
+    // blur events.
+    input.currentEntry = currentEntry;
+    this.listContainer_.startBatchUpdates();
   }
 
-  const leadEntry = /** @type {Entry} */ (dm.getFileList().item(leadIndex));
-  if (!util.isSameEntry(
-          this.listContainer_.renameInput.currentEntry, leadEntry)) {
-    return;
-  }
-
-  const leadListItem =
-      this.listContainer_.findListItemForNode(this.listContainer_.renameInput);
-  if (this.listContainer_.currentListType == ListContainer.ListType.DETAIL) {
-    this.listContainer_.table.updateFileMetadata(leadListItem, leadEntry);
-  }
-  this.listContainer_.currentList.restoreLeadItem(leadListItem);
-};
-
-/**
- * @param {Event} event Key event.
- * @private
- */
-NamingController.prototype.onRenameInputKeyDown_ = function(event) {
-  // Ignore key events if event.keyCode is VK_PROCESSKEY(229).
-  // TODO(fukino): Remove this workaround once crbug.com/644140 is fixed.
-  if (event.keyCode === 229) {
-    return;
-  }
-
-  if (!this.isRenamingInProgress()) {
-    return;
-  }
-
-  // Do not move selection or lead item in list during rename.
-  if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
-    event.stopPropagation();
-  }
-
-  switch (util.getKeyModifiers(event) + event.key) {
-    case 'Escape':
-      this.cancelRename_();
-      event.preventDefault();
-      break;
-
-    case 'Enter':
-      this.commitRename_();
-      event.preventDefault();
-      break;
-  }
-};
-
-/**
- * @param {Event} event Blur event.
- * @private
- */
-NamingController.prototype.onRenameInputBlur_ = function(event) {
-  if (this.isRenamingInProgress() &&
-      !this.listContainer_.renameInput.validation_) {
-    this.commitRename_();
-  }
-};
-
-/**
- * @private
- */
-NamingController.prototype.commitRename_ = function() {
-  const input = this.listContainer_.renameInput;
-  const entry = input.currentEntry;
-  const newName = input.value;
-
-  if (!newName || newName == entry.name) {
-    this.cancelRename_();
-    return;
-  }
-
-  const renamedItemElement =
-      this.listContainer_.findListItemForNode(this.listContainer_.renameInput);
-  const nameNode = renamedItemElement.querySelector('.filename-label');
-
-  input.validation_ = true;
-  const validationDone = valid => {
-    input.validation_ = false;
-
-    if (!valid) {
-      // Cancel rename if it fails to restore focus from alert dialog.
-      // Otherwise, just cancel the commitment and continue to rename.
-      if (document.activeElement != input) {
-        this.cancelRename_();
-      }
+  /**
+   * Restores the item which is being renamed while refreshing the file list. Do
+   * nothing if no item is being renamed or such an item disappeared.
+   *
+   * While refreshing file list it gets repopulated with new file entries.
+   * There is not a big difference whether DOM items stay the same or not.
+   * Except for the item that the user is renaming.
+   */
+  restoreItemBeingRenamed() {
+    if (!this.isRenamingInProgress()) {
       return;
     }
 
-    // Validation succeeded. Do renaming.
-    this.listContainer_.renameInput.currentEntry = null;
-    if (this.listContainer_.renameInput.parentNode) {
-      this.listContainer_.renameInput.parentNode.removeChild(
-          this.listContainer_.renameInput);
+    const dm = this.directoryModel_;
+    const leadIndex = dm.getFileListSelection().leadIndex;
+    if (leadIndex < 0) {
+      return;
     }
-    renamedItemElement.setAttribute('renaming', 'provisional');
 
-    // Optimistically apply new name immediately to avoid flickering in
-    // case of success.
-    nameNode.textContent = newName;
+    const leadEntry = /** @type {Entry} */ (dm.getFileList().item(leadIndex));
+    if (!util.isSameEntry(
+            this.listContainer_.renameInput.currentEntry, leadEntry)) {
+      return;
+    }
 
-    util.rename(
-        entry, newName,
-        newEntry => {
-          this.directoryModel_.onRenameEntry(entry, assert(newEntry), () => {
-            // Select new entry.
-            this.listContainer_.currentList.selectionModel.selectedIndex =
-                this.directoryModel_.getFileList().indexOf(newEntry);
-            // Force to update selection immediately.
-            this.selectionHandler_.onFileSelectionChanged();
+    const leadListItem = this.listContainer_.findListItemForNode(
+        this.listContainer_.renameInput);
+    if (this.listContainer_.currentListType == ListContainer.ListType.DETAIL) {
+      this.listContainer_.table.updateFileMetadata(leadListItem, leadEntry);
+    }
+    this.listContainer_.currentList.restoreLeadItem(leadListItem);
+  }
 
+  /**
+   * @param {Event} event Key event.
+   * @private
+   */
+  onRenameInputKeyDown_(event) {
+    // Ignore key events if event.keyCode is VK_PROCESSKEY(229).
+    // TODO(fukino): Remove this workaround once crbug.com/644140 is fixed.
+    if (event.keyCode === 229) {
+      return;
+    }
+
+    if (!this.isRenamingInProgress()) {
+      return;
+    }
+
+    // Do not move selection or lead item in list during rename.
+    if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
+      event.stopPropagation();
+    }
+
+    switch (util.getKeyModifiers(event) + event.key) {
+      case 'Escape':
+        this.cancelRename_();
+        event.preventDefault();
+        break;
+
+      case 'Enter':
+        this.commitRename_();
+        event.preventDefault();
+        break;
+    }
+  }
+
+  /**
+   * @param {Event} event Blur event.
+   * @private
+   */
+  onRenameInputBlur_(event) {
+    if (this.isRenamingInProgress() &&
+        !this.listContainer_.renameInput.validation_) {
+      this.commitRename_();
+    }
+  }
+
+  /**
+   * @private
+   */
+  commitRename_() {
+    const input = this.listContainer_.renameInput;
+    const entry = input.currentEntry;
+    const newName = input.value;
+
+    if (!newName || newName == entry.name) {
+      this.cancelRename_();
+      return;
+    }
+
+    const renamedItemElement = this.listContainer_.findListItemForNode(
+        this.listContainer_.renameInput);
+    const nameNode = renamedItemElement.querySelector('.filename-label');
+
+    input.validation_ = true;
+    const validationDone = valid => {
+      input.validation_ = false;
+
+      if (!valid) {
+        // Cancel rename if it fails to restore focus from alert dialog.
+        // Otherwise, just cancel the commitment and continue to rename.
+        if (document.activeElement != input) {
+          this.cancelRename_();
+        }
+        return;
+      }
+
+      // Validation succeeded. Do renaming.
+      this.listContainer_.renameInput.currentEntry = null;
+      if (this.listContainer_.renameInput.parentNode) {
+        this.listContainer_.renameInput.parentNode.removeChild(
+            this.listContainer_.renameInput);
+      }
+      renamedItemElement.setAttribute('renaming', 'provisional');
+
+      // Optimistically apply new name immediately to avoid flickering in
+      // case of success.
+      nameNode.textContent = newName;
+
+      util.rename(
+          entry, newName,
+          newEntry => {
+            this.directoryModel_.onRenameEntry(entry, assert(newEntry), () => {
+              // Select new entry.
+              this.listContainer_.currentList.selectionModel.selectedIndex =
+                  this.directoryModel_.getFileList().indexOf(newEntry);
+              // Force to update selection immediately.
+              this.selectionHandler_.onFileSelectionChanged();
+
+              renamedItemElement.removeAttribute('renaming');
+              this.listContainer_.endBatchUpdates();
+
+              // Focus may go out of the list. Back it to the list.
+              this.listContainer_.currentList.focus();
+            });
+          },
+          error => {
+            // Write back to the old name.
+            nameNode.textContent = entry.name;
             renamedItemElement.removeAttribute('renaming');
             this.listContainer_.endBatchUpdates();
 
-            // Focus may go out of the list. Back it to the list.
-            this.listContainer_.currentList.focus();
+            // Show error dialog.
+            const message = util.getRenameErrorMessage(error, entry, newName);
+            this.alertDialog_.show(message);
           });
-        },
-        error => {
-          // Write back to the old name.
-          nameNode.textContent = entry.name;
-          renamedItemElement.removeAttribute('renaming');
-          this.listContainer_.endBatchUpdates();
+    };
 
-          // Show error dialog.
-          const message = util.getRenameErrorMessage(error, entry, newName);
-          this.alertDialog_.show(message);
-        });
-  };
-
-  // TODO(mtomasz): this.getCurrentDirectoryEntry() might not return the actual
-  // parent if the directory content is a search result. Fix it to do proper
-  // validation.
-  this.validateFileName(
-      /** @type {!DirectoryEntry} */ (
-          this.directoryModel_.getCurrentDirEntry()),
-      newName, validationDone.bind(this));
-};
-
-/**
- * @private
- */
-NamingController.prototype.cancelRename_ = function() {
-  this.listContainer_.renameInput.currentEntry = null;
-
-  const item =
-      this.listContainer_.findListItemForNode(this.listContainer_.renameInput);
-  if (item) {
-    item.removeAttribute('renaming');
+    // TODO(mtomasz): this.getCurrentDirectoryEntry() might not return the
+    // actual parent if the directory content is a search result. Fix it to do
+    // proper validation.
+    this.validateFileName(
+        /** @type {!DirectoryEntry} */ (
+            this.directoryModel_.getCurrentDirEntry()),
+        newName, validationDone.bind(this));
   }
 
-  const parent = this.listContainer_.renameInput.parentNode;
-  if (parent) {
-    parent.removeChild(this.listContainer_.renameInput);
+  /**
+   * @private
+   */
+  cancelRename_() {
+    this.listContainer_.renameInput.currentEntry = null;
+
+    const item = this.listContainer_.findListItemForNode(
+        this.listContainer_.renameInput);
+    if (item) {
+      item.removeAttribute('renaming');
+    }
+
+    const parent = this.listContainer_.renameInput.parentNode;
+    if (parent) {
+      parent.removeChild(this.listContainer_.renameInput);
+    }
+
+    this.listContainer_.endBatchUpdates();
+
+    // Focus may go out of the list. Back it to the list.
+    this.listContainer_.currentList.focus();
   }
-
-  this.listContainer_.endBatchUpdates();
-
-  // Focus may go out of the list. Back it to the list.
-  this.listContainer_.currentList.focus();
-};
+}
diff --git a/ui/file_manager/file_manager/foreground/js/providers_model.js b/ui/file_manager/file_manager/foreground/js/providers_model.js
index 42f3b8c..5066dc2 100644
--- a/ui/file_manager/file_manager/foreground/js/providers_model.js
+++ b/ui/file_manager/file_manager/foreground/js/providers_model.js
@@ -5,105 +5,83 @@
 
 /**
  * An item in the model. Represents a single providing extension.
- *
- * @param {string} providerId
- * @param {!chrome.fileManagerPrivate.IconSet} iconSet
- * @param {string} name
- * @param {boolean} configurable
- * @param {boolean} watchable
- * @param {boolean} multipleMounts
- * @param {string} source
- * @constructor
- * @struct
  */
-function ProvidersModelItem(
-    providerId, iconSet, name, configurable, watchable, multipleMounts,
-    source) {
+class ProvidersModelItem {
   /**
-   * @private {string}
-   * @const
+   * @param {string} providerId
+   * @param {!chrome.fileManagerPrivate.IconSet} iconSet
+   * @param {string} name
+   * @param {boolean} configurable
+   * @param {boolean} watchable
+   * @param {boolean} multipleMounts
+   * @param {string} source
    */
-  this.providerId_ = providerId;
+  constructor(
+      providerId, iconSet, name, configurable, watchable, multipleMounts,
+      source) {
+    /** @private @const {string} */
+    this.providerId_ = providerId;
 
-  /**
-   * @private {!chrome.fileManagerPrivate.IconSet}
-   * @const
-   */
-  this.iconSet_ = iconSet;
+    /** @private @const {!chrome.fileManagerPrivate.IconSet} */
+    this.iconSet_ = iconSet;
 
-  /**
-   * @private {string}
-   * @const
-   */
-  this.name_ = name;
+    /** @private @const {string} */
+    this.name_ = name;
 
-  /**
-   * @private {boolean}
-   * @const
-   */
-  this.configurable_ = configurable;
+    /** @private @const {boolean} */
+    this.configurable_ = configurable;
 
-  /**
-   * @private {boolean}
-   * @const
-   */
-  this.watchable_ = watchable;
+    /** @private @const {boolean} */
+    this.watchable_ = watchable;
 
-  /**
-   * @private {boolean}
-   * @const
-   */
-  this.multipleMounts_ = multipleMounts;
+    /** @private @const {boolean} */
+    this.multipleMounts_ = multipleMounts;
 
-  /**
-   * @private {string}
-   * @const
-   */
-  this.source_ = source;
-}
+    /** @private @const {string} */
+    this.source_ = source;
+  }
 
-ProvidersModelItem.prototype = {
   /**
    * @return {string}
    */
   get providerId() {
     return this.providerId_;
-  },
+  }
 
   /**
    * @return {!chrome.fileManagerPrivate.IconSet}
    */
   get iconSet() {
     return this.iconSet_;
-  },
+  }
 
   /**
    * @return {string}
    */
   get name() {
     return this.name_;
-  },
+  }
 
   /**
    * @return {boolean}
    */
   get configurable() {
     return this.configurable_;
-  },
+  }
 
   /**
    * @return {boolean}
    */
   get watchable() {
     return this.watchable_;
-  },
+  }
 
   /**
    * @return {boolean}
    */
   get multipleMounts() {
     return this.multipleMounts_;
-  },
+  }
 
   /**
    * @return {string}
@@ -111,76 +89,74 @@
   get source() {
     return this.source_;
   }
-};
+}
 
 /**
  * Model for providing extensions. Providers methods for fetching lists of
  * providing extensions as well as performing operations on them, such as
  * requesting a new mount point.
- *
- * @param {!VolumeManager} volumeManager
- * @constructor
- * @struct
  */
-function ProvidersModel(volumeManager) {
+class ProvidersModel {
   /**
-   * @private {!VolumeManager}
-   * @const
+   * @param {!VolumeManager} volumeManager
    */
-  this.volumeManager_ = volumeManager;
-}
+  constructor(volumeManager) {
+    /** @private @const {!VolumeManager} */
+    this.volumeManager_ = volumeManager;
+  }
 
-/**
- * @return {!Promise<Array<ProvidersModelItem>>}
- */
-ProvidersModel.prototype.getInstalledProviders = () => {
-  return new Promise((fulfill, reject) => {
-    chrome.fileManagerPrivate.getProviders(providers => {
-      if (chrome.runtime.lastError) {
-        reject(chrome.runtime.lastError.message);
-        return;
-      }
-      const results = [];
-      providers.forEach(provider => {
-        results.push(new ProvidersModelItem(
-            provider.providerId, provider.iconSet, provider.name,
-            provider.configurable, provider.watchable, provider.multipleMounts,
-            provider.source));
+  /**
+   * @return {!Promise<Array<ProvidersModelItem>>}
+   */
+  getInstalledProviders() {
+    return new Promise((fulfill, reject) => {
+      chrome.fileManagerPrivate.getProviders(providers => {
+        if (chrome.runtime.lastError) {
+          reject(chrome.runtime.lastError.message);
+          return;
+        }
+        const results = [];
+        providers.forEach(provider => {
+          results.push(new ProvidersModelItem(
+              provider.providerId, provider.iconSet, provider.name,
+              provider.configurable, provider.watchable,
+              provider.multipleMounts, provider.source));
+        });
+        fulfill(results);
       });
-      fulfill(results);
     });
-  });
-};
+  }
 
-/**
- * @return {!Promise<Array<ProvidersModelItem>>}
- */
-ProvidersModel.prototype.getMountableProviders = function() {
-  return this.getInstalledProviders().then(providers => {
-    const mountedProviders = {};
-    for (let i = 0; i < this.volumeManager_.volumeInfoList.length; i++) {
-      const volumeInfo = this.volumeManager_.volumeInfoList.item(i);
-      if (volumeInfo.volumeType === VolumeManagerCommon.VolumeType.PROVIDED) {
-        mountedProviders[volumeInfo.providerId] = true;
+  /**
+   * @return {!Promise<Array<ProvidersModelItem>>}
+   */
+  getMountableProviders() {
+    return this.getInstalledProviders().then(providers => {
+      const mountedProviders = {};
+      for (let i = 0; i < this.volumeManager_.volumeInfoList.length; i++) {
+        const volumeInfo = this.volumeManager_.volumeInfoList.item(i);
+        if (volumeInfo.volumeType === VolumeManagerCommon.VolumeType.PROVIDED) {
+          mountedProviders[volumeInfo.providerId] = true;
+        }
       }
-    }
-    return providers.filter(item => {
-      // File systems handling files are mounted via file handlers. Device
-      // handlers are mounted when a device is inserted. Only network file
-      // systems are mounted manually by user via a menu.
-      return item.source === 'network' &&
-          (!mountedProviders[item.providerId] || item.multipleMounts);
+      return providers.filter(item => {
+        // File systems handling files are mounted via file handlers. Device
+        // handlers are mounted when a device is inserted. Only network file
+        // systems are mounted manually by user via a menu.
+        return item.source === 'network' &&
+            (!mountedProviders[item.providerId] || item.multipleMounts);
+      });
     });
-  });
-};
+  }
 
-/**
- * @param {string} providerId
- */
-ProvidersModel.prototype.requestMount = providerId => {
-  chrome.fileManagerPrivate.addProvidedFileSystem(assert(providerId), () => {
-    if (chrome.runtime.lastError) {
-      console.error(chrome.runtime.lastError.message);
-    }
-  });
-};
+  /**
+   * @param {string} providerId
+   */
+  requestMount(providerId) {
+    chrome.fileManagerPrivate.addProvidedFileSystem(assert(providerId), () => {
+      if (chrome.runtime.lastError) {
+        console.error(chrome.runtime.lastError.message);
+      }
+    });
+  }
+}
diff --git a/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js b/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js
index d815266..671168334 100644
--- a/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js
+++ b/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js
@@ -6,110 +6,479 @@
  * Loads a thumbnail using provided url. In CANVAS mode, loaded images
  * are attached as <canvas> element, while in IMAGE mode as <img>.
  * <canvas> renders faster than <img>, however has bigger memory overhead.
- *
- * @param {Entry} entry File entry.
- * @param {ThumbnailLoader.LoaderType=} opt_loaderType Canvas or Image loader,
- *     default: IMAGE.
- * @param {Object=} opt_metadata Metadata object.
- * @param {string=} opt_mediaType Media type.
- * @param {Array<ThumbnailLoader.LoadTarget>=} opt_loadTargets The list of load
- *     targets in preferential order. The default value is [CONTENT_METADATA,
- *     EXTERNAL_METADATA, FILE_ENTRY].
- * @param {number=} opt_priority Priority, the highest is 0. default: 2.
- * @constructor
  */
-function ThumbnailLoader(
-    entry, opt_loaderType, opt_metadata, opt_mediaType, opt_loadTargets,
-    opt_priority) {
-  const loadTargets = opt_loadTargets || [
-    ThumbnailLoader.LoadTarget.CONTENT_METADATA,
-    ThumbnailLoader.LoadTarget.EXTERNAL_METADATA,
-    ThumbnailLoader.LoadTarget.FILE_ENTRY
-  ];
-
+class ThumbnailLoader {
   /**
-   * @private {Entry}
-   * @const
+   * @param {!Entry} entry File entry.
+   * @param {!ThumbnailLoader.LoaderType=} opt_loaderType Canvas or Image
+   *     loader, default: IMAGE.
+   * @param {?Object=} opt_metadata Metadata object.
+   * @param {string=} opt_mediaType Media type.
+   * @param {!Array<ThumbnailLoader.LoadTarget>=} opt_loadTargets The list of
+   *     load targets in preferential order. The default value is
+   *     [CONTENT_METADATA, EXTERNAL_METADATA, FILE_ENTRY].
+   * @param {number=} opt_priority Priority, the highest is 0. default: 2.
    */
-  this.entry_ = entry;
+  constructor(
+      entry, opt_loaderType, opt_metadata, opt_mediaType, opt_loadTargets,
+      opt_priority) {
+    /** @private {boolean} */
+    this.canvasUpToDate_ = false;
 
-  this.mediaType_ = opt_mediaType || FileType.getMediaType(entry);
-  this.loaderType_ = opt_loaderType || ThumbnailLoader.LoaderType.IMAGE;
-  this.metadata_ = opt_metadata;
-  this.priority_ = (opt_priority !== undefined) ? opt_priority : 2;
+    /** @private {?Image} */
+    this.image_ = null;
+
+    /** @private {?number} */
+    this.taskId_ = null;
+
+    /** @private {?HTMLCanvasElement} */
+    this.canvas_ = null;
+
+    const loadTargets = opt_loadTargets || [
+      ThumbnailLoader.LoadTarget.CONTENT_METADATA,
+      ThumbnailLoader.LoadTarget.EXTERNAL_METADATA,
+      ThumbnailLoader.LoadTarget.FILE_ENTRY
+    ];
+
+    /** @private @const {!Entry} */
+    this.entry_ = entry;
+
+    /** @private @const {string} */
+    this.mediaType_ = opt_mediaType || FileType.getMediaType(entry);
+
+    /** @private @const {!ThumbnailLoader.LoaderType} */
+    this.loaderType_ = opt_loaderType || ThumbnailLoader.LoaderType.IMAGE;
+
+    /** @private @const {?Object|undefined} */
+    this.metadata_ = opt_metadata;
+
+    /** @private @const {number} */
+    this.priority_ = (opt_priority !== undefined) ? opt_priority : 2;
+
+    /**
+     * The image transform from metadata.
+     *
+     * TODO(tapted): I suspect this actually needs to be more complicated, but
+     * it can't be properly type-checked so long as |opt_metadata| is passed in
+     * merely as "{Object}".
+     *
+     * @private {?ImageTransformParam}
+     */
+    this.transform_ = null;
+
+    /** @private {?ThumbnailLoader.LoadTarget} */
+    this.loadTarget_ = null;
+
+    if (!opt_metadata) {
+      this.thumbnailUrl_ = entry.toURL();  // Use the URL directly.
+      this.loadTarget_ = ThumbnailLoader.LoadTarget.FILE_ENTRY;
+      return;
+    }
+
+    this.fallbackUrl_ = null;
+    this.thumbnailUrl_ = null;
+    if (opt_metadata.external && opt_metadata.external.customIconUrl) {
+      this.fallbackUrl_ = opt_metadata.external.customIconUrl;
+    }
+    const mimeType = opt_metadata && opt_metadata.contentMimeType;
+
+    for (let i = 0; i < loadTargets.length; i++) {
+      switch (loadTargets[i]) {
+        case ThumbnailLoader.LoadTarget.CONTENT_METADATA:
+          if (opt_metadata.thumbnail && opt_metadata.thumbnail.url) {
+            this.thumbnailUrl_ = opt_metadata.thumbnail.url;
+            this.transform_ =
+                opt_metadata.thumbnail && opt_metadata.thumbnail.transform;
+            this.loadTarget_ = ThumbnailLoader.LoadTarget.CONTENT_METADATA;
+          }
+          break;
+        case ThumbnailLoader.LoadTarget.EXTERNAL_METADATA:
+          if (opt_metadata.external && opt_metadata.external.thumbnailUrl &&
+              (!opt_metadata.external.present ||
+               !FileType.isImage(entry, mimeType))) {
+            this.thumbnailUrl_ = opt_metadata.external.thumbnailUrl;
+            this.croppedThumbnailUrl_ =
+                opt_metadata.external.croppedThumbnailUrl;
+            this.loadTarget_ = ThumbnailLoader.LoadTarget.EXTERNAL_METADATA;
+          }
+          break;
+        case ThumbnailLoader.LoadTarget.FILE_ENTRY:
+          if (FileType.isImage(entry, mimeType) ||
+              FileType.isVideo(entry, mimeType) ||
+              FileType.isRaw(entry, mimeType)) {
+            this.thumbnailUrl_ = entry.toURL();
+            this.transform_ =
+                opt_metadata.media && opt_metadata.media.imageTransform;
+            this.loadTarget_ = ThumbnailLoader.LoadTarget.FILE_ENTRY;
+          }
+          break;
+        default:
+          assertNotReached('Unkonwn load type: ' + loadTargets[i]);
+      }
+      if (this.thumbnailUrl_) {
+        break;
+      }
+    }
+
+    if (!this.thumbnailUrl_ && this.fallbackUrl_) {
+      // Use fallback as the primary thumbnail.
+      this.thumbnailUrl_ = this.fallbackUrl_;
+      this.fallbackUrl_ = null;
+    }  // else the generic thumbnail based on the media type will be used.
+  }
 
   /**
-   * The image transform from metadata.
-   *
-   * TODO(tapted): I suspect this actually needs to be more complicated, but
-   * it can't be properly type-checked so long as |opt_metadata| is passed in
-   * merely as "{Object}".
-   *
-   * @type {?ImageTransformParam}
+   * Returns the target of loading.
+   * @return {?ThumbnailLoader.LoadTarget}
    */
-  this.transform_ = null;
+  getLoadTarget() {
+    return this.loadTarget_;
+  }
 
   /**
-   * @type {?ThumbnailLoader.LoadTarget}
+   * Loads and attaches an image.
+   *
+   * @param {!Element} box Container element.
+   * @param {ThumbnailLoader.FillMode} fillMode Fill mode.
+   * @param {function(Image)} onSuccess Success callback, accepts the image.
+   * @param {number} autoFillThreshold Auto fill threshold.
+   * @param {number} boxWidth Container box's width.
+   * @param {number} boxHeight Container box's height.
+   */
+  load(box, fillMode, onSuccess, autoFillThreshold, boxWidth, boxHeight) {
+    if (!this.thumbnailUrl_) {
+      // Relevant CSS rules are in file_types.css.
+      box.setAttribute('generic-thumbnail', this.mediaType_);
+      return;
+    }
+
+    this.cancel();
+    this.canvasUpToDate_ = false;
+    this.image_ = new Image();
+    this.image_.setAttribute('alt', this.entry_.name);
+    this.image_.onload = () => {
+      this.attachImage_(box, fillMode, autoFillThreshold, boxWidth, boxHeight);
+      onSuccess(this.image_);
+    };
+    this.image_.onerror = () => {
+      if (this.fallbackUrl_) {
+        this.thumbnailUrl_ = this.fallbackUrl_;
+        this.fallbackUrl_ = null;
+        this.load(
+            box, fillMode, onSuccess,
+            ThumbnailLoader.AUTO_FILL_THRESHOLD_DEFAULT_VALUE, box.clientWidth,
+            box.clientHeight);
+      } else {
+        box.setAttribute('generic-thumbnail', this.mediaType_);
+      }
+    };
+
+    if (this.image_.src) {
+      console.warn('Thumbnail already loaded: ' + this.thumbnailUrl_);
+      return;
+    }
+
+    // TODO(mtomasz): Smarter calculation of the requested size.
+    const wasAttached = box.ownerDocument.contains(box);
+    const modificationTime = this.metadata_ && this.metadata_.filesystem &&
+        this.metadata_.filesystem.modificationTime &&
+        this.metadata_.filesystem.modificationTime.getTime();
+    this.taskId_ = ImageLoaderClient.loadToImage(
+        LoadImageRequest.createRequest({
+          url: this.thumbnailUrl_,
+          maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH,
+          maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT,
+          cache: true,
+          priority: this.priority_,
+          timestamp: modificationTime,
+          orientation: this.transform_
+        }),
+        this.image_, () => {}, () => {
+          this.image_.onerror(new Event('load-error'));
+        });
+  }
+
+  /**
+   * Loads thumbnail as data url. If data url of thumbnail can be fetched from
+   * metadata, this fetches it from it. Otherwise, this tries to load it from
+   * thumbnail loader.
+   * Compared with ThumbnailLoader.load, this method does not provide a
+   * functionality to fit image to a box.
+   *
+   * @param {ThumbnailLoader.FillMode} fillMode Only FIT and OVER_FILL is
+   *     supported. This takes effect only when external thumbnail source is
+   *     used.
+   * @return {!Promise<{data:string, width:number, height:number}>} A promise
+   *     which is resolved when data url is fetched.
+   *
+   * TODO(yawano): Support cancel operation.
+   */
+  loadAsDataUrl(fillMode) {
+    assert(
+        fillMode === ThumbnailLoader.FillMode.FIT ||
+        fillMode === ThumbnailLoader.FillMode.OVER_FILL);
+
+    return new Promise((resolve, reject) => {
+      // Load by using ImageLoaderClient.
+      const modificationTime = this.metadata_ && this.metadata_.filesystem &&
+          this.metadata_.filesystem.modificationTime &&
+          this.metadata_.filesystem.modificationTime.getTime();
+      let request = LoadImageRequest.createRequest({
+        url: this.thumbnailUrl_,
+        maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH,
+        maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT,
+        cache: true,
+        priority: this.priority_,
+        timestamp: modificationTime,
+        orientation: this.transform_
+      });
+
+      if (fillMode === ThumbnailLoader.FillMode.OVER_FILL) {
+        // Use cropped thumbnail url if available.
+        request.url = this.croppedThumbnailUrl_ ? this.croppedThumbnailUrl_ :
+                                                  this.thumbnailUrl_;
+
+        // Set crop option to image loader. Since image of croppedThumbnailUrl_
+        // is 360x360 with current implementation, it's no problem to crop it.
+        request.width = 360;
+        request.height = 360;
+        request.crop = true;
+      }
+
+      ImageLoaderClient.getInstance().load(request, result => {
+        if (result.status === LoadImageResponseStatus.SUCCESS) {
+          resolve(result);
+        } else {
+          reject(result);
+        }
+      });
+    });
+  }
+
+  /**
+   * Cancels loading the current image.
+   */
+  cancel() {
+    if (this.taskId_) {
+      this.image_.onload = () => {};
+      this.image_.onerror = () => {};
+      ImageLoaderClient.getInstance().cancel(this.taskId_);
+      this.taskId_ = null;
+    }
+  }
+
+  /**
+   * @return {boolean} True if a valid image is loaded.
+   */
+  hasValidImage() {
+    return !!(this.image_ && this.image_.width && this.image_.height);
+  }
+
+  /**
+   * @return {number} Image width.
+   */
+  getWidth() {
+    return this.image_.width;
+  }
+
+  /**
+   * @return {number} Image height.
+   */
+  getHeight() {
+    return this.image_.height;
+  }
+
+  /**
+   * Load an image but do not attach it.
+   *
+   * @param {function(boolean)} callback Callback, parameter is true if the
+   *     image has loaded successfully or a stock icon has been used.
+   */
+  loadDetachedImage(callback) {
+    if (!this.thumbnailUrl_) {
+      callback(true);
+      return;
+    }
+
+    this.cancel();
+    this.canvasUpToDate_ = false;
+    this.image_ = new Image();
+    this.image_.onload = callback.bind(null, true);
+    this.image_.onerror = callback.bind(null, false);
+
+    // TODO(mtomasz): Smarter calculation of the requested size.
+    const modificationTime = this.metadata_ && this.metadata_.filesystem &&
+        this.metadata_.filesystem.modificationTime &&
+        this.metadata_.filesystem.modificationTime.getTime();
+    this.taskId_ = ImageLoaderClient.loadToImage(
+        LoadImageRequest.createRequest({
+          url: this.thumbnailUrl_,
+          maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH,
+          maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT,
+          cache: true,
+          priority: this.priority_,
+          timestamp: modificationTime,
+          orientation: this.transform_
+        }),
+        this.image_, () => {}, () => {
+          this.image_.onerror(new Event('load-error'));
+        });
+  }
+
+  /**
+   * Renders the thumbnail into either canvas or an image element.
    * @private
    */
-  this.loadTarget_ = null;
-
-  if (!opt_metadata) {
-    this.thumbnailUrl_ = entry.toURL();  // Use the URL directly.
-    this.loadTarget_ = ThumbnailLoader.LoadTarget.FILE_ENTRY;
-    return;
-  }
-
-  this.fallbackUrl_ = null;
-  this.thumbnailUrl_ = null;
-  if (opt_metadata.external && opt_metadata.external.customIconUrl) {
-    this.fallbackUrl_ = opt_metadata.external.customIconUrl;
-  }
-  const mimeType = opt_metadata && opt_metadata.contentMimeType;
-
-  for (let i = 0; i < loadTargets.length; i++) {
-    switch (loadTargets[i]) {
-      case ThumbnailLoader.LoadTarget.CONTENT_METADATA:
-        if (opt_metadata.thumbnail && opt_metadata.thumbnail.url) {
-          this.thumbnailUrl_ = opt_metadata.thumbnail.url;
-          this.transform_ =
-              opt_metadata.thumbnail && opt_metadata.thumbnail.transform;
-          this.loadTarget_ = ThumbnailLoader.LoadTarget.CONTENT_METADATA;
-        }
-        break;
-      case ThumbnailLoader.LoadTarget.EXTERNAL_METADATA:
-        if (opt_metadata.external && opt_metadata.external.thumbnailUrl &&
-            (!opt_metadata.external.present ||
-             !FileType.isImage(entry, mimeType))) {
-          this.thumbnailUrl_ = opt_metadata.external.thumbnailUrl;
-          this.croppedThumbnailUrl_ = opt_metadata.external.croppedThumbnailUrl;
-          this.loadTarget_ = ThumbnailLoader.LoadTarget.EXTERNAL_METADATA;
-        }
-        break;
-      case ThumbnailLoader.LoadTarget.FILE_ENTRY:
-        if (FileType.isImage(entry, mimeType) ||
-            FileType.isVideo(entry, mimeType) ||
-            FileType.isRaw(entry, mimeType)) {
-          this.thumbnailUrl_ = entry.toURL();
-          this.transform_ =
-              opt_metadata.media && opt_metadata.media.imageTransform;
-          this.loadTarget_ = ThumbnailLoader.LoadTarget.FILE_ENTRY;
-        }
-        break;
-      default:
-        assertNotReached('Unkonwn load type: ' + loadTargets[i]);
+  renderMedia_() {
+    if (this.loaderType_ !== ThumbnailLoader.LoaderType.CANVAS) {
+      return;
     }
-    if (this.thumbnailUrl_) {
-      break;
+
+    if (!this.canvas_) {
+      this.canvas_ =
+          /** @type {HTMLCanvasElement} */ (document.createElement('canvas'));
+    }
+
+    // Copy the image to a canvas if the canvas is outdated.
+    // At this point, image transformation is not applied because we attach
+    // style attribute to an img element in attachImage() instead.
+    if (!this.canvasUpToDate_) {
+      this.canvas_.width = this.image_.width;
+      this.canvas_.height = this.image_.height;
+      const context = this.canvas_.getContext('2d');
+      context.drawImage(this.image_, 0, 0);
+      this.canvasUpToDate_ = true;
     }
   }
 
-  if (!this.thumbnailUrl_ && this.fallbackUrl_) {
-    // Use fallback as the primary thumbnail.
-    this.thumbnailUrl_ = this.fallbackUrl_;
-    this.fallbackUrl_ = null;
-  }  // else the generic thumbnail based on the media type will be used.
+  /**
+   * Attach the image to a given element.
+   * @param {!Element} box Container element.
+   * @param {ThumbnailLoader.FillMode} fillMode Fill mode.
+   * @param {number} autoFillThreshold Threshold value which is used for fill
+   *     mode auto.
+   * @param {number} boxWidth Container box's width.
+   * @param {number} boxHeight Container box's height.
+   * @private
+   */
+  attachImage_(box, fillMode, autoFillThreshold, boxWidth, boxHeight) {
+    if (!this.hasValidImage()) {
+      box.setAttribute('generic-thumbnail', this.mediaType_);
+      return;
+    }
+
+    this.renderMedia_();
+    const attachableMedia =
+        this.loaderType_ === ThumbnailLoader.LoaderType.CANVAS ? this.canvas_ :
+                                                                 this.image_;
+
+    ThumbnailLoader.centerImage_(
+        box, attachableMedia, fillMode, autoFillThreshold, boxWidth, boxHeight);
+
+    if (attachableMedia.parentNode !== box) {
+      box.textContent = '';
+      box.appendChild(attachableMedia);
+    }
+
+    if (!this.taskId_) {
+      attachableMedia.classList.add('cached');
+    }
+  }
+
+  /**
+   * Gets the loaded image.
+   *
+   * @return {Image|HTMLCanvasElement} Either image or a canvas object.
+   */
+  getImage() {
+    this.renderMedia_();
+    return (this.loaderType_ === ThumbnailLoader.LoaderType.IMAGE) ?
+        this.image_ :
+        this.canvas_;
+  }
+
+  /**
+   * Updates the image style to fit/fill the container.
+   *
+   * Using webkit center packing does not align the image properly, so we need
+   * to wait until the image loads and its dimensions are known, then manually
+   * position it at the center.
+   *
+   * @param {Element} box Containing element.
+   * @param {Image|HTMLCanvasElement} img Element containing an image.
+   * @param {ThumbnailLoader.FillMode} fillMode Fill mode.
+   * @param {number} autoFillThreshold Threshold value which is used for fill
+   *     mode auto.
+   * @param {number} boxWidth Container box's width.
+   * @param {number} boxHeight Container box's height.
+   * @private
+   */
+  static centerImage_(
+      box, img, fillMode, autoFillThreshold, boxWidth, boxHeight) {
+    const imageWidth = img.width;
+    const imageHeight = img.height;
+
+    let fractionX;
+    let fractionY;
+
+    let fill;
+    switch (fillMode) {
+      case ThumbnailLoader.FillMode.FILL:
+      case ThumbnailLoader.FillMode.OVER_FILL:
+        fill = true;
+        break;
+      case ThumbnailLoader.FillMode.FIT:
+        fill = false;
+        break;
+      case ThumbnailLoader.FillMode.AUTO:
+        const imageRatio = imageWidth / imageHeight;
+        let boxRatio = 1.0;
+        if (boxWidth && boxHeight) {
+          boxRatio = boxWidth / boxHeight;
+        }
+        // Cropped area in percents.
+        const ratioFactor = boxRatio / imageRatio;
+        fill = (ratioFactor >= 1.0 - autoFillThreshold) &&
+            (ratioFactor <= 1.0 + autoFillThreshold);
+        break;
+    }
+
+    if (boxWidth && boxHeight) {
+      // When we know the box size we can position the image correctly even
+      // in a non-square box.
+      const fitScaleX = boxWidth / imageWidth;
+      const fitScaleY = boxHeight / imageHeight;
+
+      let scale = fill ? Math.max(fitScaleX, fitScaleY) :
+                         Math.min(fitScaleX, fitScaleY);
+
+      if (fillMode !== ThumbnailLoader.FillMode.OVER_FILL) {
+        scale = Math.min(scale, 1);  // Never overscale.
+      }
+
+      fractionX = imageWidth * scale / boxWidth;
+      fractionY = imageHeight * scale / boxHeight;
+    } else {
+      // We do not know the box size so we assume it is square.
+      // Compute the image position based only on the image dimensions.
+      // First try vertical fit or horizontal fill.
+      fractionX = imageWidth / imageHeight;
+      fractionY = 1;
+      if ((fractionX < 1) === !!fill) {  // Vertical fill or horizontal fit.
+        fractionY = 1 / fractionX;
+        fractionX = 1;
+      }
+    }
+
+    function percent(fraction) {
+      return (fraction * 100).toFixed(2) + '%';
+    }
+
+    img.style.width = percent(fractionX);
+    img.style.height = percent(fractionY);
+    img.style.left = percent((1 - fractionX) / 2);
+    img.style.top = percent((1 - fractionY) / 2);
+  }
 }
 
 /**
@@ -166,355 +535,3 @@
  * @type {number}
  */
 ThumbnailLoader.THUMBNAIL_MAX_HEIGHT = 500;
-
-/**
- * Returns the target of loading.
- * @return {?ThumbnailLoader.LoadTarget}
- */
-ThumbnailLoader.prototype.getLoadTarget = function() {
-  return this.loadTarget_;
-};
-
-/**
- * Loads and attaches an image.
- *
- * @param {!Element} box Container element.
- * @param {ThumbnailLoader.FillMode} fillMode Fill mode.
- * @param {function(Image)} onSuccess Success callback, accepts the image.
- * @param {number} autoFillThreshold Auto fill threshold.
- * @param {number} boxWidth Container box's width.
- * @param {number} boxHeight Container box's height.
- */
-ThumbnailLoader.prototype.load = function(
-    box, fillMode, onSuccess, autoFillThreshold, boxWidth, boxHeight) {
-  if (!this.thumbnailUrl_) {
-    // Relevant CSS rules are in file_types.css.
-    box.setAttribute('generic-thumbnail', this.mediaType_);
-    return;
-  }
-
-  this.cancel();
-  this.canvasUpToDate_ = false;
-  this.image_ = new Image();
-  this.image_.setAttribute('alt', this.entry_.name);
-  this.image_.onload = () => {
-    this.attachImage_(box, fillMode, autoFillThreshold, boxWidth, boxHeight);
-    onSuccess(this.image_);
-  };
-  this.image_.onerror = () => {
-    if (this.fallbackUrl_) {
-      this.thumbnailUrl_ = this.fallbackUrl_;
-      this.fallbackUrl_ = null;
-      this.load(
-          box, fillMode, onSuccess,
-          ThumbnailLoader.AUTO_FILL_THRESHOLD_DEFAULT_VALUE, box.clientWidth,
-          box.clientHeight);
-    } else {
-      box.setAttribute('generic-thumbnail', this.mediaType_);
-    }
-  };
-
-  if (this.image_.src) {
-    console.warn('Thumbnail already loaded: ' + this.thumbnailUrl_);
-    return;
-  }
-
-  // TODO(mtomasz): Smarter calculation of the requested size.
-  const wasAttached = box.ownerDocument.contains(box);
-  const modificationTime = this.metadata_ && this.metadata_.filesystem &&
-      this.metadata_.filesystem.modificationTime &&
-      this.metadata_.filesystem.modificationTime.getTime();
-  this.taskId_ = ImageLoaderClient.loadToImage(
-      LoadImageRequest.createRequest({
-        url: this.thumbnailUrl_,
-        maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH,
-        maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT,
-        cache: true,
-        priority: this.priority_,
-        timestamp: modificationTime,
-        orientation: this.transform_
-      }),
-      this.image_, () => {}, () => {
-        this.image_.onerror(new Event('load-error'));
-      });
-};
-
-/**
- * Loads thumbnail as data url. If data url of thumbnail can be fetched from
- * metadata, this fetches it from it. Otherwise, this tries to load it from
- * thumbnail loader.
- * Compared with ThumbnailLoader.load, this method does not provide a
- * functionality to fit image to a box.
- *
- * @param {ThumbnailLoader.FillMode} fillMode Only FIT and OVER_FILL is
- *     supported. This takes effect only when external thumbnail source is used.
- * @return {!Promise<{data:string, width:number, height:number}>} A promise
- *     which is resolved when data url is fetched.
- *
- * TODO(yawano): Support cancel operation.
- */
-ThumbnailLoader.prototype.loadAsDataUrl = function(fillMode) {
-  assert(
-      fillMode === ThumbnailLoader.FillMode.FIT ||
-      fillMode === ThumbnailLoader.FillMode.OVER_FILL);
-
-  return new Promise((resolve, reject) => {
-    // Load by using ImageLoaderClient.
-    const modificationTime = this.metadata_ && this.metadata_.filesystem &&
-        this.metadata_.filesystem.modificationTime &&
-        this.metadata_.filesystem.modificationTime.getTime();
-    let request = LoadImageRequest.createRequest({
-      url: this.thumbnailUrl_,
-      maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH,
-      maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT,
-      cache: true,
-      priority: this.priority_,
-      timestamp: modificationTime,
-      orientation: this.transform_
-    });
-
-    if (fillMode === ThumbnailLoader.FillMode.OVER_FILL) {
-      // Use cropped thumbnail url if available.
-      request.url = this.croppedThumbnailUrl_ ? this.croppedThumbnailUrl_ :
-                                                this.thumbnailUrl_;
-
-      // Set crop option to image loader. Since image of croppedThumbnailUrl_ is
-      // 360x360 with current implementation, it's no problem to crop it.
-      request.width = 360;
-      request.height = 360;
-      request.crop = true;
-    }
-
-    ImageLoaderClient.getInstance().load(request, result => {
-      if (result.status === LoadImageResponseStatus.SUCCESS) {
-        resolve(result);
-      } else {
-        reject(result);
-      }
-    });
-  });
-};
-
-/**
- * Cancels loading the current image.
- */
-ThumbnailLoader.prototype.cancel = function() {
-  if (this.taskId_) {
-    this.image_.onload = () => {};
-    this.image_.onerror = () => {};
-    ImageLoaderClient.getInstance().cancel(this.taskId_);
-    this.taskId_ = null;
-  }
-};
-
-/**
- * @return {boolean} True if a valid image is loaded.
- */
-ThumbnailLoader.prototype.hasValidImage = function() {
-  return !!(this.image_ && this.image_.width && this.image_.height);
-};
-
-/**
- * @return {number} Image width.
- */
-ThumbnailLoader.prototype.getWidth = function() {
-  return this.image_.width;
-};
-
-/**
- * @return {number} Image height.
- */
-ThumbnailLoader.prototype.getHeight = function() {
-  return this.image_.height;
-};
-
-/**
- * Load an image but do not attach it.
- *
- * @param {function(boolean)} callback Callback, parameter is true if the image
- *     has loaded successfully or a stock icon has been used.
- */
-ThumbnailLoader.prototype.loadDetachedImage = function(callback) {
-  if (!this.thumbnailUrl_) {
-    callback(true);
-    return;
-  }
-
-  this.cancel();
-  this.canvasUpToDate_ = false;
-  this.image_ = new Image();
-  this.image_.onload = callback.bind(null, true);
-  this.image_.onerror = callback.bind(null, false);
-
-  // TODO(mtomasz): Smarter calculation of the requested size.
-  const modificationTime = this.metadata_ && this.metadata_.filesystem &&
-      this.metadata_.filesystem.modificationTime &&
-      this.metadata_.filesystem.modificationTime.getTime();
-  this.taskId_ = ImageLoaderClient.loadToImage(
-      LoadImageRequest.createRequest({
-        url: this.thumbnailUrl_,
-        maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH,
-        maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT,
-        cache: true,
-        priority: this.priority_,
-        timestamp: modificationTime,
-        orientation: this.transform_
-      }),
-      this.image_, () => {}, () => {
-        this.image_.onerror(new Event('load-error'));
-      });
-};
-
-/**
- * Renders the thumbnail into either canvas or an image element.
- * @private
- */
-ThumbnailLoader.prototype.renderMedia_ = function() {
-  if (this.loaderType_ !== ThumbnailLoader.LoaderType.CANVAS) {
-    return;
-  }
-
-  if (!this.canvas_) {
-    this.canvas_ = document.createElement('canvas');
-  }
-
-  // Copy the image to a canvas if the canvas is outdated.
-  // At this point, image transformation is not applied because we attach style
-  // attribute to an img element in attachImage() instead.
-  if (!this.canvasUpToDate_) {
-    this.canvas_.width = this.image_.width;
-    this.canvas_.height = this.image_.height;
-    const context = this.canvas_.getContext('2d');
-    context.drawImage(this.image_, 0, 0);
-    this.canvasUpToDate_ = true;
-  }
-};
-
-/**
- * Attach the image to a given element.
- * @param {!Element} box Container element.
- * @param {ThumbnailLoader.FillMode} fillMode Fill mode.
- * @param {number} autoFillThreshold Threshold value which is used for fill
- *     mode auto.
- * @param {number} boxWidth Container box's width.
- * @param {number} boxHeight Container box's height.
- * @private
- */
-ThumbnailLoader.prototype.attachImage_ = function(
-    box, fillMode, autoFillThreshold, boxWidth, boxHeight) {
-  if (!this.hasValidImage()) {
-    box.setAttribute('generic-thumbnail', this.mediaType_);
-    return;
-  }
-
-  this.renderMedia_();
-  const attachableMedia =
-      this.loaderType_ === ThumbnailLoader.LoaderType.CANVAS ? this.canvas_ :
-                                                               this.image_;
-
-  ThumbnailLoader.centerImage_(
-      box, attachableMedia, fillMode, autoFillThreshold, boxWidth, boxHeight);
-
-  if (attachableMedia.parentNode !== box) {
-    box.textContent = '';
-    box.appendChild(attachableMedia);
-  }
-
-  if (!this.taskId_) {
-    attachableMedia.classList.add('cached');
-  }
-};
-
-/**
- * Gets the loaded image.
- *
- * @return {Image|HTMLCanvasElement} Either image or a canvas object.
- */
-ThumbnailLoader.prototype.getImage = function() {
-  this.renderMedia_();
-  return (this.loaderType_ === ThumbnailLoader.LoaderType.IMAGE) ? this.image_ :
-                                                                   this.canvas_;
-};
-
-/**
- * Update the image style to fit/fill the container.
- *
- * Using webkit center packing does not align the image properly, so we need
- * to wait until the image loads and its dimensions are known, then manually
- * position it at the center.
- *
- * @param {Element} box Containing element.
- * @param {Image|HTMLCanvasElement} img Element containing an image.
- * @param {ThumbnailLoader.FillMode} fillMode Fill mode.
- * @param {number} autoFillThreshold Threshold value which is used for fill mode
- *     auto.
- * @param {number} boxWidth Container box's width.
- * @param {number} boxHeight Container box's height.
- * @private
- */
-ThumbnailLoader.centerImage_ =
-    (box, img, fillMode, autoFillThreshold, boxWidth, boxHeight) => {
-      const imageWidth = img.width;
-      const imageHeight = img.height;
-
-      let fractionX;
-      let fractionY;
-
-      let fill;
-      switch (fillMode) {
-        case ThumbnailLoader.FillMode.FILL:
-        case ThumbnailLoader.FillMode.OVER_FILL:
-          fill = true;
-          break;
-        case ThumbnailLoader.FillMode.FIT:
-          fill = false;
-          break;
-        case ThumbnailLoader.FillMode.AUTO:
-          const imageRatio = imageWidth / imageHeight;
-          let boxRatio = 1.0;
-          if (boxWidth && boxHeight) {
-            boxRatio = boxWidth / boxHeight;
-          }
-          // Cropped area in percents.
-          const ratioFactor = boxRatio / imageRatio;
-          fill = (ratioFactor >= 1.0 - autoFillThreshold) &&
-              (ratioFactor <= 1.0 + autoFillThreshold);
-          break;
-      }
-
-      if (boxWidth && boxHeight) {
-        // When we know the box size we can position the image correctly even
-        // in a non-square box.
-        const fitScaleX = boxWidth / imageWidth;
-        const fitScaleY = boxHeight / imageHeight;
-
-        let scale = fill ? Math.max(fitScaleX, fitScaleY) :
-                           Math.min(fitScaleX, fitScaleY);
-
-        if (fillMode !== ThumbnailLoader.FillMode.OVER_FILL) {
-          scale = Math.min(scale, 1);  // Never overscale.
-        }
-
-        fractionX = imageWidth * scale / boxWidth;
-        fractionY = imageHeight * scale / boxHeight;
-      } else {
-        // We do not know the box size so we assume it is square.
-        // Compute the image position based only on the image dimensions.
-        // First try vertical fit or horizontal fill.
-        fractionX = imageWidth / imageHeight;
-        fractionY = 1;
-        if ((fractionX < 1) === !!fill) {  // Vertical fill or horizontal fit.
-          fractionY = 1 / fractionX;
-          fractionX = 1;
-        }
-      }
-
-      function percent(fraction) {
-        return (fraction * 100).toFixed(2) + '%';
-      }
-
-      img.style.width = percent(fractionX);
-      img.style.height = percent(fractionY);
-      img.style.left = percent((1 - fractionX) / 2);
-      img.style.top = percent((1 - fractionY) / 2);
-    };
diff --git a/ui/file_manager/file_manager/foreground/js/toolbar_controller.js b/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
index e514651..bc1ab7b 100644
--- a/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
@@ -17,10 +17,11 @@
    *     the toolbar.
    * @param {!FileSelectionHandler} selectionHandler
    * @param {!DirectoryModel} directoryModel
+   * @param {!VolumeManager} volumeManager
    */
   constructor(
       toolbar, navigationList, listContainer, locationLine, selectionHandler,
-      directoryModel) {
+      directoryModel, volumeManager) {
     /**
      * @private {!HTMLElement}
      * @const
@@ -100,6 +101,12 @@
      */
     this.directoryModel_ = directoryModel;
 
+    /**
+     * @private {!VolumeManager}
+     * @const
+     */
+    this.volumeManager_ = volumeManager;
+
     this.selectionHandler_.addEventListener(
         FileSelectionHandler.EventType.CHANGE,
         this.onSelectionChanged_.bind(this));
@@ -114,7 +121,7 @@
         'relayout', this.onNavigationListRelayout_.bind(this));
 
     this.directoryModel_.addEventListener(
-        'directory-change', this.updateCurrentDirectoryButtons_.bind(this));
+        'directory-changed', this.updateCurrentDirectoryButtons_.bind(this));
 
     // Watch visibility of toolbar buttons to update the width of location line.
     const observer =
@@ -172,10 +179,8 @@
     this.deleteButton_.hidden =
         (selection.totalCount === 0 || this.directoryModel_.isReadOnly() ||
          selection.hasReadOnlyEntry() ||
-         (util.isMyFilesVolumeEnabled() &&
-          this.directoryModel_.getCurrentRootType() ==
-              VolumeManagerCommon.RootType.DOWNLOADS &&
-          selection.entries.some(entry => entry.fullPath === '/Downloads')));
+         selection.entries.some(
+             entry => util.isNonModifiable(this.volumeManager_, entry)));
 
     // Set .selecting class to containing element to change the view
     // accordingly.
diff --git a/ui/file_manager/integration_tests/file_manager/my_files.js b/ui/file_manager/integration_tests/file_manager/my_files.js
index 704cb37..e984cbe 100644
--- a/ui/file_manager/integration_tests/file_manager/my_files.js
+++ b/ui/file_manager/integration_tests/file_manager/my_files.js
@@ -1,6 +1,28 @@
 // Copyright 2018 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
+/**
+ * Select My files in directory tree and wait for load.
+ *
+ * @param {string} appId ID of the app window.
+ */
+async function selectMyFiles(appId) {
+  // Select My Files folder.
+  const myFilesQuery = '#directory-tree [entry-label="My files"]';
+  const isDriveQuery = false;
+  chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
+      'selectInDirectoryTree', appId, [myFilesQuery, isDriveQuery]));
+
+  // Wait for file list to display Downloads and Crostini.
+  const downloadsRow = ['Downloads', '--', 'Folder'];
+  const playFilesRow = ['Play files', '--', 'Folder'];
+  const crostiniRow = ['Linux files', '--', 'Folder'];
+  await remoteCall.waitForFiles(
+      appId, [downloadsRow, playFilesRow, crostiniRow],
+      {ignoreFileSize: true, ignoreLastModifiedTime: true});
+}
+
 /**
  * Tests if MyFiles is displayed when flag is true.
  */
@@ -72,11 +94,8 @@
   chrome.test.assertEq(1, buttonElements.length);
   chrome.test.assertFalse(buttonElements[0].hidden);
 
-  // Select My Files folder.
-  const myFilesQuery = '#directory-tree [entry-label="My files"]';
-  const isDriveQuery = false;
-  chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
-      'selectInDirectoryTree', appId, [myFilesQuery, isDriveQuery]));
+  // Select My files in directory tree.
+  await selectMyFiles(appId);
 
   // Get the search button element.
   buttonElements = await remoteCall.callRemoteTestUtil(
@@ -122,19 +141,8 @@
   const appId =
       await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
 
-  // Select My Files folder.
-  const myFilesQuery = '#directory-tree [entry-label="My files"]';
-  const isDriveQuery = false;
-  chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
-      'selectInDirectoryTree', appId, [myFilesQuery, isDriveQuery]));
-
-  // Wait for file list to display Downloads and Crostini.
-  const downloadsRow = ['Downloads', '--', 'Folder'];
-  const playFilesRow = ['Play files', '--', 'Folder'];
-  const crostiniRow = ['Linux files', '--', 'Folder'];
-  await remoteCall.waitForFiles(
-      appId, [downloadsRow, playFilesRow, crostiniRow],
-      {ignoreFileSize: true, ignoreLastModifiedTime: true});
+  // Select My files in directory tree.
+  await selectMyFiles(appId);
 
   // Double click on Download on file list.
   const downloadsFileListQuery = '#file-list [file-name="Downloads"]';
@@ -228,23 +236,8 @@
   const appId =
       await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.photos], []);
 
-  // Select "My files" folder via directory tree.
-  const myFilesQuery = '#directory-tree [entry-label="My files"]';
-  const isDriveQuery = false;
-  chrome.test.assertTrue(
-      !!await remoteCall.callRemoteTestUtil(
-          'selectInDirectoryTree', appId, [myFilesQuery, isDriveQuery]),
-      'selectInDirectoryTree failed');
-
-  // Wait for Downloads to load.
-  const expectedRows = [
-    ['Downloads', '--', 'Folder'],
-    ['Play files', '--', 'Folder'],
-    ['Linux files', '--', 'Folder'],
-  ];
-  await remoteCall.waitForFiles(
-      appId, expectedRows,
-      {ignoreFileSize: true, ignoreLastModifiedTime: true});
+  // Select My files in directory tree.
+  await selectMyFiles(appId);
 
   // Select Downloads via file list.
   const downloads = ['Downloads'];
@@ -386,3 +379,43 @@
   chrome.test.assertTrue(
       !!await remoteCall.waitForElementLost(appId, playFilesTreeItem));
 };
+
+/**
+ * Tests that toolbar delete is not shown for Downloads, or Linux files.
+ */
+testcase.myFilesToolbarDelete = async () => {
+  // Open Files app on local Downloads.
+  const appId =
+      await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
+
+  // Select My files in directory tree.
+  await selectMyFiles(appId);
+
+  // Select Downloads folder in list.
+  chrome.test.assertTrue(
+      await remoteCall.callRemoteTestUtil('selectFile', appId, ['Downloads']));
+
+  // Test that the delete button isn't visible.
+  const hiddenDeleteButton = '#delete-button[hidden]';
+  await remoteCall.waitForElement(appId, hiddenDeleteButton);
+
+  // Select fake entry Linux files folder in list.
+  chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
+      'selectFile', appId, ['Linux files']));
+
+  // Test that the delete button isn't visible.
+  await remoteCall.waitForElement(appId, hiddenDeleteButton);
+
+  // Mount crostini and test real root entry.
+  await mountCrostini(appId);
+
+  // Select My files in directory tree.
+  await selectMyFiles(appId);
+
+  // Select real Linux files folder in list.
+  chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
+      'selectFile', appId, ['Linux files']));
+
+  // Test that the delete button isn't visible.
+  await remoteCall.waitForElement(appId, hiddenDeleteButton);
+};
diff --git a/ui/native_theme/native_theme_mac.mm b/ui/native_theme/native_theme_mac.mm
index 40e5cb5..7c3ffa8c 100644
--- a/ui/native_theme/native_theme_mac.mm
+++ b/ui/native_theme/native_theme_mac.mm
@@ -19,7 +19,6 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/native_theme/common_theme.h"
-#include "ui/native_theme/native_theme_aura.h"
 
 namespace {
 
@@ -153,8 +152,6 @@
 
 // static
 NativeTheme* NativeTheme::GetInstanceForWeb() {
-  if (features::IsFormControlsRefreshEnabled())
-    return NativeThemeAura::web_instance();
   return NativeThemeMac::instance();
 }
 
diff --git a/ui/views/DEPS b/ui/views/DEPS
index 07b7bc43..4959057 100644
--- a/ui/views/DEPS
+++ b/ui/views/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+cc/paint",
   "+components/crash/core/common/crash_key.h",
+  "+components/remote_cocoa",
   "+components/vector_icons",
   "+mojo/public/cpp/bindings",
   "+services/ws/public/mojom",
@@ -21,7 +22,6 @@
   "+ui/resources/grit/ui_resources.h",
   "+ui/strings/grit/ui_strings.h",
   "+ui/touch_selection",
-  "+ui/views_bridge_mac",
   "+ui/wm/core",
   "+ui/wm/public",
 
diff --git a/ui/views/cocoa/DEPS b/ui/views/cocoa/DEPS
index e6cea14f..d4cfbf0 100644
--- a/ui/views/cocoa/DEPS
+++ b/ui/views/cocoa/DEPS
@@ -1,5 +1,4 @@
 include_rules = [
-  "+components/remote_cocoa/common",
+  "+components/remote_cocoa",
   "+ui/accelerated_widget_mac",
-  "+ui/views_bridge_mac",
 ]
diff --git a/ui/views/cocoa/bridged_native_widget_host_impl.h b/ui/views/cocoa/bridged_native_widget_host_impl.h
index 32bd7be..eddecb59 100644
--- a/ui/views/cocoa/bridged_native_widget_host_impl.h
+++ b/ui/views/cocoa/bridged_native_widget_host_impl.h
@@ -10,6 +10,7 @@
 
 #include "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
+#include "components/remote_cocoa/app_shim/bridged_native_widget_host_helper.h"
 #include "components/remote_cocoa/common/bridged_native_widget.mojom.h"
 #include "components/remote_cocoa/common/bridged_native_widget_host.mojom.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
@@ -25,7 +26,6 @@
 #include "ui/views/views_export.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/dialog_observer.h"
-#include "ui/views_bridge_mac/bridged_native_widget_host_helper.h"
 
 @class NativeWidgetMacNSWindow;
 @class NSAccessibilityRemoteUIElement;
diff --git a/ui/views/cocoa/bridged_native_widget_host_impl.mm b/ui/views/cocoa/bridged_native_widget_host_impl.mm
index 865faa3..b51ced5 100644
--- a/ui/views/cocoa/bridged_native_widget_host_impl.mm
+++ b/ui/views/cocoa/bridged_native_widget_host_impl.mm
@@ -8,6 +8,9 @@
 
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
+#include "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
+#include "components/remote_cocoa/app_shim/mouse_capture.h"
+#include "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
 #include "mojo/public/cpp/bindings/strong_associated_binding.h"
 #include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
 #include "ui/base/cocoa/animation_utils.h"
@@ -33,9 +36,6 @@
 #include "ui/views/window/dialog_client_view.h"
 #include "ui/views/window/dialog_delegate.h"
 #include "ui/views/word_lookup_client.h"
-#include "ui/views_bridge_mac/bridged_native_widget_impl.h"
-#include "ui/views_bridge_mac/cocoa_mouse_capture.h"
-#include "ui/views_bridge_mac/native_widget_mac_nswindow.h"
 
 using views_bridge_mac::mojom::BridgedNativeWidgetInitParams;
 using views_bridge_mac::mojom::WindowVisibilityState;
diff --git a/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm b/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm
index 0dce22e..20ee2294 100644
--- a/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm
+++ b/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 
 #import <Cocoa/Cocoa.h>
 
diff --git a/ui/views/cocoa/bridged_native_widget_unittest.mm b/ui/views/cocoa/bridged_native_widget_unittest.mm
index 9ca1972..8a871ad 100644
--- a/ui/views/cocoa/bridged_native_widget_unittest.mm
+++ b/ui/views/cocoa/bridged_native_widget_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 
 #import <Cocoa/Cocoa.h>
 #include <objc/runtime.h>
@@ -19,6 +19,9 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_task_environment.h"
+#import "components/remote_cocoa/app_shim/bridged_content_view.h"
+#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
+#import "components/remote_cocoa/app_shim/views_nswindow_delegate.h"
 #import "testing/gtest_mac.h"
 #import "ui/base/cocoa/window_size_constants.h"
 #include "ui/base/ime/input_method.h"
@@ -37,9 +40,6 @@
 #include "ui/views/widget/root_view.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
-#import "ui/views_bridge_mac/bridged_content_view.h"
-#import "ui/views_bridge_mac/native_widget_mac_nswindow.h"
-#import "ui/views_bridge_mac/views_nswindow_delegate.h"
 
 using base::ASCIIToUTF16;
 using base::SysNSStringToUTF8;
diff --git a/ui/views/cocoa/cocoa_mouse_capture_unittest.mm b/ui/views/cocoa/cocoa_mouse_capture_unittest.mm
index 68dad9f..c1a75f27 100644
--- a/ui/views/cocoa/cocoa_mouse_capture_unittest.mm
+++ b/ui/views/cocoa/cocoa_mouse_capture_unittest.mm
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ui/views_bridge_mac/cocoa_mouse_capture.h"
+#import "components/remote_cocoa/app_shim/mouse_capture.h"
 
 #import <Cocoa/Cocoa.h>
 
 #import "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
+#import "components/remote_cocoa/app_shim/mouse_capture_delegate.h"
 #import "ui/base/test/cocoa_helper.h"
 #import "ui/events/test/cocoa_test_event_utils.h"
-#import "ui/views_bridge_mac/cocoa_mouse_capture_delegate.h"
 
 // Simple test view that counts calls to -[NSView mouseDown:].
 @interface CocoaMouseCaptureTestView : NSView {
diff --git a/ui/views/cocoa/drag_drop_client_mac.h b/ui/views/cocoa/drag_drop_client_mac.h
index d952728..326650c 100644
--- a/ui/views/cocoa/drag_drop_client_mac.h
+++ b/ui/views/cocoa/drag_drop_client_mac.h
@@ -11,11 +11,11 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "components/remote_cocoa/app_shim/drag_drop_client.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/views/views_export.h"
 #include "ui/views/widget/drop_helper.h"
-#include "ui/views_bridge_mac/drag_drop_client.h"
 
 namespace gfx {
 class Point;
diff --git a/ui/views/cocoa/drag_drop_client_mac.mm b/ui/views/cocoa/drag_drop_client_mac.mm
index da67163..ac1c1b5 100644
--- a/ui/views/cocoa/drag_drop_client_mac.mm
+++ b/ui/views/cocoa/drag_drop_client_mac.mm
@@ -7,12 +7,12 @@
 #include "base/mac/mac_util.h"
 #include "base/run_loop.h"
 #include "base/strings/sys_string_conversions.h"
+#import "components/remote_cocoa/app_shim/bridged_content_view.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #import "ui/base/dragdrop/os_exchange_data_provider_mac.h"
 #include "ui/gfx/image/image_skia_util_mac.h"
 #include "ui/views/drag_utils.h"
 #include "ui/views/widget/native_widget_mac.h"
-#import "ui/views_bridge_mac/bridged_content_view.h"
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 namespace views {
 
diff --git a/ui/views/cocoa/drag_drop_client_mac_unittest.mm b/ui/views/cocoa/drag_drop_client_mac_unittest.mm
index 94f0eea..e19072b0 100644
--- a/ui/views/cocoa/drag_drop_client_mac_unittest.mm
+++ b/ui/views/cocoa/drag_drop_client_mac_unittest.mm
@@ -12,6 +12,7 @@
 #include "base/mac/sdk_forward_declarations.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #import "ui/base/clipboard/clipboard_util_mac.h"
 #include "ui/gfx/image/image_unittest_util.h"
 #import "ui/views/cocoa/bridged_native_widget_host_impl.h"
@@ -19,7 +20,6 @@
 #include "ui/views/view.h"
 #include "ui/views/widget/native_widget_mac.h"
 #include "ui/views/widget/widget.h"
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 using base::ASCIIToUTF16;
 
diff --git a/ui/views/cocoa/text_input_host.mm b/ui/views/cocoa/text_input_host.mm
index dbe86f72..c1bbbe0ce 100644
--- a/ui/views/cocoa/text_input_host.mm
+++ b/ui/views/cocoa/text_input_host.mm
@@ -4,11 +4,11 @@
 
 #include "ui/views/cocoa/text_input_host.h"
 
+#include "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
 #include "ui/base/ime/text_input_client.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/views/cocoa/bridged_native_widget_host_impl.h"
-#include "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 namespace {
 
diff --git a/ui/views/cocoa/tooltip_manager_mac.mm b/ui/views/cocoa/tooltip_manager_mac.mm
index cbf12b14..de37a7a8 100644
--- a/ui/views/cocoa/tooltip_manager_mac.mm
+++ b/ui/views/cocoa/tooltip_manager_mac.mm
@@ -5,11 +5,11 @@
 #include "ui/views/cocoa/tooltip_manager_mac.h"
 
 #include "base/no_destructor.h"
+#import "components/remote_cocoa/app_shim/bridged_content_view.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #include "ui/base/cocoa/cocoa_base_utils.h"
 #include "ui/gfx/font_list.h"
 #import "ui/gfx/mac/coordinate_conversion.h"
-#import "ui/views_bridge_mac/bridged_content_view.h"
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 namespace {
 
diff --git a/ui/views/controls/scrollbar/cocoa_scroll_bar.h b/ui/views/controls/scrollbar/cocoa_scroll_bar.h
index b4ae90e..a839959 100644
--- a/ui/views/controls/scrollbar/cocoa_scroll_bar.h
+++ b/ui/views/controls/scrollbar/cocoa_scroll_bar.h
@@ -8,11 +8,11 @@
 #import "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
 #include "base/timer/timer.h"
+#import "components/remote_cocoa/app_shim/views_scrollbar_bridge.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/gfx/animation/slide_animation.h"
 #include "ui/views/controls/scrollbar/scroll_bar.h"
 #include "ui/views/views_export.h"
-#import "ui/views_bridge_mac/views_scrollbar_bridge.h"
 
 namespace views {
 
diff --git a/ui/views/test/platform_test_helper_cocoa.mm b/ui/views/test/platform_test_helper_cocoa.mm
index 98d5872..4a0831b 100644
--- a/ui/views/test/platform_test_helper_cocoa.mm
+++ b/ui/views/test/platform_test_helper_cocoa.mm
@@ -9,9 +9,9 @@
 #import "base/mac/scoped_nsobject.h"
 #import "base/mac/scoped_objc_class_swizzler.h"
 #include "base/macros.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #include "ui/views/widget/native_widget_mac.h"
 #include "ui/views/widget/widget.h"
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 namespace views {
 
diff --git a/ui/views/test/widget_test_mac.mm b/ui/views/test/widget_test_mac.mm
index e936011..6414d67 100644
--- a/ui/views/test/widget_test_mac.mm
+++ b/ui/views/test/widget_test_mac.mm
@@ -9,10 +9,10 @@
 #import "base/mac/scoped_nsobject.h"
 #import "base/mac/scoped_objc_class_swizzler.h"
 #include "base/macros.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 #include "ui/views/cocoa/bridged_native_widget_host_impl.h"
 #include "ui/views/widget/native_widget_mac.h"
 #include "ui/views/widget/root_view.h"
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
 namespace views {
 namespace test {
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm
index 4b1b2c4..ccb81cf 100644
--- a/ui/views/widget/native_widget_mac.mm
+++ b/ui/views/widget/native_widget_mac.mm
@@ -14,6 +14,10 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/crash/core/common/crash_key.h"
+#import "components/remote_cocoa/app_shim/bridged_content_view.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
+#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
+#import "components/remote_cocoa/app_shim/views_nswindow_delegate.h"
 #import "ui/base/cocoa/constrained_window/constrained_window_animation.h"
 #import "ui/base/cocoa/window_size_constants.h"
 #include "ui/display/display.h"
@@ -29,10 +33,6 @@
 #include "ui/views/widget/drop_helper.h"
 #include "ui/views/widget/widget_delegate.h"
 #include "ui/views/window/native_frame_view.h"
-#import "ui/views_bridge_mac/bridged_content_view.h"
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
-#import "ui/views_bridge_mac/native_widget_mac_nswindow.h"
-#import "ui/views_bridge_mac/views_nswindow_delegate.h"
 
 using views_bridge_mac::mojom::WindowVisibilityState;
 
diff --git a/ui/views/widget/native_widget_mac_unittest.mm b/ui/views/widget/native_widget_mac_unittest.mm
index 340fe7a..ce14d4c 100644
--- a/ui/views/widget/native_widget_mac_unittest.mm
+++ b/ui/views/widget/native_widget_mac_unittest.mm
@@ -19,6 +19,9 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/thread_task_runner_handle.h"
+#import "components/remote_cocoa/app_shim/bridged_content_view.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
+#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
 #import "testing/gtest_mac.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -42,9 +45,6 @@
 #include "ui/views/widget/native_widget_private.h"
 #include "ui/views/window/dialog_client_view.h"
 #include "ui/views/window/dialog_delegate.h"
-#import "ui/views_bridge_mac/bridged_content_view.h"
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
-#import "ui/views_bridge_mac/native_widget_mac_nswindow.h"
 
 // Donates an implementation of -[NSAnimation stopAnimation] which calls the
 // original implementation, then quits a nested run loop.
diff --git a/ui/views/widget/widget_utils_mac.mm b/ui/views/widget/widget_utils_mac.mm
index 6dbf0d1..8d843b83 100644
--- a/ui/views/widget/widget_utils_mac.mm
+++ b/ui/views/widget/widget_utils_mac.mm
@@ -4,7 +4,7 @@
 
 #include "ui/views/widget/widget_utils_mac.h"
 
-#import "ui/views_bridge_mac/bridged_native_widget_impl.h"
+#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h"
 
 namespace views {
 
diff --git a/ui/views_bridge_mac/browser_native_widget_window_mac.h b/ui/views_bridge_mac/browser_native_widget_window_mac.h
deleted file mode 100644
index 6b7004a9..0000000
--- a/ui/views_bridge_mac/browser_native_widget_window_mac.h
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_BRIDGE_MAC_BROWSER_NATIVE_WIDGET_WINDOW_MAC_H_
-#define UI_VIEWS_BRIDGE_MAC_BROWSER_NATIVE_WIDGET_WINDOW_MAC_H_
-
-#import "ui/views_bridge_mac/native_widget_mac_nswindow.h"
-
-@interface BrowserNativeWidgetWindow : NativeWidgetMacNSWindow
-@end
-
-#endif  // UI_VIEWS_BRIDGE_MAC_BROWSER_NATIVE_WIDGET_WINDOW_MAC_H_
diff --git a/ui/views_bridge_mac/views_bridge_mac_export.h b/ui/views_bridge_mac/views_bridge_mac_export.h
deleted file mode 100644
index 6a44a0d..0000000
--- a/ui/views_bridge_mac/views_bridge_mac_export.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_BRIDGE_MAC_VIEWS_BRIDGE_MAC_EXPORT_H_
-#define UI_VIEWS_BRIDGE_MAC_VIEWS_BRIDGE_MAC_EXPORT_H_
-
-// Defines VIEWS_BRIDGE_MAC_EXPORT so that functionality implemented by the
-// RemoteMacViews module can be exported to consumers.
-
-#if defined(COMPONENT_BUILD)
-#if defined(WIN32)
-
-#if defined(VIEWS_BRIDGE_MAC_IMPLEMENTATION)
-#define VIEWS_BRIDGE_MAC_EXPORT __declspec(dllexport)
-#else
-#define VIEWS_BRIDGE_MAC_EXPORT __declspec(dllimport)
-#endif  // defined(VIEWS_BRIDGE_MAC_IMPLEMENTATION)
-
-#else  // defined(WIN32)
-#if defined(VIEWS_BRIDGE_MAC_IMPLEMENTATION)
-#define VIEWS_BRIDGE_MAC_EXPORT __attribute__((visibility("default")))
-#else
-#define VIEWS_BRIDGE_MAC_EXPORT
-#endif
-#endif
-
-#else  // defined(COMPONENT_BUILD)
-#define VIEWS_BRIDGE_MAC_EXPORT
-#endif
-
-#endif  // UI_VIEWS_BRIDGE_MAC_VIEWS_BRIDGE_MAC_EXPORT_H_