diff --git a/DEPS b/DEPS
index 9e6ec67..29ad74a 100644
--- a/DEPS
+++ b/DEPS
@@ -79,11 +79,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': 'bd6525304d448f9c1ce04bf6b10bc9306802823e',
+  'skia_revision': '8bac928009b8689f082e48ad5bc62f65297b894b',
   # 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': '598a80edc4157838591a393a728a2f4f79a3b66f',
+  'v8_revision': 'c95cc11a077fda49803a693126a8459f3eb9a7a1',
   # 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.
@@ -91,7 +91,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'd2cb7cec4f3e303880ab5d55f6a58165cd23a631',
+  'angle_revision': '6dd4a92aa6df4f422f470a20d44b7cdce6c4d095',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -103,7 +103,7 @@
   # 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': '9600a771999de20fb22130cdb97088591508f89f',
+  'pdfium_revision': 'f8af565a78ee1910b8c98a5bdfb9ab6b88442317',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -241,7 +241,7 @@
   },
 
   'src/ios/third_party/motion_animator_objc/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-motion/motion-animator-objc.git' + '@' + 'ff39ecc69fdee46d388cc9f882201d54c3d5039c',
+      'url': Var('chromium_git') + '/external/github.com/material-motion/motion-animator-objc.git' + '@' + '5df831026445004b2fc0f6a42f8b8f33af46512b',
       'condition': 'checkout_ios',
   },
 
@@ -331,12 +331,12 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + '7d15090bd03615b00e6ad070da5e85c0aa76aa13',
+      'url': Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + '57bbe676efe5f394395c123d713968d104e878bf',
       'condition': 'checkout_linux',
   },
 
   'src/third_party/custom_tabs_client/src': {
-      'url': Var('chromium_git') + '/custom-tabs-client.git' + '@' + '7884a658507dc99bcb54b328bc29d76e64c3b684',
+      'url': Var('chromium_git') + '/custom-tabs-client.git' + '@' + '54788baf5bfbe5bb42977dc52c2f608392e60f72',
       'condition': 'checkout_android',
   },
 
@@ -365,7 +365,7 @@
   },
 
   'src/third_party/ffmpeg':
-    Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + 'ef99a5d2520f934fa6c74ef7219aaa47e8717914',
+    Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + '4468d4967f5dd6a733860af355ef61095b5cd5b1',
 
   'src/third_party/flac':
     Var('chromium_git') + '/chromium/deps/flac.git' + '@' + '7d0f5b3a173ffe98db08057d1f52b7787569e0a6',
@@ -397,7 +397,7 @@
   },
 
   'src/third_party/googletest/src':
-    Var('chromium_git') + '/external/github.com/google/googletest.git' + '@' + 'fe1144246e7ecae688608f7ed0a7ec1ee3e2d2af',
+    Var('chromium_git') + '/external/github.com/google/googletest.git' + '@' + '703b4a85a21e394252560a89cc856b384b48c286',
 
   # GNU binutils assembler for x86-32.
   'src/third_party/gnu_binutils': {
@@ -489,7 +489,7 @@
   },
 
   'src/third_party/libvpx/source/libvpx':
-    Var('chromium_git') + '/webm/libvpx.git' + '@' +  'edc9a4687699b372a0c27856020b42434ddc3014',
+    Var('chromium_git') + '/webm/libvpx.git' + '@' +  'c6fcb9bb94ea02324d9d842b71ac3d0ab0329c10',
 
   'src/third_party/libwebm/source':
     Var('chromium_git') + '/webm/libwebm.git' + '@' + 'b03c65468b06d097f27235d93d76bfc45f490ede',
@@ -657,7 +657,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '3c1cb0203b6cfc10389e85a350b2ea6ca29d01ce',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '8d9dcb1c89354356df4e5c7623a907c05a3cc35a', # commit position 21742
+    Var('webrtc_git') + '/src.git' + '@' + '12eb85881c865a67be07d04856383fea22890d6a', # commit position 21742
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -680,7 +680,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@5dc7db08f8081b8e3b96a060f3279843295ad0f2',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@595b668667262c291190744691b5974062336795',
     'condition': 'checkout_src_internal',
   },
 
@@ -1411,8 +1411,6 @@
 recursedeps = [
   # buildtools provides clang_format, libc++, and libc++abi
   'src/buildtools',
-  # android_tools manages the NDK.
-  'src/third_party/android_tools',
   # ANGLE manages DEPS that it also owns the build files for, such as dEQP.
   ("src/third_party/angle", "DEPS.chromium"),
   # src-internal has its own DEPS file to pull additional internal repos
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
index 3477a0d7..63d1c5f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
@@ -982,6 +982,7 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView"})
+    @DisabledTest(message = "crbug/819085")
     public void testSafeBrowsingClickLearnMoreLink() throws Throwable {
         loadInterstitialAndClickLink(PHISHING_HTML_PATH, "learn-more-link",
                 appendLocale("https://support.google.com/chrome/?p=cpn_safe_browsing_wv"));
@@ -990,6 +991,7 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView"})
+    @DisabledTest(message = "crbug/819085")
     public void testSafeBrowsingClickReportErrorLink() throws Throwable {
         // Only phishing interstitials have the report-error-link
         loadInterstitialAndClickLink(PHISHING_HTML_PATH, "report-error-link",
diff --git a/ash/app_launch_unittest.cc b/ash/app_launch_unittest.cc
index 97356da..f1907b0 100644
--- a/ash/app_launch_unittest.cc
+++ b/ash/app_launch_unittest.cc
@@ -14,9 +14,9 @@
 
 namespace ash {
 
-void RunCallback(bool* success, const base::Closure& callback, bool result) {
+void RunCallback(bool* success, base::RepeatingClosure callback, bool result) {
   *success = result;
-  callback.Run();
+  std::move(callback).Run();
 }
 
 class AppLaunchTest : public service_manager::test::ServiceTest {
@@ -46,7 +46,7 @@
   bool success = false;
   test_interface->EnsureClientHasDrawnWindow(
       quick_launch::mojom::kServiceName,
-      base::Bind(&RunCallback, &success, run_loop.QuitClosure()));
+      base::BindOnce(&RunCallback, &success, run_loop.QuitClosure()));
   run_loop.Run();
   EXPECT_TRUE(success);
 }
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 6b8e4e22..70244fc7 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -157,16 +157,17 @@
 void AppListControllerImpl::SetModelData(
     std::vector<AppListItemMetadataPtr> apps,
     bool is_search_engine_google) {
-  // Populate new models.
+  // Clear old model data.
   model_.DeleteAllItems();
   search_model_.DeleteAllResults();
-  for (size_t i = 0; i < apps.size(); ++i) {
-    AppListItemMetadataPtr item = std::move(apps[i]);
-    const std::string& folder_id = item->folder_id;
+
+  // Populate new models.
+  for (auto& app : apps) {
+    const std::string folder_id = app->folder_id;
     if (folder_id.empty())
-      AddItem(std::move(item));
+      AddItem(std::move(app));
     else
-      AddItemToFolder(std::move(item), folder_id);
+      AddItemToFolder(std::move(app), folder_id);
   }
   search_model_.SetSearchEngineIsGoogle(is_search_engine_google);
 }
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index caaa0e5..41b39e9 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1128,7 +1128,7 @@
         App does not support split-screen.
       </message>
 
-      <!-- Lockscreen strings -->
+      <!-- Login screen strings -->
       <message name="IDS_ASH_LOGIN_POD_PASSWORD_PLACEHOLDER" desc="Text to display as placeholder in the password field when password is the only auth method.">
         Password
       </message>
@@ -1183,6 +1183,24 @@
       <message name="IDS_ASH_LOGIN_POD_NON_OWNER_USER_REMOVE_WARNING_PART_2" desc="Text shown as a warning when attempting to remove non-owner user. Also see IDS_ASH_LOGIN_POD_NON_OWNER_USER_REMOVE_WARNING_PART_1.">
         $1 can still sign in later.
       </message>
+      <message name="IDS_ASH_LOGIN_PUBLIC_ACCOUNT_INFO_FORMAT" desc="Template for text shown in the public account user pod, informing the user that this is a public, managed account.">
+        Managed by <ph name="DOMAIN">$1<ex>yourdomain.com</ex></ph>
+      </message>
+      <message name="IDS_ASH_LOGIN_PUBLIC_ACCOUNT_MONITORING_WARNING" desc="Template for text shown in the public account user pod, informing the user that this is a public, managed account.">
+        The device admin may be able to monitor your activity.
+      </message>
+      <message name="IDS_ASH_LOGIN_PUBLIC_ACCOUNT_SIGNOUT_REMINDER" desc="Text shown in the public account user pod, reminding the user to log out.">
+        Your information will be removed when you sign out. <ph name="LEARN_MORE">$1<ex>Learn more</ex></ph>
+      </message>
+      <message name="IDS_ASH_LOGIN_PUBLIC_SESSION_LANGUAGE_AND_INPUT" desc="Link in public session pod that shows a section which allows the user to change the UI language and keyboard layout.">
+        Language and input
+      </message>
+      <message name="IDS_ASH_LOGIN_LANGUAGE_SELECTION_SELECT" desc="Label for language selection dropdown">
+        Set your language
+      </message>
+      <message name="IDS_ASH_LOGIN_KEYBOARD_SELECTION_SELECT" desc="Label for keyboard selection dropdown">
+        Set your keyboard
+      </message>
 
       <!-- Multi-profiles intro dialog -->
       <message name="IDS_ASH_MULTIPROFILES_INTRO_HEADLINE" desc="Describes which feature multi-profiles intro dialog presents.">
diff --git a/ash/login/login_screen_controller.cc b/ash/login/login_screen_controller.cc
index 36925e8..e024c81 100644
--- a/ash/login/login_screen_controller.cc
+++ b/ash/login/login_screen_controller.cc
@@ -172,6 +172,24 @@
   std::move(callback).Run(LockScreen::IsShown() && !is_authenticating_);
 }
 
+void LoginScreenController::SetPublicSessionDisplayName(
+    const AccountId& account_id,
+    const std::string& display_name) {
+  if (DataDispatcher())
+    DataDispatcher()->SetPublicSessionDisplayName(account_id, display_name);
+}
+
+void LoginScreenController::SetPublicSessionLocales(
+    const AccountId& account_id,
+    std::unique_ptr<base::ListValue> locales,
+    const std::string& default_locale,
+    bool show_advanced_view) {
+  if (DataDispatcher()) {
+    DataDispatcher()->SetPublicSessionLocales(
+        account_id, std::move(locales), default_locale, show_advanced_view);
+  }
+}
+
 void LoginScreenController::AuthenticateUser(const AccountId& account_id,
                                              const std::string& password,
                                              bool authenticated_by_pin,
diff --git a/ash/login/login_screen_controller.h b/ash/login/login_screen_controller.h
index 82a71a51..499ebe6 100644
--- a/ash/login/login_screen_controller.h
+++ b/ash/login/login_screen_controller.h
@@ -65,6 +65,12 @@
                          const std::string& enterprise_info_text,
                          const std::string& bluetooth_name) override;
   void IsReadyForPassword(IsReadyForPasswordCallback callback) override;
+  void SetPublicSessionDisplayName(const AccountId& account_id,
+                                   const std::string& display_name) override;
+  void SetPublicSessionLocales(const AccountId& account_id,
+                               std::unique_ptr<base::ListValue> locales,
+                               const std::string& default_locale,
+                               bool show_advanced_view) override;
 
   // Wrappers around the mojom::LoginScreenClient interface. Hash the password
   // and send AuthenticateUser request to LoginScreenClient.
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index c918bdf..ae7b613c 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -481,6 +481,20 @@
   LayoutTopHeader();
 }
 
+void LockContentsView::OnPublicSessionDisplayNameChanged(
+    const AccountId& account_id,
+    const std::string& display_name) {
+  NOTIMPLEMENTED();
+}
+
+void LockContentsView::OnPublicSessionLocalesChanged(
+    const AccountId& account_id,
+    const base::ListValue& locales,
+    const std::string& default_locale,
+    bool show_advanced_view) {
+  NOTIMPLEMENTED();
+}
+
 void LockContentsView::OnFocusLeavingLockScreenApps(bool reverse) {
   if (!reverse || lock_screen_apps_active_)
     FocusNextWidget(reverse);
diff --git a/ash/login/ui/lock_contents_view.h b/ash/login/ui/lock_contents_view.h
index dcb3a03..7f9ccb70 100644
--- a/ash/login/ui/lock_contents_view.h
+++ b/ash/login/ui/lock_contents_view.h
@@ -104,6 +104,13 @@
   void OnDevChannelInfoChanged(const std::string& os_version_label_text,
                                const std::string& enterprise_info_text,
                                const std::string& bluetooth_name) override;
+  void OnPublicSessionDisplayNameChanged(
+      const AccountId& account_id,
+      const std::string& display_name) override;
+  void OnPublicSessionLocalesChanged(const AccountId& account_id,
+                                     const base::ListValue& locales,
+                                     const std::string& default_locale,
+                                     bool show_advanced_view) override;
 
   // SystemTrayFocusObserver:
   void OnFocusLeavingSystemTray(bool reverse) override;
diff --git a/ash/login/ui/lock_debug_view.cc b/ash/login/ui/lock_debug_view.cc
index 9387a4c..a29c3b7d 100644
--- a/ash/login/ui/lock_debug_view.cc
+++ b/ash/login/ui/lock_debug_view.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#include "ash/ime/ime_controller.h"
 #include "ash/login/login_screen_controller.h"
 #include "ash/login/ui/layout_util.h"
 #include "ash/login/ui/lock_contents_view.h"
@@ -18,7 +19,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "mojo/common/values_struct_traits.h"
 #include "ui/base/ime/chromeos/ime_keyboard.h"
-#include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/views/controls/button/md_text_button.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/view.h"
@@ -318,9 +318,8 @@
 
   // Enable or disable caps lock.
   if (sender == toggle_caps_lock_) {
-    chromeos::input_method::ImeKeyboard* keyboard =
-        chromeos::input_method::InputMethodManager::Get()->GetImeKeyboard();
-    keyboard->SetCapsLockEnabled(!keyboard->CapsLockIsEnabled());
+    ImeController* ime_controller = Shell::Get()->ime_controller();
+    ime_controller->SetCapsLockFromTray(!ime_controller->IsCapsLockEnabled());
     return;
   }
 
diff --git a/ash/login/ui/login_auth_user_view.cc b/ash/login/ui/login_auth_user_view.cc
index c05bc83..9e7dca8 100644
--- a/ash/login/ui/login_auth_user_view.cc
+++ b/ash/login/ui/login_auth_user_view.cc
@@ -44,7 +44,10 @@
 const int kDistanceBetweenPasswordFieldAndPinKeyboard = 20;
 
 // Distance from the end of pin keyboard to the bottom of the big user view.
-const int kDistanceFromPinKeyboardToBigUserViewBottom = 48;
+const int kDistanceFromPinKeyboardToBigUserViewBottom = 50;
+
+// Distance from the top of the user view to the user icon.
+constexpr int kDistanceFromTopOfBigUserViewToUserIconDp = 54;
 
 // Returns an observer that will hide |view| when it fires. The observer will
 // delete itself after firing. Make sure to call |observer->SetReady()| after
@@ -108,9 +111,12 @@
       on_auth_(on_auth),
       on_tap_(on_tap),
       weak_factory_(this) {
+  DCHECK_NE(user->basic_user_info->type,
+            user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+
   // Build child views.
   user_view_ = new LoginUserView(
-      LoginDisplayStyle::kLarge, true /*show_dropdown*/,
+      LoginDisplayStyle::kLarge, true /*show_dropdown*/, false /*show_domain*/,
       base::Bind(&LoginAuthUserView::OnUserViewTap, base::Unretained(this)));
 
   password_view_ = new LoginPasswordView();
@@ -164,6 +170,7 @@
   };
 
   // Add views in rendering order.
+  add_padding(kDistanceFromTopOfBigUserViewToUserIconDp);
   add_view(wrapped_user_view);
   add_padding(kDistanceBetweenUserViewAndPasswordDp);
   add_view(password_view_);
diff --git a/ash/login/ui/login_data_dispatcher.cc b/ash/login/ui/login_data_dispatcher.cc
index 08b994c..f280408c 100644
--- a/ash/login/ui/login_data_dispatcher.cc
+++ b/ash/login/ui/login_data_dispatcher.cc
@@ -31,6 +31,16 @@
     const std::string& enterprise_info_text,
     const std::string& bluetooth_name) {}
 
+void LoginDataDispatcher::Observer::OnPublicSessionDisplayNameChanged(
+    const AccountId& account_id,
+    const std::string& display_name) {}
+
+void LoginDataDispatcher::Observer::OnPublicSessionLocalesChanged(
+    const AccountId& account_id,
+    const base::ListValue& locales,
+    const std::string& default_locale,
+    bool show_advanced_view) {}
+
 LoginDataDispatcher::LoginDataDispatcher() = default;
 
 LoginDataDispatcher::~LoginDataDispatcher() = default;
@@ -83,4 +93,22 @@
   }
 }
 
+void LoginDataDispatcher::SetPublicSessionDisplayName(
+    const AccountId& account_id,
+    const std::string& display_name) {
+  for (auto& observer : observers_)
+    observer.OnPublicSessionDisplayNameChanged(account_id, display_name);
+}
+
+void LoginDataDispatcher::SetPublicSessionLocales(
+    const AccountId& account_id,
+    std::unique_ptr<base::ListValue> locales,
+    const std::string& default_locale,
+    bool show_advanced_view) {
+  for (auto& observer : observers_) {
+    observer.OnPublicSessionLocalesChanged(account_id, *locales, default_locale,
+                                           show_advanced_view);
+  }
+}
+
 }  // namespace ash
diff --git a/ash/login/ui/login_data_dispatcher.h b/ash/login/ui/login_data_dispatcher.h
index 12258c10..4fef1be 100644
--- a/ash/login/ui/login_data_dispatcher.h
+++ b/ash/login/ui/login_data_dispatcher.h
@@ -63,6 +63,20 @@
         const std::string& os_version_label_text,
         const std::string& enterprise_info_text,
         const std::string& bluetooth_name);
+
+    // Called when public session display name is changed for user with
+    // |account_id|.
+    virtual void OnPublicSessionDisplayNameChanged(
+        const AccountId& account_id,
+        const std::string& display_name);
+
+    // Called when public session locales are changed for user with
+    // |account_id|.
+    virtual void OnPublicSessionLocalesChanged(
+        const AccountId& account_id,
+        const base::ListValue& locales,
+        const std::string& default_locale,
+        bool show_advanced_view);
   };
 
   LoginDataDispatcher();
@@ -80,6 +94,12 @@
   void SetDevChannelInfo(const std::string& os_version_label_text,
                          const std::string& enterprise_info_text,
                          const std::string& bluetooth_name);
+  void SetPublicSessionDisplayName(const AccountId& account_id,
+                                   const std::string& display_name);
+  void SetPublicSessionLocales(const AccountId& account_id,
+                               std::unique_ptr<base::ListValue> locales,
+                               const std::string& default_locale,
+                               bool show_advanced_view);
 
  private:
   base::ObserverList<Observer> observers_;
diff --git a/ash/login/ui/login_password_view.cc b/ash/login/ui/login_password_view.cc
index 8bb5878..51f25b5 100644
--- a/ash/login/ui/login_password_view.cc
+++ b/ash/login/ui/login_password_view.cc
@@ -9,12 +9,12 @@
 #include "ash/login/ui/non_accessible_view.h"
 #include "ash/public/cpp/login_constants.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/user/button_from_view.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_node_data.h"
-#include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/events/event_constants.h"
@@ -359,7 +359,9 @@
   view_->easy_unlock_icon_->set_immediately_hover_for_test();
 }
 
-LoginPasswordView::LoginPasswordView() : ime_keyboard_observer_(this) {
+LoginPasswordView::LoginPasswordView() {
+  Shell::Get()->ime_controller()->AddObserver(this);
+
   auto root_layout =
       std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical);
   root_layout->set_main_axis_alignment(
@@ -444,19 +446,16 @@
   // Make sure the textfield always starts with focus.
   textfield_->RequestFocus();
 
-  // Input method manager may be null in tests.
-  if (chromeos::input_method::InputMethodManager::Get()) {
-    chromeos::input_method::ImeKeyboard* keyboard =
-        chromeos::input_method::InputMethodManager::Get()->GetImeKeyboard();
-    ime_keyboard_observer_.Add(keyboard);
-    OnCapsLockChanged(keyboard->CapsLockIsEnabled());
-  }
+  // Initialize with the initial state of caps lock.
+  OnCapsLockChanged(Shell::Get()->ime_controller()->IsCapsLockEnabled());
 
   // Make sure the UI start with the correct states.
   UpdateUiState();
 }
 
-LoginPasswordView::~LoginPasswordView() = default;
+LoginPasswordView::~LoginPasswordView() {
+  Shell::Get()->ime_controller()->RemoveObserver(this);
+}
 
 void LoginPasswordView::Init(
     const OnPasswordSubmit& on_submit,
diff --git a/ash/login/ui/login_password_view.h b/ash/login/ui/login_password_view.h
index c7f816c..f1a984d 100644
--- a/ash/login/ui/login_password_view.h
+++ b/ash/login/ui/login_password_view.h
@@ -6,6 +6,7 @@
 #define ASH_LOGIN_UI_LOGIN_PASSWORD_VIEW_H_
 
 #include "ash/ash_export.h"
+#include "ash/ime/ime_controller.h"
 #include "ash/login/ui/animated_rounded_image_view.h"
 #include "ash/public/interfaces/login_user_info.mojom.h"
 #include "ash/public/interfaces/user_info.mojom.h"
@@ -36,11 +37,10 @@
 //
 //   * * * * * *   =>
 //  ------------------
-class ASH_EXPORT LoginPasswordView
-    : public views::View,
-      public views::ButtonListener,
-      public views::TextfieldController,
-      public chromeos::input_method::ImeKeyboard::Observer {
+class ASH_EXPORT LoginPasswordView : public views::View,
+                                     public views::ButtonListener,
+                                     public views::TextfieldController,
+                                     public ImeController::Observer {
  public:
   // TestApi is used for tests to get internal implementation details.
   class ASH_EXPORT TestApi {
@@ -116,9 +116,8 @@
   void ContentsChanged(views::Textfield* sender,
                        const base::string16& new_contents) override;
 
-  // chromeos::input_method::ImeKeyboard::Observer:
+  // ImeController::Observer:
   void OnCapsLockChanged(bool enabled) override;
-  void OnLayoutChanging(const std::string& layout_name) override {}
 
  private:
   class EasyUnlockIcon;
@@ -144,10 +143,6 @@
   EasyUnlockIcon* easy_unlock_icon_ = nullptr;
   views::View* easy_unlock_right_margin_ = nullptr;
 
-  ScopedObserver<chromeos::input_method::ImeKeyboard,
-                 chromeos::input_method::ImeKeyboard::Observer>
-      ime_keyboard_observer_;
-
   DISALLOW_COPY_AND_ASSIGN(LoginPasswordView);
 };
 
diff --git a/ash/login/ui/login_user_view.cc b/ash/login/ui/login_user_view.cc
index b9c86d3c..7210fad 100644
--- a/ash/login/ui/login_user_view.cc
+++ b/ash/login/ui/login_user_view.cc
@@ -10,16 +10,19 @@
 #include "ash/login/ui/animated_rounded_image_view.h"
 #include "ash/login/ui/hover_notifier.h"
 #include "ash/login/ui/image_parser.h"
+#include "ash/login/ui/layout_util.h"
 #include "ash/login/ui/login_bubble.h"
 #include "ash/login/ui/login_button.h"
 #include "ash/login/ui/non_accessible_view.h"
 #include "ash/login/ui/user_switch_flip_animation.h"
 #include "ash/public/cpp/login_constants.h"
+#include "ash/public/interfaces/user_info.mojom.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/user/rounded_image_view.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/user_manager/user_type.h"
 #include "mojo/common/values_struct_traits.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/compositor/layer_animation_sequence.h"
@@ -27,6 +30,7 @@
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
@@ -40,8 +44,6 @@
 constexpr int kVerticalSpacingBetweenEntriesDp = 32;
 // Horizontal spacing between username label and the dropdown icon.
 constexpr int kDistanceBetweenUsernameAndDropdownDp = 8;
-// Distance from the top of the user view to the user icon.
-constexpr int kDistanceFromTopOfBigUserViewToUserIconDp = 54;
 // Distance between user icon and the user label in small/extra-small layouts.
 constexpr int kSmallManyDistanceFromUserIconToUserLabelDp = 16;
 
@@ -63,9 +65,17 @@
 constexpr float kTransparentUserViewOpacity = 0.63f;
 constexpr float kUserFadeAnimationDurationMs = 180;
 
-constexpr const char kUserViewClassName[] = "UserView";
-constexpr const char kLoginUserImageClassName[] = "LoginUserImage";
-constexpr const char kLoginUserLabelClassName[] = "LoginUserLabel";
+constexpr char kUserViewClassName[] = "UserView";
+constexpr char kLoginUserImageClassName[] = "LoginUserImage";
+constexpr char kLoginUserLabelClassName[] = "LoginUserLabel";
+constexpr char kLoginUserDomainClassName[] = "LoginUserDomain";
+
+// Color of the user domain text.
+constexpr SkColor kDomainTextColor =
+    SkColorSetARGBMacro(0xAB, 0xFF, 0xFF, 0xFF);
+constexpr int kEnterpriseIconSizeDp = 12;
+constexpr int kBetweenEnterpriseIconAndDomainDp = 8;
+constexpr int kVerticalSpacingBetweenUserNameAndDomainDp = 17;
 
 int GetImageSize(LoginDisplayStyle style) {
   switch (style) {
@@ -215,6 +225,52 @@
   DISALLOW_COPY_AND_ASSIGN(TapButton);
 };
 
+class LoginUserView::UserDomainInfoView : public NonAccessibleView {
+ public:
+  UserDomainInfoView() : NonAccessibleView(kLoginUserDomainClassName) {
+    auto layout =
+        std::make_unique<views::BoxLayout>(views::BoxLayout::kHorizontal);
+    layout->set_main_axis_alignment(
+        views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
+    SetLayoutManager(std::move(layout));
+
+    views::ImageView* image = new views::ImageView();
+    image->SetImage(
+        gfx::CreateVectorIcon(kLoginScreenEnterpriseIcon, kDomainTextColor));
+    image->SetPreferredSize(
+        gfx::Size(kEnterpriseIconSizeDp, kEnterpriseIconSizeDp));
+    AddChildView(image);
+
+    auto* spacer = new NonAccessibleView();
+    spacer->SetPreferredSize(gfx::Size(kBetweenEnterpriseIconAndDomainDp, 0));
+    AddChildView(spacer);
+
+    label_ = new views::Label();
+    label_->SetEnabledColor(kDomainTextColor);
+    label_->SetSubpixelRenderingEnabled(false);
+    label_->SetAutoColorReadabilityEnabled(false);
+    label_->SetFontList(views::Label::GetDefaultFontList().Derive(
+        0, gfx::Font::FontStyle::NORMAL, gfx::Font::Weight::NORMAL));
+    AddChildView(label_);
+  }
+
+  ~UserDomainInfoView() override = default;
+
+  // views::View:
+  gfx::Size CalculatePreferredSize() const override {
+    gfx::Size size = views::View::CalculatePreferredSize();
+    size.set_width(kLargeUserViewWidthDp);
+    return size;
+  }
+
+  void SetText(const base::string16& text) { label_->SetText(text); }
+
+ private:
+  views::Label* label_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(UserDomainInfoView);
+};
+
 // LoginUserView is defined after LoginUserView::UserLabel so it can access the
 // class members.
 
@@ -259,11 +315,13 @@
 
 LoginUserView::LoginUserView(LoginDisplayStyle style,
                              bool show_dropdown,
+                             bool show_domain,
                              const OnTap& on_tap)
     : on_tap_(on_tap), display_style_(style) {
-  // show_dropdown can only be true when the user view is rendering in large
-  // mode.
+  // show_dropdown and show_domain can only be true when the user view is
+  // rendering in large mode.
   DCHECK(!show_dropdown || style == LoginDisplayStyle::kLarge);
+  DCHECK(!show_domain || style == LoginDisplayStyle::kLarge);
 
   user_image_ = new UserImage(GetImageSize(style));
   user_label_ = new UserLabel(style);
@@ -277,6 +335,8 @@
         gfx::CreateVectorIcon(kLockScreenDropdownIcon, SK_ColorWHITE));
     user_dropdown_->SetFocusBehavior(FocusBehavior::ALWAYS);
   }
+  if (show_domain)
+    user_domain_ = new UserDomainInfoView();
   tap_button_ = new TapButton(this);
   SetTapEnabled(true);
 
@@ -305,6 +365,9 @@
   if (user_dropdown_)
     setup_layer(user_dropdown_);
 
+  if (user_domain_)
+    setup_layer(user_domain_);
+
   hover_notifier_ = std::make_unique<HoverNotifier>(
       this, base::Bind(&LoginUserView::OnHover, base::Unretained(this)));
   user_menu_ = std::make_unique<LoginBubble>();
@@ -360,6 +423,10 @@
       user_dropdown_->layer()->GetAnimator()->StartAnimation(
           make_opacity_sequence());
     }
+    if (user_domain_) {
+      user_domain_->layer()->GetAnimator()->StartAnimation(
+          make_opacity_sequence());
+    }
   } else {
     // Do not animate, so directly update to the current user.
     UpdateCurrentUserState();
@@ -435,6 +502,16 @@
         IDS_ASH_LOGIN_POD_MENU_BUTTON_ACCESSIBLE_NAME, email));
   }
 
+  if (user_domain_) {
+    const base::Optional<std::string>& enterprise_domain =
+        current_user_->public_account_info->enterprise_domain;
+    if (enterprise_domain) {
+      user_domain_->SetText(l10n_util::GetStringFUTF16(
+          IDS_ASH_LOGIN_PUBLIC_ACCOUNT_INFO_FORMAT,
+          base::UTF8ToUTF16(enterprise_domain.value())));
+    }
+  }
+
   user_image_->UpdateForUser(current_user_);
   user_label_->UpdateForUser(current_user_);
   Layout();
@@ -448,7 +525,8 @@
     return;
 
   // Animate to new opacity.
-  auto build_settings = [](views::View* view) {
+  auto build_settings = [](views::View* view)
+      -> std::unique_ptr<ui::ScopedLayerAnimationSettings> {
     auto settings = std::make_unique<ui::ScopedLayerAnimationSettings>(
         view->layer()->GetAnimator());
     settings->SetTransitionDuration(
@@ -456,17 +534,26 @@
     settings->SetTweenType(gfx::Tween::Type::EASE_IN_OUT);
     return settings;
   };
-  auto user_image_settings = build_settings(user_image_);
-  auto user_label_settings = build_settings(user_label_);
+  std::unique_ptr<ui::ScopedLayerAnimationSettings> user_image_settings =
+      build_settings(user_image_);
+  std::unique_ptr<ui::ScopedLayerAnimationSettings> user_label_settings =
+      build_settings(user_label_);
   float target_opacity =
       is_opaque_ ? kOpaqueUserViewOpacity : kTransparentUserViewOpacity;
   user_image_->layer()->SetOpacity(target_opacity);
   user_label_->layer()->SetOpacity(target_opacity);
   if (user_dropdown_) {
-    auto user_dropdown_settings = build_settings(user_dropdown_);
+    std::unique_ptr<ui::ScopedLayerAnimationSettings> user_dropdown_settings =
+        build_settings(user_dropdown_);
     user_dropdown_->layer()->SetOpacity(target_opacity);
   }
 
+  if (user_domain_) {
+    std::unique_ptr<ui::ScopedLayerAnimationSettings> user_domain_settings =
+        build_settings(user_domain_);
+    user_domain_->layer()->SetOpacity(target_opacity);
+  }
+
   // Animate avatar only if we are opaque.
   user_image_->SetAnimationEnabled(is_opaque_);
 }
@@ -478,6 +565,8 @@
   AddChildView(tap_button_);
   if (user_dropdown_)
     AddChildView(user_dropdown_);
+  if (user_domain_)
+    AddChildView(user_domain_);
 
   // Use views::GridLayout instead of views::BoxLayout because views::BoxLayout
   // lays out children according to the view->children order.
@@ -486,6 +575,8 @@
 
   constexpr int kImageColumnId = 0;
   constexpr int kLabelDropdownColumnId = 1;
+  constexpr int kLabelDomainColumnId = 2;
+
   {
     views::ColumnSet* image = layout->AddColumnSet(kImageColumnId);
     image->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
@@ -517,13 +608,19 @@
     label_dropdown->AddPaddingColumn(1.0f /*resize_percent*/, 0 /*width*/);
   }
 
+  {
+    views::ColumnSet* label_domain = layout->AddColumnSet(kLabelDomainColumnId);
+    label_domain->AddColumn(views::GridLayout::CENTER,
+                            views::GridLayout::CENTER, 1 /*resize_percent*/,
+                            views::GridLayout::USE_PREF, 0 /*fixed_width*/,
+                            0 /*min_width*/);
+  }
+
   auto add_padding = [&](int amount) {
     layout->AddPaddingRow(0 /*vertical_resize*/, amount /*size*/);
   };
 
   // Add views in rendering order.
-  add_padding(kDistanceFromTopOfBigUserViewToUserIconDp);
-
   // Image
   layout->StartRow(0 /*vertical_resize*/, kImageColumnId);
   layout->AddView(user_image_);
@@ -535,6 +632,12 @@
   layout->AddView(user_label_);
   if (user_dropdown_)
     layout->AddView(user_dropdown_);
+
+  if (user_domain_) {
+    add_padding(kVerticalSpacingBetweenUserNameAndDomainDp);
+    layout->StartRow(0 /*vertical_resize*/, kLabelDomainColumnId);
+    layout->AddView(user_domain_);
+  }
 }
 
 void LoginUserView::SetSmallishLayout() {
diff --git a/ash/login/ui/login_user_view.h b/ash/login/ui/login_user_view.h
index 3e70c6a..4d27549c 100644
--- a/ash/login/ui/login_user_view.h
+++ b/ash/login/ui/login_user_view.h
@@ -49,6 +49,7 @@
 
   LoginUserView(LoginDisplayStyle style,
                 bool show_dropdown,
+                bool show_domain,
                 const OnTap& on_tap);
   ~LoginUserView() override;
 
@@ -72,6 +73,7 @@
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
  private:
+  class UserDomainInfoView;
   class UserImage;
   class UserLabel;
   class TapButton;
@@ -104,6 +106,9 @@
   TapButton* tap_button_ = nullptr;
   std::unique_ptr<LoginBubble> user_menu_;
 
+  // Show the domain information for public account user.
+  UserDomainInfoView* user_domain_ = nullptr;
+
   // True iff the view is currently opaque (ie, opacity = 1).
   bool is_opaque_ = false;
   // True if the view must be opaque (ie, opacity = 1) regardless of input
diff --git a/ash/login/ui/login_user_view_unittest.cc b/ash/login/ui/login_user_view_unittest.cc
index 95cc39a..65406597 100644
--- a/ash/login/ui/login_user_view_unittest.cc
+++ b/ash/login/ui/login_user_view_unittest.cc
@@ -24,8 +24,9 @@
   // Builds a new LoginUserView instance and adds it to |container_|.
   LoginUserView* AddUserView(LoginDisplayStyle display_style,
                              bool show_dropdown) {
+    // TODO(crbug.com/809635): Add test case for show_domain.
     auto* view =
-        new LoginUserView(display_style, show_dropdown,
+        new LoginUserView(display_style, show_dropdown, false /*show_domain*/,
                           base::BindRepeating(&LoginUserViewUnittest::OnTapped,
                                               base::Unretained(this)));
     mojom::LoginUserInfoPtr user = CreateUser("foo@foo.com");
diff --git a/ash/login/ui/scrollable_users_list_view.cc b/ash/login/ui/scrollable_users_list_view.cc
index 639a7a3..4ad0497d 100644
--- a/ash/login/ui/scrollable_users_list_view.cc
+++ b/ash/login/ui/scrollable_users_list_view.cc
@@ -136,6 +136,7 @@
   for (std::size_t i = 1u; i < users.size(); ++i) {
     auto* view = new LoginUserView(
         layout_params_.display_style, false /*show_dropdown*/,
+        false /*show_domain*/,
         base::BindRepeating(on_user_view_tap, i - 1) /*on_tap*/);
     user_views_.push_back(view);
     view->UpdateForUser(users[i], false /*animate*/);
diff --git a/ash/public/interfaces/login_screen.mojom b/ash/public/interfaces/login_screen.mojom
index 8fd9a31b..5b50918 100644
--- a/ash/public/interfaces/login_screen.mojom
+++ b/ash/public/interfaces/login_screen.mojom
@@ -8,6 +8,7 @@
 import "components/password_manager/public/interfaces/sync_password_data.mojom";
 import "components/proximity_auth/public/interfaces/auth_type.mojom";
 import "components/signin/public/interfaces/account_id.mojom";
+import "mojo/common/values.mojom";
 import "mojo/public/mojom/base/string16.mojom";
 
 // Allows clients (e.g. Chrome browser) to control the ash login/lock/user-add
@@ -82,6 +83,24 @@
 
   // Check if the login/lock screen is ready for a password.
   IsReadyForPassword() => (bool is_ready);
+
+  // Set the public session display name for user with |account_id|.
+  SetPublicSessionDisplayName(signin.mojom.AccountId account_id,
+                              string display_name);
+
+  // Set the public session locales for user with |account_id|.
+  // TODO: Use array<LocaleStruct> for |locales| instead of
+  // mojo.common.mojom.ListValue.
+  // |locales|: Available locales for this user.
+  //            Each entry is a base::DictionaryValue and it contains several
+  //            keys like displayName, nativeDisplayName, optionGroupName etc.
+  // |default_locale|: Default locale for this user.
+  // |show_advanced_view|: True if we should show the advanced expanded user
+  //                       view for the public session.
+  SetPublicSessionLocales(signin.mojom.AccountId account_id,
+                          mojo.common.mojom.ListValue locales,
+                          string default_locale,
+                          bool show_advanced_view);
 };
 
 // Allows ash lock screen to control a client (e.g. Chrome browser). Requests
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index 2093732..cde5b3a 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -31,6 +31,8 @@
     "lock_screen_caps_lock.icon",
     "lock_screen_dropdown.1x.icon",
     "lock_screen_dropdown.icon",
+    "login_screen_enterprise.1x.icon",
+    "login_screen_enterprise.icon",
     "network_badge_add_other.1x.icon",
     "network_badge_add_other.icon",
     "network_badge_captive_portal.1x.icon",
diff --git a/ash/resources/vector_icons/login_screen_enterprise.1x.icon b/ash/resources/vector_icons/login_screen_enterprise.1x.icon
new file mode 100644
index 0000000..bb0c84fd
--- /dev/null
+++ b/ash/resources/vector_icons/login_screen_enterprise.1x.icon
@@ -0,0 +1,88 @@
+// 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.
+
+CANVAS_DIMENSIONS, 12,
+MOVE_TO, 8, 9,
+R_H_LINE_TO, 1,
+V_LINE_TO, 8,
+H_LINE_TO, 8,
+R_V_LINE_TO, 1,
+CLOSE,
+R_MOVE_TO, 0, -2,
+R_H_LINE_TO, 1,
+V_LINE_TO, 6,
+H_LINE_TO, 8,
+R_V_LINE_TO, 1,
+CLOSE,
+MOVE_TO, 4, 4,
+R_H_LINE_TO, 1,
+V_LINE_TO, 3,
+H_LINE_TO, 4,
+R_V_LINE_TO, 1,
+CLOSE,
+R_MOVE_TO, 0, 2,
+R_H_LINE_TO, 1,
+V_LINE_TO, 5,
+H_LINE_TO, 4,
+R_V_LINE_TO, 1,
+CLOSE,
+R_MOVE_TO, 0, 2,
+R_H_LINE_TO, 1,
+V_LINE_TO, 7,
+H_LINE_TO, 4,
+R_V_LINE_TO, 1,
+CLOSE,
+R_MOVE_TO, 0, 2,
+R_H_LINE_TO, 1,
+V_LINE_TO, 9,
+H_LINE_TO, 4,
+R_V_LINE_TO, 1,
+CLOSE,
+MOVE_TO, 2, 4,
+R_H_LINE_TO, 1,
+V_LINE_TO, 3,
+H_LINE_TO, 2,
+R_V_LINE_TO, 1,
+CLOSE,
+R_MOVE_TO, 0, 2,
+R_H_LINE_TO, 1,
+V_LINE_TO, 5,
+H_LINE_TO, 2,
+R_V_LINE_TO, 1,
+CLOSE,
+R_MOVE_TO, 0, 2,
+R_H_LINE_TO, 1,
+V_LINE_TO, 7,
+H_LINE_TO, 2,
+R_V_LINE_TO, 1,
+CLOSE,
+R_MOVE_TO, 0, 2,
+R_H_LINE_TO, 1,
+V_LINE_TO, 9,
+H_LINE_TO, 2,
+R_V_LINE_TO, 1,
+CLOSE,
+R_MOVE_TO, 4, -6,
+V_LINE_TO, 2,
+H_LINE_TO, 1,
+R_V_LINE_TO, 9,
+R_H_LINE_TO, 10,
+V_LINE_TO, 4,
+H_LINE_TO, 6,
+CLOSE,
+R_MOVE_TO, 4, 6,
+H_LINE_TO, 6,
+V_LINE_TO, 9,
+R_H_LINE_TO, 1,
+V_LINE_TO, 8,
+H_LINE_TO, 6,
+V_LINE_TO, 7,
+R_H_LINE_TO, 1,
+V_LINE_TO, 6,
+H_LINE_TO, 6,
+V_LINE_TO, 5,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, 5,
+CLOSE,
+END
diff --git a/ash/resources/vector_icons/login_screen_enterprise.icon b/ash/resources/vector_icons/login_screen_enterprise.icon
new file mode 100644
index 0000000..523a4d2
--- /dev/null
+++ b/ash/resources/vector_icons/login_screen_enterprise.icon
@@ -0,0 +1,88 @@
+// 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.
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 16, 17,
+R_H_LINE_TO, 2,
+R_V_LINE_TO, -2,
+R_H_LINE_TO, -2,
+R_V_LINE_TO, 2,
+CLOSE,
+R_MOVE_TO, 0, -4,
+R_H_LINE_TO, 2,
+R_V_LINE_TO, -2,
+R_H_LINE_TO, -2,
+R_V_LINE_TO, 2,
+CLOSE,
+R_MOVE_TO, 4, 6,
+R_H_LINE_TO, -8,
+R_V_LINE_TO, -2,
+R_H_LINE_TO, 2,
+R_V_LINE_TO, -2,
+R_H_LINE_TO, -2,
+R_V_LINE_TO, -2,
+R_H_LINE_TO, 2,
+R_V_LINE_TO, -2,
+R_H_LINE_TO, -2,
+V_LINE_TO, 9,
+R_H_LINE_TO, 8,
+R_V_LINE_TO, 10,
+CLOSE,
+MOVE_TO, 8, 7,
+R_H_LINE_TO, 2,
+V_LINE_TO, 5,
+H_LINE_TO, 8,
+R_V_LINE_TO, 2,
+CLOSE,
+R_MOVE_TO, 0, 4,
+R_H_LINE_TO, 2,
+V_LINE_TO, 9,
+H_LINE_TO, 8,
+R_V_LINE_TO, 2,
+CLOSE,
+R_MOVE_TO, 0, 4,
+R_H_LINE_TO, 2,
+R_V_LINE_TO, -2,
+H_LINE_TO, 8,
+R_V_LINE_TO, 2,
+CLOSE,
+R_MOVE_TO, 0, 4,
+R_H_LINE_TO, 2,
+R_V_LINE_TO, -2,
+H_LINE_TO, 8,
+R_V_LINE_TO, 2,
+CLOSE,
+MOVE_TO, 4, 7,
+R_H_LINE_TO, 2,
+V_LINE_TO, 5,
+H_LINE_TO, 4,
+R_V_LINE_TO, 2,
+CLOSE,
+R_MOVE_TO, 0, 4,
+R_H_LINE_TO, 2,
+V_LINE_TO, 9,
+H_LINE_TO, 4,
+R_V_LINE_TO, 2,
+CLOSE,
+R_MOVE_TO, 0, 4,
+R_H_LINE_TO, 2,
+R_V_LINE_TO, -2,
+H_LINE_TO, 4,
+R_V_LINE_TO, 2,
+CLOSE,
+R_MOVE_TO, 0, 4,
+R_H_LINE_TO, 2,
+R_V_LINE_TO, -2,
+H_LINE_TO, 4,
+R_V_LINE_TO, 2,
+CLOSE,
+R_MOVE_TO, 8, -12,
+V_LINE_TO, 3,
+H_LINE_TO, 2,
+R_V_LINE_TO, 18,
+R_H_LINE_TO, 20,
+V_LINE_TO, 7,
+H_LINE_TO, 12,
+CLOSE,
+END
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index c025675..4d1a3ca 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -517,7 +517,6 @@
 
   shelf_->DestroyShelfWidget();
 
-  aura::client::SetDragDropClient(GetRootWindow(), nullptr);
   ::wm::SetTooltipClient(GetRootWindow(), nullptr);
 }
 
diff --git a/ash/shell.cc b/ash/shell.cc
index 31aa09c..6d72023 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -731,6 +731,8 @@
   logout_confirmation_controller_.reset();
 
   // Drag-and-drop must be canceled prior to close all windows.
+  for (aura::Window* root : GetAllRootWindows())
+    aura::client::SetDragDropClient(root, nullptr);
   drag_drop_controller_.reset();
 
   // Controllers who have WindowObserver added must be deleted
diff --git a/base/strings/string_piece.h b/base/strings/string_piece.h
index d6236d1..0362381 100644
--- a/base/strings/string_piece.h
+++ b/base/strings/string_piece.h
@@ -219,16 +219,31 @@
     length_ = str ? STRING_TYPE::traits_type::length(str) : 0;
   }
 
-  constexpr value_type operator[](size_type i) const { return ptr_[i]; }
-  value_type front() const { return ptr_[0]; }
-  value_type back() const { return ptr_[length_ - 1]; }
+  constexpr value_type operator[](size_type i) const {
+    CHECK(i < length_);
+    return ptr_[i];
+  }
+
+  value_type front() const {
+    CHECK_NE(0UL, length_);
+    return ptr_[0];
+  }
+
+  value_type back() const {
+    CHECK_NE(0UL, length_);
+    return ptr_[length_ - 1];
+  }
 
   constexpr void remove_prefix(size_type n) {
+    CHECK(n <= length_);
     ptr_ += n;
     length_ -= n;
   }
 
-  constexpr void remove_suffix(size_type n) { length_ -= n; }
+  constexpr void remove_suffix(size_type n) {
+    CHECK(n <= length_);
+    length_ -= n;
+  }
 
   int compare(const BasicStringPiece<STRING_TYPE>& x) const {
     int r = wordmemcmp(
@@ -357,7 +372,7 @@
 
  protected:
   const value_type* ptr_;
-  size_type     length_;
+  size_type length_;
 };
 
 template <typename STRING_TYPE>
diff --git a/build/android/gyp/merge_manifest.py b/build/android/gyp/merge_manifest.py
index 43fef82..044c0d2 100755
--- a/build/android/gyp/merge_manifest.py
+++ b/build/android/gyp/merge_manifest.py
@@ -31,7 +31,7 @@
 
 
 @contextlib.contextmanager
-def _PatchedManifest(manifest_path):
+def _ProcessManifest(manifest_path):
   """Patches an Android manifest to always include the 'tools' namespace
   declaration, as it is not propagated by the manifest merger from the SDK.
 
@@ -41,6 +41,7 @@
   manifests = doc.getElementsByTagName('manifest')
   assert len(manifests) == 1
   manifest = manifests[0]
+  package = manifest.getAttribute('package')
 
   manifest.setAttribute('xmlns:%s' % TOOLS_NAMESPACE_PREFIX, TOOLS_NAMESPACE)
 
@@ -48,7 +49,7 @@
   with tempfile.NamedTemporaryFile(prefix=tmp_prefix) as patched_manifest:
     doc.writexml(patched_manifest)
     patched_manifest.flush()
-    yield patched_manifest.name
+    yield patched_manifest.name, package
 
 
 def _BuildManifestMergerClasspath(build_vars):
@@ -90,8 +91,9 @@
   if extras:
     cmd += ['--libs', ':'.join(extras)]
 
-  with _PatchedManifest(args.root_manifest) as root_manifest:
-    cmd += ['--main', root_manifest]
+  with _ProcessManifest(args.root_manifest) as tup:
+    root_manifest, package = tup
+    cmd += ['--main', root_manifest, '--property', 'PACKAGE=' + package]
     build_utils.CheckOutput(cmd,
       # https://issuetracker.google.com/issues/63514300: The merger doesn't set
       # a nonzero exit code for failures.
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index bc1573be..02e162026 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -1679,9 +1679,6 @@
     return;
 
   auto* controller = layer_tree_impl()->image_animation_controller();
-  if (!controller)
-    return;
-
   const auto& metadata = raster_source_->GetDisplayItemList()
                              ->discardable_image_map()
                              .animated_images_metadata();
@@ -1698,9 +1695,6 @@
     return;
 
   auto* controller = layer_tree_impl()->image_animation_controller();
-  if (!controller)
-    return;
-
   const auto& metadata = raster_source_->GetDisplayItemList()
                              ->discardable_image_map()
                              .animated_images_metadata();
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index dfccb52a..38e0a3a 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -83,7 +83,6 @@
     settings.commit_to_active_tree = false;
     settings.layer_transforms_should_scale_layer_contents = true;
     settings.create_low_res_tiling = true;
-    settings.enable_image_animations = true;
     return settings;
   }
 
diff --git a/cc/paint/display_item_list.cc b/cc/paint/display_item_list.cc
index d88cfa7..efe2e20 100644
--- a/cc/paint/display_item_list.cc
+++ b/cc/paint/display_item_list.cc
@@ -56,21 +56,16 @@
   paint_op_buffer_.Playback(canvas, PlaybackParams(image_provider), &offsets);
 }
 
-void DisplayItemList::GrowCurrentBeginItemVisualRect(
-    const gfx::Rect& visual_rect) {
-  DCHECK(usage_hint_ == kTopLevelDisplayItemList);
-  if (!begin_paired_indices_.empty())
-    visual_rects_[begin_paired_indices_.back().first].Union(visual_rect);
-}
-
 void DisplayItemList::Finalize() {
   TRACE_EVENT0("cc", "DisplayItemList::Finalize");
+#if DCHECK_IS_ON()
   // If this fails a call to StartPaint() was not ended.
-  DCHECK(!in_painting_);
+  DCHECK(!IsPainting());
   // If this fails we had more calls to EndPaintOfPairedBegin() than
   // to EndPaintOfPairedEnd().
-  DCHECK_EQ(0, in_paired_begin_count_);
+  DCHECK(begin_paired_indices_.empty());
   DCHECK_EQ(visual_rects_.size(), offsets_.size());
+#endif
 
   if (usage_hint_ == kTopLevelDisplayItemList) {
     rtree_.Build(visual_rects_,
@@ -164,8 +159,10 @@
 }
 
 void DisplayItemList::Reset() {
-  DCHECK(!in_painting_);
-  DCHECK_EQ(0, in_paired_begin_count_);
+#if DCHECK_IS_ON()
+  DCHECK(!IsPainting());
+  DCHECK(begin_paired_indices_.empty());
+#endif
 
   rtree_.Reset();
   image_map_.Reset();
@@ -176,9 +173,6 @@
   offsets_.shrink_to_fit();
   begin_paired_indices_.clear();
   begin_paired_indices_.shrink_to_fit();
-  current_range_start_ = 0;
-  in_paired_begin_count_ = 0;
-  in_painting_ = false;
 }
 
 sk_sp<PaintRecord> DisplayItemList::ReleaseAsRecord() {
diff --git a/cc/paint/display_item_list.h b/cc/paint/display_item_list.h
index c3e50a3..273caf3 100644
--- a/cc/paint/display_item_list.h
+++ b/cc/paint/display_item_list.h
@@ -59,26 +59,30 @@
 
   void Raster(SkCanvas* canvas, ImageProvider* image_provider = nullptr) const;
 
-  // TODO(vmpstr): This is only used to keep track of debugging info, so we can
-  // probably remove it? But it would be nice to delimit painting in a block
-  // somehow (RAII object maybe).
   void StartPaint() {
-    DCHECK(!in_painting_);
-    in_painting_ = true;
+#if DCHECK_IS_ON()
+    DCHECK(!IsPainting());
+    current_range_start_ = paint_op_buffer_.size();
+#endif
   }
 
   // Push functions construct a new op on the paint op buffer, while maintaining
   // bookkeeping information. Must be called after invoking StartPaint().
   template <typename T, typename... Args>
   void push(Args&&... args) {
-    DCHECK(in_painting_);
+#if DCHECK_IS_ON()
+    DCHECK(IsPainting());
+#endif
     if (usage_hint_ == kTopLevelDisplayItemList)
       offsets_.push_back(paint_op_buffer_.next_op_offset());
     paint_op_buffer_.push<T>(std::forward<Args>(args)...);
   }
 
   void EndPaintOfUnpaired(const gfx::Rect& visual_rect) {
-    in_painting_ = false;
+#if DCHECK_IS_ON()
+    DCHECK(IsPainting());
+    current_range_start_ = kNotPainting;
+#endif
     if (usage_hint_ == kToBeReleasedAsPaintOpBuffer)
       return;
 
@@ -88,26 +92,32 @@
   }
 
   void EndPaintOfPairedBegin(const gfx::Rect& visual_rect = gfx::Rect()) {
-    in_painting_ = false;
+#if DCHECK_IS_ON()
+    DCHECK(IsPainting());
+    DCHECK_LT(current_range_start_, paint_op_buffer_.size());
+    current_range_start_ = kNotPainting;
+#endif
     if (usage_hint_ == kToBeReleasedAsPaintOpBuffer)
       return;
-    DCHECK_NE(visual_rects_.size(), paint_op_buffer_.size());
+
+    DCHECK_LT(visual_rects_.size(), paint_op_buffer_.size());
     size_t count = paint_op_buffer_.size() - visual_rects_.size();
     for (size_t i = 0; i < count; ++i)
       visual_rects_.push_back(visual_rect);
     begin_paired_indices_.push_back(
         std::make_pair(visual_rects_.size() - 1, count));
-
-    in_paired_begin_count_++;
   }
 
   void EndPaintOfPairedEnd() {
-    in_painting_ = false;
+#if DCHECK_IS_ON()
+    DCHECK(IsPainting());
+    DCHECK_LT(current_range_start_, paint_op_buffer_.size());
+    current_range_start_ = kNotPainting;
+#endif
     if (usage_hint_ == kToBeReleasedAsPaintOpBuffer)
       return;
-    DCHECK_NE(current_range_start_, paint_op_buffer_.size());
-    DCHECK(in_paired_begin_count_);
 
+    DCHECK(begin_paired_indices_.size());
     size_t last_begin_index = begin_paired_indices_.back().first;
     size_t last_begin_count = begin_paired_indices_.back().second;
     DCHECK_GT(last_begin_count, 0u);
@@ -131,8 +141,6 @@
     // The block that ended needs to be included in the bounds of the enclosing
     // block.
     GrowCurrentBeginItemVisualRect(visual_rect);
-
-    in_paired_begin_count_--;
   }
 
   // Called after all items are appended, to process the items.
@@ -183,7 +191,11 @@
 
   // If we're currently within a paired display item block, unions the
   // given visual rect with the begin display item's visual rect.
-  void GrowCurrentBeginItemVisualRect(const gfx::Rect& visual_rect);
+  void GrowCurrentBeginItemVisualRect(const gfx::Rect& visual_rect) {
+    DCHECK_EQ(usage_hint_, kTopLevelDisplayItemList);
+    if (!begin_paired_indices_.empty())
+      visual_rects_[begin_paired_indices_.back().first].Union(visual_rect);
+  }
 
   // RTree stores indices into the paint op buffer.
   // TODO(vmpstr): Update the rtree to store offsets instead.
@@ -202,15 +214,14 @@
   // counts refer to the number of visual rects in that begin sequence that end
   // with the index.
   std::vector<std::pair<size_t, size_t>> begin_paired_indices_;
+
+#if DCHECK_IS_ON()
   // While recording a range of ops, this is the position in the PaintOpBuffer
   // where the recording started.
-  size_t current_range_start_ = 0;
-  // For debugging, tracks the number of currently nested visual rects being
-  // added.
-  int in_paired_begin_count_ = 0;
-  // For debugging, tracks if we're painting a visual rect range, to prevent
-  // nesting.
-  bool in_painting_ = false;
+  bool IsPainting() const { return current_range_start_ != kNotPainting; }
+  const size_t kNotPainting = static_cast<size_t>(-1);
+  size_t current_range_start_ = kNotPainting;
+#endif
 
   UsageHint usage_hint_;
 
diff --git a/cc/resources/resource_pool.cc b/cc/resources/resource_pool.cc
index c12c9550..9a14906 100644
--- a/cc/resources/resource_pool.cc
+++ b/cc/resources/resource_pool.cc
@@ -570,11 +570,11 @@
   base::UnguessableToken shm_guid;
   base::trace_event::MemoryAllocatorDumpGuid backing_guid;
   if (shared_bitmap_) {
-    // Software resources are in shared memory but are kept closed to avoid
-    // holding an fd open. So there is no SharedMemoryHandle to get a guid
-    // from.
-    DCHECK(!shared_bitmap_->GetSharedMemoryHandle().IsValid());
-    backing_guid = viz::GetSharedBitmapGUIDForTracing(shared_bitmap_->id());
+    // If the SharedBitmap was allocated for cross-process use, then it has this
+    // |shm_guid|, else we fallback to the SharedBitmapGUID.
+    shm_guid = shared_bitmap_->GetCrossProcessGUID();
+    if (shm_guid.is_empty())
+      backing_guid = viz::GetSharedBitmapGUIDForTracing(shared_bitmap_->id());
   } else if (gpu_backing_) {
     // We prefer the SharedMemoryGuid() if it exists, if the resource is backed
     // by shared memory.
diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc
index e6eb1222e..4c274867 100644
--- a/cc/resources/resource_provider.cc
+++ b/cc/resources/resource_provider.cc
@@ -207,7 +207,7 @@
         backing_memory_allocated = !!resource.gl_id;
         break;
       case viz::ResourceType::kBitmap:
-        backing_memory_allocated = resource.has_shared_bitmap_id;
+        backing_memory_allocated = !!resource.shared_bitmap;
         break;
     }
 
@@ -236,10 +236,16 @@
     base::UnguessableToken shared_memory_guid;
     switch (resource.type) {
       case viz::ResourceType::kGpuMemoryBuffer:
-        guid =
-            resource.gpu_memory_buffer->GetGUIDForTracing(tracing_process_id);
+        // GpuMemoryBuffers may be backed by shared memory, and in that case we
+        // use the guid from there to attribute for the global shared memory
+        // dumps. Otherwise, they may be backed by native structures, and we
+        // fall back to that with GetGUIDForTracing.
         shared_memory_guid =
             resource.gpu_memory_buffer->GetHandle().handle.GetGUID();
+        if (shared_memory_guid.is_empty()) {
+          guid =
+              resource.gpu_memory_buffer->GetGUIDForTracing(tracing_process_id);
+        }
         break;
       case viz::ResourceType::kTexture:
         DCHECK(resource.gl_id);
@@ -249,24 +255,28 @@
             resource.gl_id);
         break;
       case viz::ResourceType::kBitmap:
-        DCHECK(resource.has_shared_bitmap_id);
-        guid = viz::GetSharedBitmapGUIDForTracing(resource.shared_bitmap_id);
-        if (resource.shared_bitmap) {
-          shared_memory_guid =
-              resource.shared_bitmap->GetSharedMemoryHandle().GetGUID();
-        }
+        // If the resource comes from out of process, it will have this id,
+        // which we prefer. Otherwise, we fall back to the SharedBitmapGUID
+        // which can be generated for in-process bitmaps.
+        shared_memory_guid = resource.shared_bitmap->GetCrossProcessGUID();
+        if (shared_memory_guid.is_empty())
+          guid = viz::GetSharedBitmapGUIDForTracing(resource.shared_bitmap_id);
         break;
     }
 
-    DCHECK(!guid.empty());
+    DCHECK(!shared_memory_guid.is_empty() || !guid.empty());
 
-    const int kImportance = 2;
+    const int kImportanceForInteral = 2;
+    const int kImportanceForExternal = 1;
+    int importance = resource.origin == viz::internal::Resource::INTERNAL
+                         ? kImportanceForInteral
+                         : kImportanceForExternal;
     if (!shared_memory_guid.is_empty()) {
       pmd->CreateSharedMemoryOwnershipEdge(dump->guid(), shared_memory_guid,
-                                           kImportance);
+                                           importance);
     } else {
       pmd->CreateSharedGlobalAllocatorDump(guid);
-      pmd->AddOwnershipEdge(dump->guid(), guid, kImportance);
+      pmd->AddOwnershipEdge(dump->guid(), guid, importance);
     }
   }
 
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc
index f6234675..4815f07 100644
--- a/cc/tiles/tile_manager.cc
+++ b/cc/tiles/tile_manager.cc
@@ -885,14 +885,10 @@
   WhichTree tree = tile->tiling()->tree();
 
   for (const auto* original_draw_image : images_in_tile) {
-    size_t frame_index = original_draw_image->paint_image().frame_index();
-    if (tile_manager_settings_.enable_image_animations) {
-      const auto& image = original_draw_image->paint_image();
-      frame_index = client_->GetFrameIndexForImage(image, tree);
-      if (image_to_frame_index) {
-        (*image_to_frame_index)[image.stable_id()] = frame_index;
-      }
-    }
+    const auto& image = original_draw_image->paint_image();
+    size_t frame_index = client_->GetFrameIndexForImage(image, tree);
+    if (image_to_frame_index)
+      (*image_to_frame_index)[image.stable_id()] = frame_index;
 
     DrawImage draw_image(*original_draw_image, tile->raster_transform().scale(),
                          frame_index, raster_color_space);
@@ -915,11 +911,8 @@
   WhichTree tree = tile->tiling()->tree();
 
   for (const auto* original_draw_image : images_in_tile) {
-    size_t frame_index = original_draw_image->paint_image().frame_index();
-    if (tile_manager_settings_.enable_image_animations) {
-      frame_index = client_->GetFrameIndexForImage(
-          original_draw_image->paint_image(), tree);
-    }
+    size_t frame_index = client_->GetFrameIndexForImage(
+        original_draw_image->paint_image(), tree);
     DrawImage draw_image(*original_draw_image, tile->raster_transform().scale(),
                          frame_index, raster_color_space);
     if (checker_image_tracker_.ShouldCheckerImage(draw_image, tree)) {
diff --git a/cc/tiles/tile_manager_settings.h b/cc/tiles/tile_manager_settings.h
index 7a3d158..6a7f7fb 100644
--- a/cc/tiles/tile_manager_settings.h
+++ b/cc/tiles/tile_manager_settings.h
@@ -13,7 +13,6 @@
   bool use_partial_raster = false;
   bool enable_checker_imaging = false;
   size_t min_image_bytes_to_checker = 1 * 1024 * 1024;
-  bool enable_image_animations = false;
 };
 
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 9319a74..5e013bbb 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -257,6 +257,14 @@
       has_scrolled_by_touch_(false),
       touchpad_and_wheel_scroll_latching_enabled_(false),
       impl_thread_phase_(ImplThreadPhase::IDLE),
+      // It is safe to use base::Unretained here since we will outlive the
+      // ImageAnimationController.
+      image_animation_controller_(
+          GetTaskRunner(),
+          base::BindRepeating(
+              &LayerTreeHostImpl::RequestInvalidationForAnimatedImages,
+              base::Unretained(this)),
+          settings_.enable_image_animation_resync),
       default_color_space_id_(gfx::ColorSpace::GetNextId()),
       default_color_space_(gfx::ColorSpace::CreateSRGB()) {
   DCHECK(mutator_host_);
@@ -283,17 +291,6 @@
       settings.top_controls_hide_threshold);
 
   tile_manager_.SetDecodedImageTracker(&decoded_image_tracker_);
-
-  if (settings_.enable_image_animations) {
-    // It is safe to use base::Unretained here since we will outlive the
-    // ImageAnimationController.
-    base::Closure invalidation_callback =
-        base::Bind(&LayerTreeHostImpl::RequestInvalidationForAnimatedImages,
-                   base::Unretained(this));
-    image_animation_controller_.emplace(
-        GetTaskRunner(), std::move(invalidation_callback),
-        settings_.enable_image_animation_resync);
-  }
 }
 
 LayerTreeHostImpl::~LayerTreeHostImpl() {
@@ -418,13 +415,10 @@
     if (ukm_manager_)
       ukm_manager_->AddCheckerboardedImages(images_to_invalidate.size());
 
-    if (image_animation_controller_.has_value()) {
-      const auto& animated_images =
-          image_animation_controller_.value().AnimateForSyncTree(
-              CurrentBeginFrameArgs().frame_time);
-      images_to_invalidate.insert(animated_images.begin(),
-                                  animated_images.end());
-    }
+    const auto& animated_images =
+        image_animation_controller_.AnimateForSyncTree(
+            CurrentBeginFrameArgs().frame_time);
+    images_to_invalidate.insert(animated_images.begin(), animated_images.end());
     sync_tree()->InvalidateRegionForImages(images_to_invalidate);
   }
 
@@ -501,14 +495,18 @@
 }
 
 void LayerTreeHostImpl::AnimatePendingTreeAfterCommit() {
-  AnimateInternal(false);
+  // Animate the pending tree layer animations to put them at initial positions
+  // and starting state. There is no need to run other animations on pending
+  // tree because they depend on user inputs so the state is identical to what
+  // the active tree has.
+  AnimateLayers(CurrentBeginFrameArgs().frame_time, /* is_active_tree */ false);
 }
 
 void LayerTreeHostImpl::Animate() {
-  AnimateInternal(true);
+  AnimateInternal();
 }
 
-void LayerTreeHostImpl::AnimateInternal(bool active_tree) {
+void LayerTreeHostImpl::AnimateInternal() {
   DCHECK(task_runner_provider_->IsImplThread());
   base::TimeTicks monotonic_time = CurrentBeginFrameArgs().frame_time;
 
@@ -533,19 +531,17 @@
   }
 
   did_animate |= AnimatePageScale(monotonic_time);
-  did_animate |= AnimateLayers(monotonic_time, active_tree);
+  did_animate |= AnimateLayers(monotonic_time, /* is_active_tree */ true);
   did_animate |= AnimateScrollbars(monotonic_time);
   did_animate |= AnimateBrowserControls(monotonic_time);
 
-  if (active_tree) {
     // Animating stuff can change the root scroll offset, so inform the
     // synchronous input handler.
-    UpdateRootLayerStateForSynchronousInputHandler();
-    if (did_animate) {
-      // If the tree changed, then we want to draw at the end of the current
-      // frame.
-      SetNeedsRedraw();
-    }
+  UpdateRootLayerStateForSynchronousInputHandler();
+  if (did_animate) {
+    // If the tree changed, then we want to draw at the end of the current
+    // frame.
+    SetNeedsRedraw();
   }
 }
 
@@ -1524,11 +1520,10 @@
 
 size_t LayerTreeHostImpl::GetFrameIndexForImage(const PaintImage& paint_image,
                                                 WhichTree tree) const {
-  DCHECK(image_animation_controller_.has_value());
   if (!paint_image.ShouldAnimate())
     return paint_image.frame_index();
 
-  return image_animation_controller_->GetFrameIndexForImage(
+  return image_animation_controller_.GetFrameIndexForImage(
       paint_image.stable_id(), tree);
 }
 
@@ -2476,8 +2471,7 @@
 }
 
 void LayerTreeHostImpl::ActivateStateForImages() {
-  if (image_animation_controller_)
-    image_animation_controller_->DidActivate();
+  image_animation_controller_.DidActivate();
   tile_manager_.DidActivateSyncTree();
 }
 
@@ -4818,8 +4812,6 @@
 }
 
 void LayerTreeHostImpl::RequestInvalidationForAnimatedImages() {
-  DCHECK(image_animation_controller_);
-
   // If we are animating an image, we want at least one draw of the active tree
   // before a new tree is activated.
   bool needs_first_draw_on_activation = true;
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 0547246..b5c412e 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -452,9 +452,7 @@
   ResourcePool* resource_pool() { return resource_pool_.get(); }
   ImageDecodeCache* image_decode_cache() { return image_decode_cache_.get(); }
   ImageAnimationController* image_animation_controller() {
-    if (!image_animation_controller_.has_value())
-      return nullptr;
-    return &image_animation_controller_.value();
+    return &image_animation_controller_;
   }
 
   virtual bool WillBeginImplFrame(const viz::BeginFrameArgs& args);
@@ -709,7 +707,7 @@
   void ReleaseTileResources();
   void RecreateTileResources();
 
-  void AnimateInternal(bool active_tree);
+  void AnimateInternal();
 
   // The function is called to update state on the sync tree after a commit
   // finishes or after the sync tree was created to invalidate content on the
@@ -976,7 +974,7 @@
 
   ImplThreadPhase impl_thread_phase_;
 
-  base::Optional<ImageAnimationController> image_animation_controller_;
+  ImageAnimationController image_animation_controller_;
 
   std::unique_ptr<UkmManager> ukm_manager_;
 
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 75c11b8d..f1b0754 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -8483,10 +8483,6 @@
  public:
   void BeginTest() override { PostSetNeedsCommitToMainThread(); }
 
-  void InitializeSettings(LayerTreeSettings* settings) override {
-    settings->enable_image_animations = true;
-  }
-
   void SetupTree() override {
     gfx::Size layer_size(1000, 500);
     content_layer_client_.set_bounds(layer_size);
diff --git a/cc/trees/layer_tree_settings.cc b/cc/trees/layer_tree_settings.cc
index a353e68e..08137e0e 100644
--- a/cc/trees/layer_tree_settings.cc
+++ b/cc/trees/layer_tree_settings.cc
@@ -44,7 +44,6 @@
   tile_manager_settings.use_partial_raster = use_partial_raster;
   tile_manager_settings.enable_checker_imaging = enable_checker_imaging;
   tile_manager_settings.min_image_bytes_to_checker = min_image_bytes_to_checker;
-  tile_manager_settings.enable_image_animations = enable_image_animations;
   return tile_manager_settings;
 }
 
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h
index be4f876..dfbc8a5 100644
--- a/cc/trees/layer_tree_settings.h
+++ b/cc/trees/layer_tree_settings.h
@@ -139,11 +139,8 @@
   // would have been used, out of process gpu raster will be used instead.
   bool enable_oop_rasterization = false;
 
-  // Whether images should be animated in the compositor.
-  bool enable_image_animations = false;
-
   // Whether image animations can be reset to the beginning to avoid skipping
-  // many frames. Only effective if |enable_image_animations| is true.
+  // many frames.
   bool enable_image_animation_resync = true;
 
   // Whether to use edge anti-aliasing for all layer types that supports it.
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 038b34f5..616c44f 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -464,10 +464,12 @@
         <item name="android:alpha">0.6</item>
     </style>
 
-    <!-- First Run and Bookmark/recent-tabs dialogs -->
+    <!-- First Run and Bookmark/recent-tabs dialogs.
+         TODO(https://crbug.com/819142): Remove textAppearance when all TextViews have text style
+         explicitly specified. -->
     <style name="DialogWhenLargeBase" parent="Theme.AppCompat.Light.DialogWhenLarge" >
         <item name="android:windowBackground">@drawable/bg_white_dialog</item>
-        <item name="android:textColor">@color/default_text_color</item>
+        <item name="android:textAppearance">@style/BlackBodyDefault</item>
         <item name="android:textColorLink">@color/light_active_color</item>
         <item name="colorPrimaryDark">@android:color/black</item>
         <item name="colorAccent">@color/light_active_color</item>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
index df293d7..a1636d5a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
@@ -26,6 +26,7 @@
 import org.chromium.components.offline_items_collection.OfflineItem.Progress;
 import org.chromium.components.offline_items_collection.OfflineItemFilter;
 import org.chromium.components.offline_items_collection.OfflineItemState;
+import org.chromium.components.offline_items_collection.VisualsCallback;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.ui.widget.Toast;
 
@@ -177,6 +178,9 @@
     /** @return The file extension type. See list at the top of the file. */
     public abstract int getFileExtensionType();
 
+    /** Requests the backend provider to provide thumbnail for this item. */
+    public abstract void requestVisualsFromProvider(VisualsCallback callback);
+
     /** @return How much of the download has completed, or null if there is no progress. */
     abstract Progress getDownloadProgress();
 
@@ -350,6 +354,9 @@
         }
 
         @Override
+        public void requestVisualsFromProvider(VisualsCallback callback) {}
+
+        @Override
         public Progress getDownloadProgress() {
             return mItem.getDownloadInfo().getProgress();
         }
@@ -559,6 +566,11 @@
         }
 
         @Override
+        public void requestVisualsFromProvider(VisualsCallback callback) {
+            getOfflineContentProvider().getVisualsForItem(mItem.id, callback);
+        }
+
+        @Override
         public Progress getDownloadProgress() {
             return mItem.progress;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java
index c031e247..31ad6fb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java
@@ -31,7 +31,10 @@
 import org.chromium.chrome.browser.widget.ThumbnailProvider;
 import org.chromium.chrome.browser.widget.TintedImageButton;
 import org.chromium.chrome.browser.widget.selection.SelectableItemView;
+import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.OfflineItem.Progress;
+import org.chromium.components.offline_items_collection.OfflineItemVisuals;
+import org.chromium.components.offline_items_collection.VisualsCallback;
 import org.chromium.components.variations.VariationsAssociatedData;
 import org.chromium.ui.UiUtils;
 
@@ -232,7 +235,9 @@
         // immediately if the thumbnail is cached or asynchronously if it has to be fetched from a
         // remote source.
         mThumbnailBitmap = null;
-        if (fileType == DownloadFilter.FILTER_IMAGE && item.isComplete()) {
+        if (item.isOfflinePage()) {
+            requestVisualsFromProvider(item);
+        } else if (fileType == DownloadFilter.FILTER_IMAGE && item.isComplete()) {
             thumbnailProvider.getThumbnail(this);
         } else {
             // TODO(dfalcantara): Get thumbnails for audio and video files when possible.
@@ -304,14 +309,23 @@
         setLongClickable(item.isComplete());
     }
 
-    /**
-     * @param thumbnail The Bitmap to use for the icon ImageView.
-     */
-    public void setThumbnailBitmap(Bitmap thumbnail) {
+    private void setThumbnailBitmap(Bitmap thumbnail) {
         mThumbnailBitmap = thumbnail;
         updateIconView();
     }
 
+    private void requestVisualsFromProvider(DownloadHistoryItemWrapper item) {
+        item.requestVisualsFromProvider(new VisualsCallback() {
+            @Override
+            public void onVisualsAvailable(ContentId id, OfflineItemVisuals visuals) {
+                if (visuals == null) return;
+
+                mThumbnailBitmap = visuals.icon;
+                updateIconView();
+            }
+        });
+    }
+
     @Override
     public void onSelectionStateChange(List<DownloadHistoryItemWrapper> selectedItems) {
         super.onSelectionStateChange(selectedItems);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
index bedf326..381b7b6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
@@ -8,6 +8,7 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
+import org.chromium.base.StrictModeContext;
 import org.chromium.base.SysUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
@@ -98,7 +99,10 @@
     @Nullable
     public static MediaRouter getAndroidMediaRouter() {
         if (sAndroidMediaRouterSetForTest) return sAndroidMediaRouterForTest;
-        try {
+
+        // Some manufacturers have an implementation that causes StrictMode
+        // violations. See https://crbug.com/818325.
+        try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
             // Pre-MR1 versions of JB do not have the complete MediaRouter APIs,
             // so getting the MediaRouter instance will throw an exception.
             return MediaRouter.getInstance(ContextUtils.getApplicationContext());
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index c6a5d7b..f3b71c8f 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -4661,6 +4661,15 @@
   <message name="IDS_ARC_MIGRATE_ENCRYPTION_SUCCESS_MESSAGE" desc="Message of the toast shown on the first sign-in after the successfully completed migration needed for using Android apps.">
     You can now use Android apps.
   </message>
+  <message name="IDS_ARC_USB_PERMISSION_TITLE" desc="Titlebar of Android app USB permissions prompt window">
+    Confirm USB Permission
+  </message>
+  <message name="IDS_ARC_USB_SCAN_DEVICE_LIST_PERMISSION_HEADING" desc="Heading text in the content area of the Android app scan device list permission prompt. Tells the user the app will be able to scan attached USB device list if confirmed.">
+    Allow "<ph name="APP_NAME">$1<ex>Gmail Checker</ex></ph>" to get the list of your attached USB devices?
+  </message>
+  <message name="IDS_ARC_USB_ACCESS_PERMISSION_HEADING" desc="Heading text in the content area of the Android app USB device access permission prompt. Tells the user the app will be able to access the usb device if confirmed.">
+    Allow "<ph name="APP_NAME">$1<ex>Gmail Checker</ex></ph>" to access:
+  </message>
   <message name="IDS_VOICE_INTERACTION_VALUE_PROP_LOADING" desc="Loading message of the voice interaction value prop dialog.">
     Loading Google Assistant...
   </message>
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index ae21985..d05a5f1 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6645,6 +6645,9 @@
         <message name="IDS_CONTROLLED_SETTING_EXTENSION" desc="Text displayed in the controlled settings bubble when a setting's value is enforced by an extension.">
           This setting is enforced by the "<ph name="NAME">$1<ex>Google Cast</ex></ph>" extension.
         </message>
+        <message name="IDS_CONTROLLED_SETTING_EXTENSION_WITHOUT_NAME" desc="Text displayed in the controlled settings bubble when a setting's value is enforced by an extension.">
+          This setting is enforced by an extension.
+        </message>
         <message name="IDS_CONTROLLED_SETTING_RECOMMENDED" desc="Text displayed in the controlled settings bubble when a value is recommended for a setting through policy and the user has not overridden this recommendation.">
           You are following the administrator's recommendation for this setting.
         </message>
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 209adf6..96decee6 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -1265,7 +1265,7 @@
           blocking_task_runner);
   subresource_filter_ruleset_service_->set_ruleset_service(
       std::make_unique<subresource_filter::RulesetService>(
-          local_state(), blocking_task_runner, background_task_runner,
+          local_state(), background_task_runner,
           subresource_filter_ruleset_service_.get(), indexed_ruleset_base_dir));
 }
 
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index e54a72a..bf0b959 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1542,6 +1542,9 @@
     "settings/system_settings_provider.h",
     "settings/token_encryptor.cc",
     "settings/token_encryptor.h",
+    "smb_client/discovery/host_locator.h",
+    "smb_client/discovery/in_memory_host_locator.cc",
+    "smb_client/discovery/in_memory_host_locator.h",
     "smb_client/smb_file_system.cc",
     "smb_client/smb_file_system.h",
     "smb_client/smb_provider.cc",
@@ -2039,6 +2042,7 @@
     "settings/session_manager_operation_unittest.cc",
     "settings/shutdown_policy_handler_unittest.cc",
     "settings/stub_cros_settings_provider_unittest.cc",
+    "smb_client/discovery/in_memory_host_locator_unittest.cc",
     "smb_client/smb_service_unittest.cc",
     "smb_client/temp_file_manager_unittest.cc",
     "system/automatic_reboot_manager_unittest.cc",
diff --git a/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc b/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc
index 2e61c850..1331f99 100644
--- a/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc
+++ b/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc
@@ -36,7 +36,6 @@
 ChromeUserSelectionScreen::ChromeUserSelectionScreen(
     const std::string& display_type)
     : UserSelectionScreen(display_type),
-      handler_initialized_(false),
       weak_factory_(this) {
   device_local_account_policy_service_ =
       g_browser_process->platform_part()
@@ -66,7 +65,7 @@
 
 void ChromeUserSelectionScreen::SendUserList() {
   UserSelectionScreen::SendUserList();
-  handler_initialized_ = true;
+  users_loaded_ = true;
 }
 
 void ChromeUserSelectionScreen::OnPolicyUpdated(const std::string& user_id) {
@@ -95,7 +94,7 @@
 
   public_session_display_names_[account_id] = display_name;
 
-  if (!handler_initialized_)
+  if (!users_loaded_)
     return;
 
   if (!display_name.empty()) {
@@ -166,7 +165,7 @@
 void ChromeUserSelectionScreen::SetPublicSessionLocales(
     const AccountId& account_id,
     const std::vector<std::string>& recommended_locales) {
-  if (!handler_initialized_)
+  if (!users_loaded_)
     return;
 
   // Construct the list of available locales. This list consists of the
diff --git a/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.h b/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.h
index 05e89a2..077eaf2 100644
--- a/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.h
+++ b/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.h
@@ -55,8 +55,6 @@
       const AccountId& account_id,
       const std::vector<std::string>& recommended_locales);
 
-  bool handler_initialized_;
-
   policy::DeviceLocalAccountPolicyService* device_local_account_policy_service_;
 
   // Map from public session account IDs to their display names set by policy.
diff --git a/chrome/browser/chromeos/login/screens/user_selection_screen.cc b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
index fa4ef00..38c369d8 100644
--- a/chrome/browser/chromeos/login/screens/user_selection_screen.cc
+++ b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
@@ -862,6 +862,10 @@
   return user_info_list;
 }
 
+void UserSelectionScreen::SetUsersLoaded(bool loaded) {
+  users_loaded_ = loaded;
+}
+
 EasyUnlockService* UserSelectionScreen::GetEasyUnlockServiceForUser(
     const AccountId& account_id) const {
   if (GetScreenType() == OTHER_SCREEN)
diff --git a/chrome/browser/chromeos/login/screens/user_selection_screen.h b/chrome/browser/chromeos/login/screens/user_selection_screen.h
index fca09d8b..8e9bb812 100644
--- a/chrome/browser/chromeos/login/screens/user_selection_screen.h
+++ b/chrome/browser/chromeos/login/screens/user_selection_screen.h
@@ -127,6 +127,7 @@
 
   std::unique_ptr<base::ListValue> UpdateAndReturnUserListForWebUI();
   std::vector<ash::mojom::LoginUserInfoPtr> UpdateAndReturnUserListForMojo();
+  void SetUsersLoaded(bool loaded);
 
  protected:
   UserBoardView* view_ = nullptr;
@@ -135,6 +136,9 @@
   std::map<AccountId, std::vector<std::string>>
       public_session_recommended_locales_;
 
+  // Whether users have been sent to the UI(WebUI or Views).
+  bool users_loaded_ = false;
+
  private:
   class DircryptoMigrationChecker;
 
diff --git a/chrome/browser/chromeos/login/ui/login_display_views.cc b/chrome/browser/chromeos/login/ui/login_display_views.cc
index b7d83b0..276e85b 100644
--- a/chrome/browser/chromeos/login/ui/login_display_views.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_views.cc
@@ -48,6 +48,7 @@
   user_selection_screen_->Init(filtered_users);
   client->LoadUsers(user_selection_screen_->UpdateAndReturnUserListForMojo(),
                     show_guest);
+  user_selection_screen_->SetUsersLoaded(true /*loaded*/);
 }
 
 void LoginDisplayViews::OnPreferencesChanged() {
diff --git a/chrome/browser/chromeos/login/user_selection_screen_proxy.cc b/chrome/browser/chromeos/login/user_selection_screen_proxy.cc
index 5d177a7..f69beb404 100644
--- a/chrome/browser/chromeos/login/user_selection_screen_proxy.cc
+++ b/chrome/browser/chromeos/login/user_selection_screen_proxy.cc
@@ -67,6 +67,23 @@
 
 UserSelectionScreenProxy::~UserSelectionScreenProxy() = default;
 
+void UserSelectionScreenProxy::SetPublicSessionDisplayName(
+    const AccountId& account_id,
+    const std::string& display_name) {
+  LoginScreenClient::Get()->SetPublicSessionDisplayName(account_id,
+                                                        display_name);
+}
+
+void UserSelectionScreenProxy::SetPublicSessionLocales(
+    const AccountId& account_id,
+    std::unique_ptr<base::ListValue> locales,
+    const std::string& default_locale,
+    bool multiple_recommended_locales) {
+  LoginScreenClient::Get()->SetPublicSessionLocales(
+      account_id, std::move(locales), default_locale,
+      multiple_recommended_locales);
+}
+
 void UserSelectionScreenProxy::ShowUserPodCustomIcon(
     const AccountId& account_id,
     const proximity_auth::ScreenlockBridge::UserPodCustomIconOptions&
diff --git a/chrome/browser/chromeos/login/user_selection_screen_proxy.h b/chrome/browser/chromeos/login/user_selection_screen_proxy.h
index 82ec000..61e42b4 100644
--- a/chrome/browser/chromeos/login/user_selection_screen_proxy.h
+++ b/chrome/browser/chromeos/login/user_selection_screen_proxy.h
@@ -20,11 +20,11 @@
 
   // UserBoardView:
   void SetPublicSessionDisplayName(const AccountId& account_id,
-                                   const std::string& display_name) override{};
+                                   const std::string& display_name) override;
   void SetPublicSessionLocales(const AccountId& account_id,
                                std::unique_ptr<base::ListValue> locales,
                                const std::string& default_locale,
-                               bool multiple_recommended_locales) override{};
+                               bool multiple_recommended_locales) override;
   void ShowBannerMessage(const base::string16& message) override{};
   void ShowUserPodCustomIcon(
       const AccountId& account_id,
diff --git a/chrome/browser/chromeos/smb_client/discovery/host_locator.h b/chrome/browser/chromeos/smb_client/discovery/host_locator.h
new file mode 100644
index 0000000..07cff00
--- /dev/null
+++ b/chrome/browser/chromeos/smb_client/discovery/host_locator.h
@@ -0,0 +1,40 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_SMB_CLIENT_DISCOVERY_HOST_LOCATOR_H_
+#define CHROME_BROWSER_CHROMEOS_SMB_CLIENT_DISCOVERY_HOST_LOCATOR_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+
+namespace chromeos {
+namespace smb_client {
+
+using Hostname = std::string;
+using Address = std::string;
+using HostMap = std::map<Hostname, Address>;
+using FindHostsCallback = base::OnceCallback<void(const HostMap& hosts)>;
+
+// Interface that abstracts the multiple methods of finding SMB hosts in a
+// network. (e.g. mDNS, NetBIOS over TCP, LMHosts, DNS)
+class HostLocator {
+ public:
+  HostLocator() = default;
+  virtual ~HostLocator() = default;
+
+  // Finds hosts in the local network. |callback| will be called once finished
+  // finding all the hosts. If no hosts are found, an empty map will be passed
+  // in the |callback|.
+  virtual void FindHosts(FindHostsCallback callback) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HostLocator);
+};
+
+}  // namespace smb_client
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_SMB_CLIENT_DISCOVERY_HOST_LOCATOR_H_
diff --git a/chrome/browser/chromeos/smb_client/discovery/in_memory_host_locator.cc b/chrome/browser/chromeos/smb_client/discovery/in_memory_host_locator.cc
new file mode 100644
index 0000000..d47f6a0
--- /dev/null
+++ b/chrome/browser/chromeos/smb_client/discovery/in_memory_host_locator.cc
@@ -0,0 +1,36 @@
+// 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/chromeos/smb_client/discovery/in_memory_host_locator.h"
+
+#include <map>
+#include <utility>
+
+namespace chromeos {
+namespace smb_client {
+
+InMemoryHostLocator::InMemoryHostLocator() = default;
+InMemoryHostLocator::~InMemoryHostLocator() = default;
+
+void InMemoryHostLocator::AddHost(const Hostname& hostname,
+                                  const Address& address) {
+  host_map_[hostname] = address;
+}
+
+void InMemoryHostLocator::AddHosts(const HostMap& hosts) {
+  for (const auto& host : hosts) {
+    AddHost(host.first, host.second);
+  }
+}
+
+void InMemoryHostLocator::RemoveHost(const Hostname& hostname) {
+  host_map_.erase(hostname);
+}
+
+void InMemoryHostLocator::FindHosts(FindHostsCallback callback) {
+  std::move(callback).Run(host_map_);
+}
+
+}  // namespace smb_client
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/smb_client/discovery/in_memory_host_locator.h b/chrome/browser/chromeos/smb_client/discovery/in_memory_host_locator.h
new file mode 100644
index 0000000..5b30d28
--- /dev/null
+++ b/chrome/browser/chromeos/smb_client/discovery/in_memory_host_locator.h
@@ -0,0 +1,41 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_SMB_CLIENT_DISCOVERY_IN_MEMORY_HOST_LOCATOR_H_
+#define CHROME_BROWSER_CHROMEOS_SMB_CLIENT_DISCOVERY_IN_MEMORY_HOST_LOCATOR_H_
+
+#include "chrome/browser/chromeos/smb_client/discovery/host_locator.h"
+
+namespace chromeos {
+namespace smb_client {
+
+// HostLocator implementation that uses a map as the source for hosts. New hosts
+// can be registered through AddHost().
+class InMemoryHostLocator : public HostLocator {
+ public:
+  InMemoryHostLocator();
+  ~InMemoryHostLocator() override;
+
+  // Adds host with |hostname| and |address| to host_map_.
+  void AddHost(const Hostname& hostname, const Address& address);
+
+  // Adds |hosts| to host_map_;
+  void AddHosts(const HostMap& hosts);
+
+  // Removes host with |hostname| from host_map_.
+  void RemoveHost(const Hostname& hostname);
+
+  // HostLocator override.
+  void FindHosts(FindHostsCallback callback) override;
+
+ private:
+  HostMap host_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(InMemoryHostLocator);
+};
+
+}  // namespace smb_client
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_SMB_CLIENT_DISCOVERY_IN_MEMORY_HOST_LOCATOR_H_
diff --git a/chrome/browser/chromeos/smb_client/discovery/in_memory_host_locator_unittest.cc b/chrome/browser/chromeos/smb_client/discovery/in_memory_host_locator_unittest.cc
new file mode 100644
index 0000000..aadcace
--- /dev/null
+++ b/chrome/browser/chromeos/smb_client/discovery/in_memory_host_locator_unittest.cc
@@ -0,0 +1,155 @@
+// 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/chromeos/smb_client/discovery/in_memory_host_locator.h"
+
+#include <map>
+#include <string>
+
+#include "base/bind.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace smb_client {
+
+namespace {
+
+// Expects |actual_hosts| to not equal |hosts|.
+void ExpectMapEntriesNotEqual(const HostMap& hosts,
+                              const HostMap& actual_hosts) {
+  EXPECT_NE(hosts, actual_hosts);
+}
+
+// Expects |actual_hosts| to equal |expected_hosts|.
+void ExpectMapEntries(const HostMap& expected_hosts,
+                      const HostMap& actual_hosts) {
+  EXPECT_EQ(expected_hosts, actual_hosts);
+}
+
+}  // namespace
+
+class InMemoryHostLocatorTest : public testing::Test {
+ public:
+  InMemoryHostLocatorTest() = default;
+  ~InMemoryHostLocatorTest() override = default;
+
+ protected:
+  void ExpectHostMapEqual(const HostMap& hosts) {
+    locator_.FindHosts(base::BindOnce(&ExpectMapEntries, hosts));
+  }
+
+  void ExpectHostMapNotEqual(const HostMap& hosts) {
+    locator_.FindHosts(base::BindOnce(&ExpectMapEntriesNotEqual, hosts));
+  }
+
+  InMemoryHostLocator locator_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InMemoryHostLocatorTest);
+};
+
+TEST_F(InMemoryHostLocatorTest, AddHostShouldNotBeEqual) {
+  HostMap incorrect_map;
+  incorrect_map["host1"] = "1.2.3.4";
+
+  // Add a different host entry using AddHost().
+  locator_.AddHost("host2", "5.6.7.8");
+
+  ExpectHostMapNotEqual(incorrect_map);
+}
+
+TEST_F(InMemoryHostLocatorTest, AddHostsShouldNotBeEqual) {
+  HostMap incorrect_map;
+  incorrect_map["host2"] = "6.7.8.9";
+
+  // Add a different host entry using AddHosts().
+  HostMap host_map;
+  host_map["host1"] = "1.2.3.4";
+  locator_.AddHosts(host_map);
+
+  ExpectHostMapNotEqual(incorrect_map);
+}
+
+TEST_F(InMemoryHostLocatorTest, ShouldFindNoHosts) {
+  ExpectHostMapEqual(HostMap());
+}
+
+TEST_F(InMemoryHostLocatorTest, ShouldFindOneHost) {
+  locator_.AddHost("host1", "1.2.3.4");
+
+  HostMap expected;
+  expected["host1"] = "1.2.3.4";
+  ExpectHostMapEqual(expected);
+}
+
+TEST_F(InMemoryHostLocatorTest, ShouldFindMultipleHosts) {
+  HostMap host_map;
+  host_map["host1"] = "1.2.3.4";
+  host_map["host2"] = "3.4.5.6";
+  locator_.AddHosts(host_map);
+
+  ExpectHostMapEqual(host_map);
+}
+
+TEST_F(InMemoryHostLocatorTest, ShouldOverwriteHostWithSameName) {
+  locator_.AddHost("host1", "1.2.3.4");
+  locator_.AddHost("host1", "5.6.7.8");
+
+  HostMap expected;
+  expected["host1"] = "5.6.7.8";
+  ExpectHostMapEqual(expected);
+}
+
+TEST_F(InMemoryHostLocatorTest, ShouldRemoveHost) {
+  HostMap host_map;
+  host_map["host1"] = "1.2.3.4";
+  host_map["host2"] = "3.4.5.6";
+  locator_.AddHosts(host_map);
+
+  ExpectHostMapEqual(host_map);
+
+  // Remove a host.
+  locator_.RemoveHost("host2");
+  ExpectHostMapNotEqual(host_map);
+
+  // The locator should only return the host that was not removed.
+  HostMap expected;
+  expected["host1"] = "1.2.3.4";
+  ExpectHostMapEqual(expected);
+}
+
+TEST_F(InMemoryHostLocatorTest, AddHostsShouldKeepPreviousHosts) {
+  locator_.AddHost("host1", "1.2.3.4");
+
+  HostMap host_map;
+  host_map["host2"] = "5.6.7.8";
+  locator_.AddHosts(host_map);
+
+  HostMap expected;
+  expected["host1"] = "1.2.3.4";
+  expected["host2"] = "5.6.7.8";
+  ExpectHostMapEqual(expected);
+}
+
+TEST_F(InMemoryHostLocatorTest, AddHostsShouldKeepPreviousHostsAndOverwrite) {
+  locator_.AddHost("host1", "1.2.3.4");
+  locator_.AddHost("host2", "5.6.7.8");
+
+  // Add a host with same hostname but different address, along with a new host.
+  HostMap host_map;
+  host_map["host2"] = "15.16.17.18";
+  host_map["host3"] = "25.26.27.28";
+  locator_.AddHosts(host_map);
+
+  // The host with the same name should be overwritten, and the new host
+  // should be added.
+  HostMap expected;
+  expected["host1"] = "1.2.3.4";
+  expected["host2"] = "15.16.17.18";
+  expected["host3"] = "25.26.27.28";
+  ExpectHostMapEqual(expected);
+}
+
+}  // namespace smb_client
+}  // namespace chromeos
diff --git a/chrome/browser/component_updater/subresource_filter_component_installer_unittest.cc b/chrome/browser/component_updater/subresource_filter_component_installer_unittest.cc
index a1bf387..6502a9a 100644
--- a/chrome/browser/component_updater/subresource_filter_component_installer_unittest.cc
+++ b/chrome/browser/component_updater/subresource_filter_component_installer_unittest.cc
@@ -44,7 +44,6 @@
                      const base::FilePath& base_dir)
       : subresource_filter::RulesetService(local_state,
                                            task_runner,
-                                           task_runner,
                                            content_service,
                                            base_dir) {}
 
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
index 5070bed..fd8a929 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -427,6 +427,23 @@
       new Event(events::DEVELOPER_PRIVATE_ON_PROFILE_STATE_CHANGED,
                 developer::OnProfileStateChanged::kEventName, std::move(args)));
   event_router_->BroadcastEvent(std::move(event));
+
+  // The following properties are updated when dev mode is toggled.
+  //   - error_collection.is_enabled
+  //   - error_collection.is_active
+  //   - runtime_errors
+  //   - manifest_errors
+  //   - install_warnings
+  // An alternative approach would be to factor out the dev mode state from the
+  // above properties and allow the UI control what happens when dev mode
+  // changes. If the UI rendering performance is an issue, instead of replacing
+  // the entire extension info, a diff of the old and new extension info can be
+  // made by the UI and only perform a partial update of the extension info.
+  const ExtensionSet& extensions =
+      ExtensionRegistry::Get(profile_)->enabled_extensions();
+  for (const auto& extension : extensions)
+    BroadcastItemStateChanged(developer::EVENT_TYPE_PREFS_CHANGED,
+                              extension->id());
 }
 
 void DeveloperPrivateEventRouter::BroadcastItemStateChanged(
diff --git a/chrome/browser/extensions/dev_mode_bubble_delegate.cc b/chrome/browser/extensions/dev_mode_bubble_delegate.cc
index 1b0f3a5..17fcc49 100644
--- a/chrome/browser/extensions/dev_mode_bubble_delegate.cc
+++ b/chrome/browser/extensions/dev_mode_bubble_delegate.cc
@@ -95,13 +95,13 @@
 bool DevModeBubbleDelegate::ShouldShow(
     const ExtensionIdList& extensions) const {
   DCHECK_LE(1u, extensions.size());
-  return !g_shown.Get().count(profile_);
+  return !g_shown.Get().count(profile_->GetOriginalProfile());
 }
 
 void DevModeBubbleDelegate::OnShown(const ExtensionIdList& extensions) {
   DCHECK_LE(1u, extensions.size());
   DCHECK(!g_shown.Get().count(profile_));
-  g_shown.Get().insert(profile_);
+  g_shown.Get().insert(profile_->GetOriginalProfile());
 }
 
 void DevModeBubbleDelegate::OnAction() {}
diff --git a/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc b/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
index 3b4bde38..8a69866 100644
--- a/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
+++ b/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
@@ -696,6 +696,75 @@
   EXPECT_EQ(0U, dev_mode_extensions.size());
 }
 
+// Test that if we show the dev mode bubble for the regular profile, we won't
+// show it for its incognito profile.
+// Regression test for crbug.com/819309.
+TEST_F(ExtensionMessageBubbleTest, ShowDevModeBubbleOncePerOriginalProfile) {
+  FeatureSwitch::ScopedOverride force_dev_mode_highlighting(
+      FeatureSwitch::force_dev_mode_highlighting(), true);
+  Init();
+
+  ASSERT_TRUE(LoadGenericExtension("1", kId1, Manifest::UNPACKED));
+
+  auto get_controller = [](Browser* browser) {
+    auto controller = std::make_unique<TestExtensionMessageBubbleController>(
+        new DevModeBubbleDelegate(browser->profile()), browser);
+    controller->SetIsActiveBubble();
+    return controller;
+  };
+
+  {
+    // Show the bubble for the regular profile, and dismiss it.
+    auto controller = get_controller(browser());
+    EXPECT_TRUE(controller->ShouldShow());
+    FakeExtensionMessageBubble bubble;
+    bubble.set_action_on_show(
+        FakeExtensionMessageBubble::BUBBLE_ACTION_CLICK_DISMISS_BUTTON);
+    bubble.set_controller(controller.get());
+    bubble.Show();
+  }
+
+  {
+    // The bubble shouldn't want to show twice for the same profile.
+    auto controller = get_controller(browser());
+    EXPECT_FALSE(controller->ShouldShow());
+  }
+
+  {
+    // Construct an off-the-record profile and browser.
+    Profile* off_the_record_profile = profile()->GetOffTheRecordProfile();
+
+    ToolbarActionsModelFactory::GetInstance()->SetTestingFactory(
+        off_the_record_profile, &BuildToolbarModel);
+
+    std::unique_ptr<BrowserWindow> off_the_record_window(CreateBrowserWindow());
+    std::unique_ptr<Browser> off_the_record_browser(
+        CreateBrowser(off_the_record_profile, Browser::TYPE_TABBED, false,
+                      off_the_record_window.get()));
+
+    // The bubble shouldn't want to show for an incognito version of the same
+    // profile.
+    auto controller = get_controller(browser());
+    EXPECT_FALSE(controller->ShouldShow());
+
+    // Now, try the inverse - show the bubble for the incognito profile, and
+    // dismiss it.
+    controller->delegate()->ClearProfileSetForTesting();
+    EXPECT_TRUE(controller->ShouldShow());
+    FakeExtensionMessageBubble bubble;
+    bubble.set_action_on_show(
+        FakeExtensionMessageBubble::BUBBLE_ACTION_CLICK_DISMISS_BUTTON);
+    bubble.set_controller(controller.get());
+    bubble.Show();
+  }
+
+  {
+    // The bubble shouldn't want to show for the regular profile.
+    auto controller = get_controller(browser());
+    EXPECT_FALSE(controller->ShouldShow());
+  }
+}
+
 // The feature this is meant to test is only implemented on Windows and Mac.
 #if defined(OS_WIN) || defined(OS_MACOSX)
 #define MAYBE_SettingsApiControllerTest SettingsApiControllerTest
diff --git a/chrome/browser/media/encrypted_media_browsertest.cc b/chrome/browser/media/encrypted_media_browsertest.cc
index f5bd68b..3cbaf94c 100644
--- a/chrome/browser/media/encrypted_media_browsertest.cc
+++ b/chrome/browser/media/encrypted_media_browsertest.cc
@@ -115,9 +115,6 @@
 // The type of video src used to load media.
 enum class SrcType { SRC, MSE };
 
-// How the CDM is hosted, using pepper or mojo.
-enum class CdmHostType { kPepper, kMojo };
-
 // Must be in sync with CONFIG_CHANGE_TYPE in eme_player_js/global.js
 enum class ConfigChangeType {
   CLEAR_TO_CLEAR = 0,
@@ -157,7 +154,6 @@
                                  const std::string& key_system,
                                  const base::StringPairs& query_params,
                                  const std::string& expected_title) {
-    DVLOG(1) << "Mojo CDM " << (IsUsingMojoCdm() ? "" : " not") << " enabled.";
     base::StringPairs new_query_params = query_params;
     StartLicenseServerIfNeeded(key_system, &new_query_params);
     RunMediaTestPage(html_page, new_query_params, expected_title, true);
@@ -282,10 +278,6 @@
         switches::autoplay::kNoUserGestureRequiredPolicy);
     command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
                                     "EncryptedMediaHdcpPolicyCheck");
-    // The test covers both mojo CDM and pepper CDM, so we need to disable the
-    // field trial testing config, which might enable mojo CDM unexpectedly.
-    command_line->AppendSwitch(
-        variations::switches::kDisableFieldTrialTestingConfig);
   }
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
@@ -298,7 +290,6 @@
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
   void SetUpCommandLineForKeySystem(const std::string& key_system,
-                                    CdmHostType cdm_host_type,
                                     bool support_experimental_cdm_interface,
                                     base::CommandLine* command_line) {
     if (GetServerConfig(key_system))
@@ -313,7 +304,6 @@
 
       // TODO(xhwang): Update ScopedFeatureList::InitWithFeatures() to accept
       // vectors so that we can simplify this block.
-      if (cdm_host_type == CdmHostType::kMojo) {
         if (support_experimental_cdm_interface) {
           scoped_feature_list_.InitWithFeatures(
               {media::kExternalClearKeyForTesting,
@@ -323,26 +313,8 @@
           scoped_feature_list_.InitWithFeatures(
               {media::kExternalClearKeyForTesting}, {});
         }
-      } else {
-        // Pepper CDM does not support any experimental CDM interface.
-        scoped_feature_list_.InitWithFeatures(
-            {media::kExternalClearKeyForTesting}, {media::kMojoCdm});
-      }
-    } else {
-      // Experimental CDM interface is only supported with External Clear Key.
-      if (cdm_host_type != CdmHostType::kMojo) {
-        scoped_feature_list_.InitWithFeatures({}, {media::kMojoCdm});
-      }
     }
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
-
-    // Make sure we actually use MojoCdm iff host type is kMojo.
-    DCHECK_EQ(cdm_host_type == CdmHostType::kMojo, IsUsingMojoCdm());
-  }
-
-  // Check whether the test is actually using mojo CDM.
-  bool IsUsingMojoCdm() const {
-    return base::FeatureList::IsEnabled(media::kMojoCdm);
   }
 
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -351,17 +323,14 @@
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
 // Tests encrypted media playback using ExternalClearKey key system in
 // decrypt-and-decode mode.
-class ECKEncryptedMediaTest : public EncryptedMediaTestBase,
-                              public testing::WithParamInterface<CdmHostType> {
+class ECKEncryptedMediaTest : public EncryptedMediaTestBase {
  public:
   // We use special |key_system| names to do non-playback related tests,
   // e.g. kExternalClearKeyFileIOTestKeySystem is used to test file IO.
   void TestNonPlaybackCases(const std::string& key_system,
                             const std::string& expected_title) {
-    // When mojo CDM is used, make sure the Clear Key CDM is properly registered
-    // in CdmRegistry.
-    if (IsUsingMojoCdm())
-      EXPECT_TRUE(IsLibraryCdmRegistered(media::kClearKeyCdmGuid));
+    // Make sure the Clear Key CDM is properly registered in CdmRegistry.
+    EXPECT_TRUE(IsLibraryCdmRegistered(media::kClearKeyCdmGuid));
 
     // Since we do not test playback, arbitrarily choose a test file and source
     // type.
@@ -383,14 +352,13 @@
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     EncryptedMediaTestBase::SetUpCommandLine(command_line);
-    SetUpCommandLineForKeySystem(kExternalClearKeyKeySystem, GetParam(), false,
+    SetUpCommandLineForKeySystem(kExternalClearKeyKeySystem, false,
                                  command_line);
   }
 };
 
 // Tests encrypted media playback using experimental CDM interface. Note that
-// experimental CDM interface is only supported by ExternalClearKey key system
-// using mojo CDM.
+// experimental CDM interface is only supported by ExternalClearKey key system.
 class EncryptedMediaTestExperimentalCdmInterface
     : public EncryptedMediaTestBase {
  public:
@@ -420,8 +388,8 @@
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     EncryptedMediaTestBase::SetUpCommandLine(command_line);
-    SetUpCommandLineForKeySystem(kExternalClearKeyKeySystem, CdmHostType::kMojo,
-                                 true, command_line);
+    SetUpCommandLineForKeySystem(kExternalClearKeyKeySystem, true,
+                                 command_line);
   }
 };
 
@@ -434,10 +402,9 @@
 // Note: Only parameterized (*_P) tests can be used. Non-parameterized (*_F)
 // tests will crash at GetParam(). To add non-parameterized tests, use
 // EncryptedMediaTestBase or one of its subclasses (e.g. WVEncryptedMediaTest).
-class EncryptedMediaTest
-    : public EncryptedMediaTestBase,
-      public testing::WithParamInterface<
-          std::tr1::tuple<const char*, SrcType, CdmHostType>> {
+class EncryptedMediaTest : public EncryptedMediaTestBase,
+                           public testing::WithParamInterface<
+                               std::tr1::tuple<const char*, SrcType>> {
  public:
   std::string CurrentKeySystem() {
     return std::tr1::get<0>(GetParam());
@@ -447,8 +414,6 @@
     return std::tr1::get<1>(GetParam());
   }
 
-  CdmHostType CurrentCdmHostType() { return std::tr1::get<2>(GetParam()); }
-
   void TestSimplePlayback(const std::string& encrypted_media,
                           const std::string& media_type) {
     RunSimpleEncryptedMediaTest(encrypted_media, media_type, CurrentKeySystem(),
@@ -549,8 +514,7 @@
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     EncryptedMediaTestBase::SetUpCommandLine(command_line);
-    SetUpCommandLineForKeySystem(CurrentKeySystem(), CurrentCdmHostType(),
-                                 false, command_line);
+    SetUpCommandLineForKeySystem(CurrentKeySystem(), false, command_line);
   }
 };
 
@@ -560,36 +524,26 @@
 INSTANTIATE_TEST_CASE_P(MSE_ClearKey,
                         EncryptedMediaTest,
                         Combine(Values(kClearKeyKeySystem),
-                                Values(SrcType::MSE),
-                                Values(CdmHostType::kPepper)));
+                                Values(SrcType::MSE)));
 
-// External Clear Key is currently only used on platforms that use Pepper CDMs.
+// External Clear Key is currently only used on platforms that use library CDMs.
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
 INSTANTIATE_TEST_CASE_P(SRC_ExternalClearKey,
                         EncryptedMediaTest,
                         Combine(Values(kExternalClearKeyKeySystem),
-                                Values(SrcType::SRC),
-                                Values(CdmHostType::kPepper)));
+                                Values(SrcType::SRC)));
 
 INSTANTIATE_TEST_CASE_P(MSE_ExternalClearKey,
                         EncryptedMediaTest,
                         Combine(Values(kExternalClearKeyKeySystem),
-                                Values(SrcType::MSE),
-                                Values(CdmHostType::kPepper)));
-
-INSTANTIATE_TEST_CASE_P(MSE_ExternalClearKey_Mojo,
-                        EncryptedMediaTest,
-                        Combine(Values(kExternalClearKeyKeySystem),
-                                Values(SrcType::MSE),
-                                Values(CdmHostType::kMojo)));
+                                Values(SrcType::MSE)));
 #else   // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 // To reduce test time, only run ClearKey SRC tests when we are not running
 // ExternalClearKey SRC tests.
 INSTANTIATE_TEST_CASE_P(SRC_ClearKey,
                         EncryptedMediaTest,
                         Combine(Values(kClearKeyKeySystem),
-                                Values(SrcType::SRC),
-                                Values(CdmHostType::kPepper)));
+                                Values(SrcType::SRC)));
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
 #if defined(WIDEVINE_CDM_AVAILABLE)
@@ -597,14 +551,7 @@
 INSTANTIATE_TEST_CASE_P(MSE_Widevine,
                         EncryptedMediaTest,
                         Combine(Values(kWidevineKeySystem),
-                                Values(SrcType::MSE),
-                                Values(CdmHostType::kPepper)));
-
-INSTANTIATE_TEST_CASE_P(MSE_Widevine_Mojo,
-                        EncryptedMediaTest,
-                        Combine(Values(kWidevineKeySystem),
-                                Values(SrcType::MSE),
-                                Values(CdmHostType::kMojo)));
+                                Values(SrcType::MSE)));
 #endif  // !defined(OS_CHROMEOS)
 #endif  // defined(WIDEVINE_CDM_AVAILABLE)
 
@@ -763,55 +710,31 @@
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
-INSTANTIATE_TEST_CASE_P(Pepper,
-                        ECKEncryptedMediaTest,
-                        Values(CdmHostType::kPepper));
-
-INSTANTIATE_TEST_CASE_P(Mojo,
-                        ECKEncryptedMediaTest,
-                        Values(CdmHostType::kMojo));
-
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, InitializeCDMFail) {
+IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, InitializeCDMFail) {
   TestNonPlaybackCases(kExternalClearKeyInitializeFailKeySystem,
                        kEmeNotSupportedError);
 }
 
 // When CDM crashes, we should still get a decode error and all sessions should
 // be closed.
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, CDMCrashDuringDecode) {
+IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, CDMCrashDuringDecode) {
   IgnorePluginCrash();
   TestNonPlaybackCases(kExternalClearKeyCrashKeySystem,
                        kEmeSessionClosedAndError);
 }
 
-// Testing that the media browser test does fail on CDM crash.
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, CDMExpectedCrash) {
-  // PluginCrashed() is only called when the CDM is running as a plugin.
-  if (IsUsingMojoCdm()) {
-    DVLOG(0) << "Skipping test; Pepper CDM specific.";
-    return;
-  }
-
-  // CDM crash is not ignored by default, the test is expected to fail.
-  EXPECT_NONFATAL_FAILURE(TestNonPlaybackCases(kExternalClearKeyCrashKeySystem,
-                                               kEmeSessionClosedAndError),
-                          "Failing test due to plugin crash.");
-}
-
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, FileIOTest) {
+IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, FileIOTest) {
   TestNonPlaybackCases(kExternalClearKeyFileIOTestKeySystem, kUnitTestSuccess);
 }
 
 // TODO(xhwang): Investigate how to fake capturing activities to test the
 // network link detection logic in OutputProtectionProxy.
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, OutputProtectionTest) {
+IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, OutputProtectionTest) {
   TestNonPlaybackCases(kExternalClearKeyOutputProtectionTestKeySystem,
                        kUnitTestSuccess);
 }
 
-// TODO(xhwang): Update this test to cover mojo PlatformVerification service
-// on ChromeOS. See http://crbug.com/479836
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, PlatformVerificationTest) {
+IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, PlatformVerificationTest) {
   TestNonPlaybackCases(kExternalClearKeyPlatformVerificationTestKeySystem,
                        kUnitTestSuccess);
 }
@@ -822,7 +745,7 @@
 #else
 #define MAYBE_MessageTypeTest MessageTypeTest
 #endif
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, MAYBE_MessageTypeTest) {
+IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, MAYBE_MessageTypeTest) {
   TestPlaybackCase(kExternalClearKeyMessageTypeTestKeySystem, kNoSessionToLoad,
                    media::kEnded);
 
@@ -837,11 +760,11 @@
   EXPECT_EQ(2, num_received_message_types);
 }
 
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, LoadLoadableSession) {
+IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, LoadLoadableSession) {
   TestPlaybackCase(kExternalClearKeyKeySystem, kLoadableSession, media::kEnded);
 }
 
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, LoadUnknownSession) {
+IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, LoadUnknownSession) {
   TestPlaybackCase(kExternalClearKeyKeySystem, kUnknownSession,
                    kEmeSessionNotFound);
 }
@@ -849,14 +772,14 @@
 const char kExternalClearKeyDecryptOnlyKeySystem[] =
     "org.chromium.externalclearkey.decryptonly";
 
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, DecryptOnly_VideoAudio_WebM) {
+IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, DecryptOnly_VideoAudio_WebM) {
   RunSimpleEncryptedMediaTest(
       "bear-320x240-av_enc-av.webm", kWebMVorbisAudioVP8Video,
       kExternalClearKeyDecryptOnlyKeySystem, SrcType::MSE);
 }
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, DecryptOnly_VideoOnly_MP4_VP9) {
+IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, DecryptOnly_VideoOnly_MP4_VP9) {
   RunSimpleEncryptedMediaTest(
       "bear-320x240-v_frag-vp9-cenc.mp4", kMP4VideoVp9Only,
       kExternalClearKeyDecryptOnlyKeySystem, SrcType::MSE);
@@ -864,23 +787,18 @@
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 #if BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION)
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, VerifyCdmHostTest) {
+IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, VerifyCdmHostTest) {
   TestNonPlaybackCases(kExternalClearKeyVerifyCdmHostTestKeySystem,
                        kUnitTestSuccess);
 }
 #endif  // BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION)
 
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, StorageIdTest) {
+IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, StorageIdTest) {
   TestNonPlaybackCases(kExternalClearKeyStorageIdTestKeySystem,
                        kUnitTestSuccess);
 }
 
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, MultipleCdmTypes) {
-  if (!IsUsingMojoCdm()) {
-    DVLOG(0) << "Skipping test; Mojo CDM specific.";
-    return;
-  }
-
+IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, MultipleCdmTypes) {
   base::StringPairs empty_query_params;
   RunMediaTestPage("multiple_cdm_types.html", empty_query_params, media::kEnded,
                    true);
diff --git a/chrome/browser/media/router/BUILD.gn b/chrome/browser/media/router/BUILD.gn
index e1d59c72..49814a8 100644
--- a/chrome/browser/media/router/BUILD.gn
+++ b/chrome/browser/media/router/BUILD.gn
@@ -81,10 +81,20 @@
       "mojo/media_router_mojo_impl.h",
       "mojo/media_router_mojo_metrics.cc",
       "mojo/media_router_mojo_metrics.h",
+      "mojo/media_sink_service_status.cc",
+      "mojo/media_sink_service_status.h",
       "presentation/independent_otr_profile_manager.cc",
       "presentation/independent_otr_profile_manager.h",
       "presentation/presentation_navigation_policy.cc",
       "presentation/presentation_navigation_policy.h",
+      "providers/cast/cast_app_availability_tracker.cc",
+      "providers/cast/cast_app_availability_tracker.h",
+      "providers/cast/cast_app_discovery_service.cc",
+      "providers/cast/cast_app_discovery_service.h",
+      "providers/cast/cast_media_route_provider.cc",
+      "providers/cast/cast_media_route_provider.h",
+      "providers/cast/chrome_cast_message_handler.cc",
+      "providers/cast/chrome_cast_message_handler.h",
       "providers/cast/dual_media_sink_service.cc",
       "providers/cast/dual_media_sink_service.h",
       "providers/extension/extension_media_route_provider_proxy.cc",
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service.cc b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service.cc
index b044847..77a2f6d 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service.cc
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service.cc
@@ -10,9 +10,6 @@
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "chrome/browser/media/router/discovery/discovery_network_monitor.h"
-#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
-#include "chrome/browser/media/router/discovery/mdns/dns_sd_delegate.h"
-#include "chrome/browser/media/router/discovery/mdns/dns_sd_registry.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/media_router/media_sink.h"
 #include "components/cast_channel/cast_socket_service.h"
@@ -68,7 +65,7 @@
   std::string processed_uuid = MediaSinkInternal::ProcessDeviceUUID(unique_id);
   std::string sink_id = base::StringPrintf("cast:<%s>", processed_uuid.c_str());
   MediaSink sink(sink_id, friendly_name, SinkIconType::CAST,
-                 MediaRouteProviderId::EXTENSION);
+                 MediaRouteProviderId::CAST);
 
   CastSinkExtraData extra_data;
   extra_data.ip_endpoint =
@@ -105,19 +102,25 @@
 }
 
 void CastMediaSinkService::Start(
-    const OnSinksDiscoveredCallback& sinks_discovered_cb) {
+    const OnSinksDiscoveredCallback& sinks_discovered_cb,
+    CastMediaSinkServiceImpl::Observer* observer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!impl_);
 
   // |sinks_discovered_cb| should only be invoked on the current sequence.
   // We wrap |sinks_discovered_cb| in a member function bound with WeakPtr to
   // ensure it will only be invoked while |this| is still valid.
-  impl_ = CreateImpl(base::BindRepeating(
-      &RunSinksDiscoveredCallbackOnSequence,
-      base::SequencedTaskRunnerHandle::Get(),
-      base::BindRepeating(&CastMediaSinkService::RunSinksDiscoveredCallback,
-                          weak_ptr_factory_.GetWeakPtr(),
-                          sinks_discovered_cb)));
+  // TODO(imcheng): Simplify this by only using observers instead of callback.
+  // This would require us to move the timer logic from MediaSinkServiceBase up
+  // to DualMediaSinkService, but will allow us to remove MediaSinkServiceBase.
+  impl_ =
+      CreateImpl(base::BindRepeating(
+                     &RunSinksDiscoveredCallbackOnSequence,
+                     base::SequencedTaskRunnerHandle::Get(),
+                     base::BindRepeating(
+                         &CastMediaSinkService::RunSinksDiscoveredCallback,
+                         weak_ptr_factory_.GetWeakPtr(), sinks_discovered_cb)),
+                 observer);
   impl_->task_runner()->PostTask(
       FROM_HERE, base::BindOnce(&CastMediaSinkServiceImpl::Start,
                                 base::Unretained(impl_.get())));
@@ -129,13 +132,15 @@
 
 std::unique_ptr<CastMediaSinkServiceImpl, base::OnTaskRunnerDeleter>
 CastMediaSinkService::CreateImpl(
-    const OnSinksDiscoveredCallback& sinks_discovered_cb) {
+    const OnSinksDiscoveredCallback& sinks_discovered_cb,
+    CastMediaSinkServiceImpl::Observer* observer) {
   cast_channel::CastSocketService* cast_socket_service =
       cast_channel::CastSocketService::GetInstance();
   scoped_refptr<base::SequencedTaskRunner> task_runner =
       cast_socket_service->task_runner();
   return std::unique_ptr<CastMediaSinkServiceImpl, base::OnTaskRunnerDeleter>(
-      new CastMediaSinkServiceImpl(sinks_discovered_cb, cast_socket_service,
+      new CastMediaSinkServiceImpl(sinks_discovered_cb, observer,
+                                   cast_socket_service,
                                    DiscoveryNetworkMonitor::GetInstance()),
       base::OnTaskRunnerDeleter(task_runner));
 }
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h
index cf854f4..886f6fcf 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h
@@ -13,6 +13,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
 #include "chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.h"
+#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
 #include "chrome/browser/media/router/discovery/mdns/dns_sd_delegate.h"
 #include "chrome/browser/media/router/discovery/mdns/dns_sd_registry.h"
 #include "chrome/common/media_router/discovery/media_sink_internal.h"
@@ -20,8 +21,6 @@
 
 namespace media_router {
 
-class CastMediaSinkServiceImpl;
-
 // A service which can be used to start background discovery and resolution of
 // Cast devices.
 // This class is not thread safe. All methods must be invoked on the UI thread.
@@ -42,8 +41,11 @@
   // Starts Cast sink discovery. No-ops if already started.
   // |sink_discovery_cb|: Callback to invoke when the list of discovered sinks
   // has been updated.
+  // |observer|: Observer passed to |impl_|. Note that unlike the callback, the
+  // observer will be invoked on the sequence |impl_| runs on. Can be nullptr.
   // Marked virtual for tests.
-  virtual void Start(const OnSinksDiscoveredCallback& sinks_discovered_cb);
+  virtual void Start(const OnSinksDiscoveredCallback& sinks_discovered_cb,
+                     CastMediaSinkServiceImpl::Observer* observer);
 
   // Initiates discovery immediately in response to a user gesture
   // (i.e., opening the Media Router dialog).
@@ -53,7 +55,8 @@
 
   // Marked virtual for tests.
   virtual std::unique_ptr<CastMediaSinkServiceImpl, base::OnTaskRunnerDeleter>
-  CreateImpl(const OnSinksDiscoveredCallback& sinks_discovered_cb);
+  CreateImpl(const OnSinksDiscoveredCallback& sinks_discovered_cb,
+             CastMediaSinkServiceImpl::Observer* observer);
 
   // Registers with DnsSdRegistry to listen for Cast devices. Note that this is
   // called on |Start()| on all platforms except for Windows. On Windows, this
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc
index 66dc7373..7c733e0 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc
@@ -19,49 +19,54 @@
 #include "net/base/backoff_entry.h"
 #include "net/base/net_errors.h"
 
+namespace media_router {
+
 namespace {
 
-media_router::MediaSinkInternal CreateCastSinkFromDialSink(
-    const media_router::MediaSinkInternal& dial_sink) {
-  const std::string& unique_id = dial_sink.sink().id();
+MediaSinkInternal CreateCastSinkFromDialSink(
+    const MediaSinkInternal& dial_sink) {
+  const std::string& dial_sink_id = dial_sink.sink().id();
   const std::string& friendly_name = dial_sink.sink().name();
-  media_router::MediaSink sink(unique_id, friendly_name,
-                               media_router::SinkIconType::CAST,
-                               media_router::MediaRouteProviderId::EXTENSION);
+  DCHECK_EQ("dial:", dial_sink_id.substr(0, 5))
+      << "unexpected DIAL sink id " << dial_sink_id;
 
-  media_router::CastSinkExtraData extra_data;
+  // Replace the "dial:" prefix with "cast:".
+  std::string sink_id = "cast:" + dial_sink_id.substr(5);
+  MediaSink sink(sink_id, friendly_name, SinkIconType::CAST,
+                 MediaRouteProviderId::CAST);
+
+  CastSinkExtraData extra_data;
   extra_data.ip_endpoint =
       net::IPEndPoint(dial_sink.dial_data().ip_address,
-                      media_router::CastMediaSinkServiceImpl::kCastControlPort);
+                      CastMediaSinkServiceImpl::kCastControlPort);
   extra_data.model_name = dial_sink.dial_data().model_name;
   extra_data.discovered_by_dial = true;
   extra_data.capabilities = cast_channel::CastDeviceCapability::NONE;
 
-  return media_router::MediaSinkInternal(sink, extra_data);
+  return MediaSinkInternal(sink, extra_data);
 }
 
 void RecordError(cast_channel::ChannelError channel_error,
                  cast_channel::LastError last_error) {
-  media_router::MediaRouterChannelError error_code =
-      media_router::MediaRouterChannelError::UNKNOWN;
+  MediaRouterChannelError error_code = MediaRouterChannelError::UNKNOWN;
 
   switch (channel_error) {
     // TODO(crbug.com/767204): Add in errors for transient socket and timeout
     // errors, but only after X number of occurences.
     case cast_channel::ChannelError::UNKNOWN:
-      error_code = media_router::MediaRouterChannelError::UNKNOWN;
+      error_code = MediaRouterChannelError::UNKNOWN;
       break;
     case cast_channel::ChannelError::AUTHENTICATION_ERROR:
-      error_code = media_router::MediaRouterChannelError::AUTHENTICATION;
+      error_code = MediaRouterChannelError::AUTHENTICATION;
       break;
     case cast_channel::ChannelError::CONNECT_ERROR:
-      error_code = media_router::MediaRouterChannelError::CONNECT;
+      error_code = MediaRouterChannelError::CONNECT;
       break;
     case cast_channel::ChannelError::CONNECT_TIMEOUT:
-      error_code = media_router::MediaRouterChannelError::CONNECT_TIMEOUT;
+      error_code = MediaRouterChannelError::CONNECT_TIMEOUT;
       break;
     case cast_channel::ChannelError::PING_TIMEOUT:
-      error_code = media_router::MediaRouterChannelError::PING_TIMEOUT;
+      error_code = MediaRouterChannelError::PING_TIMEOUT;
       break;
     default:
       // Do nothing and let the standard launch failure issue surface.
@@ -90,19 +95,19 @@
           cast_channel::ChannelEvent::SEND_AUTH_CHALLENGE_FAILED ||
       last_error.channel_event ==
           cast_channel::ChannelEvent::AUTH_CHALLENGE_REPLY_INVALID) {
-    error_code = media_router::MediaRouterChannelError::GENERAL_CERTIFICATE;
+    error_code = MediaRouterChannelError::GENERAL_CERTIFICATE;
   }
 
   // Certificate timing errors
   if (last_error.channel_event ==
           cast_channel::ChannelEvent::SSL_CERT_EXCESSIVE_LIFETIME ||
       last_error.net_return_value == net::ERR_CERT_DATE_INVALID) {
-    error_code = media_router::MediaRouterChannelError::CERTIFICATE_TIMING;
+    error_code = MediaRouterChannelError::CERTIFICATE_TIMING;
   }
 
   // Network/firewall access denied
   if (last_error.net_return_value == net::ERR_NETWORK_ACCESS_DENIED) {
-    error_code = media_router::MediaRouterChannelError::NETWORK;
+    error_code = MediaRouterChannelError::NETWORK;
   }
 
   // Authentication errors (assumed active ssl manipulation)
@@ -110,10 +115,10 @@
           cast_channel::ChallengeReplyError::CERT_NOT_SIGNED_BY_TRUSTED_CA ||
       last_error.challenge_reply_error ==
           cast_channel::ChallengeReplyError::SIGNED_BLOBS_MISMATCH) {
-    error_code = media_router::MediaRouterChannelError::AUTHENTICATION;
+    error_code = MediaRouterChannelError::AUTHENTICATION;
   }
 
-  media_router::CastAnalytics::RecordDeviceChannelError(error_code);
+  CastAnalytics::RecordDeviceChannelError(error_code);
 }
 
 // Parameter names.
@@ -147,12 +152,6 @@
 // Max failure count allowed for a Cast channel.
 constexpr int kMaxFailureCount = 100;
 
-}  // namespace
-
-namespace media_router {
-
-namespace {
-
 bool IsNetworkIdUnknownOrDisconnected(const std::string& network_id) {
   return network_id == DiscoveryNetworkMonitor::kNetworkIdUnknown ||
          network_id == DiscoveryNetworkMonitor::kNetworkIdDisconnected;
@@ -165,9 +164,11 @@
 
 CastMediaSinkServiceImpl::CastMediaSinkServiceImpl(
     const OnSinksDiscoveredCallback& callback,
+    Observer* observer,
     cast_channel::CastSocketService* cast_socket_service,
     DiscoveryNetworkMonitor* network_monitor)
     : MediaSinkServiceBase(callback),
+      observer_(observer),
       cast_socket_service_(cast_socket_service),
       network_monitor_(network_monitor),
       task_runner_(cast_socket_service_->task_runner()),
@@ -426,6 +427,11 @@
   if (sink_source != SinkSource::kDial)
     dial_sink_failure_count_.erase(ip_endpoint.address());
 
+  if (base::ContainsKey(current_sinks_map_, ip_endpoint)) {
+    DVLOG(2) << "A channel already exists for " << ip_endpoint.ToString();
+    return;
+  }
+
   if (!pending_for_open_ip_endpoints_.insert(ip_endpoint).second) {
     DVLOG(2) << "Pending opening request for " << ip_endpoint.ToString()
              << " name: " << cast_sink.sink().name();
@@ -519,7 +525,7 @@
 
   CastAnalytics::RecordCastChannelConnectResult(
       MediaRouterChannelConnectResults::SUCCESS);
-  media_router::CastSinkExtraData extra_data = cast_sink.cast_data();
+  CastSinkExtraData extra_data = cast_sink.cast_data();
   // Manually set device capabilities for sinks discovered via DIAL as DIAL
   // discovery does not provide capability info.
   if (cast_sink.cast_data().discovered_by_dial) {
@@ -538,11 +544,17 @@
   auto sink_it = current_sinks_map_.find(ip_endpoint);
   if (sink_it == current_sinks_map_.end()) {
     metrics_.RecordCastSinkDiscoverySource(sink_source);
-  } else if (sink_it->second.cast_data().discovered_by_dial &&
-             !cast_sink.cast_data().discovered_by_dial) {
-    metrics_.RecordCastSinkDiscoverySource(SinkSource::kDialMdns);
+    current_sinks_map_.emplace(ip_endpoint, cast_sink);
+  } else {
+    if (sink_it->second.cast_data().discovered_by_dial &&
+        !cast_sink.cast_data().discovered_by_dial) {
+      metrics_.RecordCastSinkDiscoverySource(SinkSource::kDialMdns);
+    }
+    sink_it->second = cast_sink;
   }
-  current_sinks_map_[ip_endpoint] = cast_sink;
+
+  if (observer_)
+    observer_->OnSinkAddedOrUpdated(cast_sink, socket);
 
   failure_count_map_.erase(ip_endpoint);
   MediaSinkServiceBase::RestartTimer();
@@ -550,7 +562,14 @@
 
 void CastMediaSinkServiceImpl::OnChannelOpenFailed(
     const net::IPEndPoint& ip_endpoint) {
-  current_sinks_map_.erase(ip_endpoint);
+  auto it = current_sinks_map_.find(ip_endpoint);
+  if (it == current_sinks_map_.end())
+    return;
+
+  if (observer_)
+    observer_->OnSinkRemoved(it->second);
+
+  current_sinks_map_.erase(it);
   MediaSinkServiceBase::RestartTimer();
 }
 
@@ -590,10 +609,7 @@
 
   for (const auto& cast_sink : cast_sinks) {
     const net::IPEndPoint& ip_endpoint = cast_sink.cast_data().ip_endpoint;
-    if (!base::ContainsKey(current_sinks_map_, ip_endpoint)) {
-      OpenChannel(ip_endpoint, cast_sink, nullptr,
-                  SinkSource::kConnectionRetry);
-    }
+    OpenChannel(ip_endpoint, cast_sink, nullptr, SinkSource::kConnectionRetry);
   }
 }
 
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h
index 48ce42f9..92c7bcd 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h
@@ -35,6 +35,23 @@
       public cast_channel::CastSocket::Observer,
       public DiscoveryNetworkMonitor::Observer {
  public:
+  // Listens for sink updates in CastMediaSinkServiceImpl. All observer methods
+  // must run on the same sequence as CastMediaSinkServiceImpl.
+  class Observer {
+   public:
+    virtual ~Observer() {}
+
+    // Invoked when |sink| is added or updated. |socket| is a pointer to the
+    // CastSocket instance associated with |sink|, and is never nullptr.
+    // |socket| is only guaranteed to be valid for the duration of this call;
+    // the caller should not hold onto the pointer.
+    virtual void OnSinkAddedOrUpdated(const MediaSinkInternal& sink,
+                                      cast_channel::CastSocket* socket) = 0;
+
+    // Invoked when |sink| is removed.
+    virtual void OnSinkRemoved(const MediaSinkInternal& sink) = 0;
+  };
+
   using SinkSource = CastDeviceCountMetrics::SinkSource;
 
   // Default Cast control port to open Cast Socket from DIAL sink.
@@ -45,11 +62,13 @@
   static constexpr int kMaxDialSinkFailureCount = 10;
 
   // |callback|: Callback passed to MediaSinkServiceBase.
+  // |observer|: Observer to invoke on sink updates. Can be nullptr.
   // |cast_socket_service|: CastSocketService to use to open Cast channels to
   // discovered devices.
   // |network_monitor|: DiscoveryNetworkMonitor to use to listen for network
   // changes.
   CastMediaSinkServiceImpl(const OnSinksDiscoveredCallback& callback,
+                           Observer* observer,
                            cast_channel::CastSocketService* cast_socket_service,
                            DiscoveryNetworkMonitor* network_monitor);
   ~CastMediaSinkServiceImpl() override;
@@ -77,10 +96,6 @@
       const std::vector<MediaSinkInternal>& cast_sinks,
       SinkSource sink_source);
 
-  // Attempts to resolve the given DIAL sink as a Cast sink. If successful,
-  // the resulting Cast sink is added to the service.
-  void OnDialSinkAdded(const MediaSinkInternal& sink);
-
   // Tries to open cast channels for sinks found by current round of mDNS
   // discovery, but without opened cast channels.
   // |cast_sinks|: list of sinks found by current round of mDNS discovery.
@@ -196,6 +211,10 @@
     static OpenParams GetFromFieldTrialParam();
   };
 
+  // Attempts to resolve the given DIAL sink as a Cast sink. If successful,
+  // the resulting Cast sink is added to the service.
+  void OnDialSinkAdded(const MediaSinkInternal& sink);
+
   // Marked virtual for testing.
   virtual void OpenChannels(const std::vector<MediaSinkInternal>& cast_sinks,
                             SinkSource sink_source);
@@ -216,7 +235,9 @@
   cast_channel::CastSocketOpenParams CreateCastSocketOpenParams(
       const net::IPEndPoint& ip_endpoint);
 
-  // Opens cast channel.
+  // Opens cast channel. This method will not open a channel if there is already
+  // a pending request for |ip_endpoint|, or if a channel for |ip_endpoint|
+  // already exists.
   // |ip_endpoint|: cast channel's target IP endpoint.
   // |cast_sink|: Cast sink created from mDNS service description or DIAL sink.
   // |backoff_entry|: backoff entry passed to |OnChannelOpened| callback.
@@ -291,6 +312,9 @@
   // Map of sinks with opened cast channels keyed by IP endpoint.
   MediaSinkInternalMap current_sinks_map_;
 
+  // Observer to notify when a sink is added, updated, or removed.
+  Observer* const observer_;
+
   // Raw pointer of leaky singleton CastSocketService, which manages adding and
   // removing Cast channels.
   cast_channel::CastSocketService* const cast_socket_service_;
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc
index 930cdfab..c00d68e 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc
@@ -34,22 +34,6 @@
 
 namespace {
 
-MediaSinkInternal CreateCastSink(int num) {
-  std::string friendly_name = base::StringPrintf("friendly name %d", num);
-  std::string unique_id = base::StringPrintf("id %d", num);
-  net::IPEndPoint ip_endpoint = CreateIPEndPoint(num);
-
-  MediaSink sink(unique_id, friendly_name, SinkIconType::CAST);
-  CastSinkExtraData extra_data;
-  extra_data.ip_endpoint = ip_endpoint;
-  extra_data.port = ip_endpoint.port();
-  extra_data.model_name = base::StringPrintf("model name %d", num);
-  extra_data.cast_channel_id = num;
-  extra_data.capabilities = cast_channel::CastDeviceCapability::AUDIO_OUT |
-                            cast_channel::CastDeviceCapability::VIDEO_OUT;
-  return MediaSinkInternal(sink, extra_data);
-}
-
 MATCHER_P(RetryParamEq, expected, "") {
   return expected.initial_delay_in_milliseconds ==
              arg.initial_delay_in_milliseconds &&
@@ -67,6 +51,16 @@
          expected.ping_interval_in_seconds == arg.ping_interval_in_seconds;
 }
 
+class MockObserver : public CastMediaSinkServiceImpl::Observer {
+ public:
+  MockObserver() {}
+  ~MockObserver() override = default;
+
+  MOCK_METHOD2(OnSinkAddedOrUpdated,
+               void(const MediaSinkInternal&, cast_channel::CastSocket*));
+  MOCK_METHOD1(OnSinkRemoved, void(const MediaSinkInternal&));
+};
+
 }  // namespace
 
 class CastMediaSinkServiceImplTest : public ::testing::Test {
@@ -77,6 +71,7 @@
         mock_cast_socket_service_(
             new cast_channel::MockCastSocketService(mock_time_task_runner_)),
         media_sink_service_impl_(mock_sink_discovered_cb_.Get(),
+                                 &observer_,
                                  mock_cast_socket_service_.get(),
                                  discovery_network_monitor_.get()) {
     mock_cast_socket_service_->SetTaskRunnerForTest(mock_time_task_runner_);
@@ -131,6 +126,7 @@
       mock_cast_socket_service_;
   base::MockTimer* mock_timer_;
   CastMediaSinkServiceImpl media_sink_service_impl_;
+  testing::NiceMock<MockObserver> observer_;
 
   DISALLOW_COPY_AND_ASSIGN(CastMediaSinkServiceImplTest);
 };
@@ -160,6 +156,7 @@
   cast_channel::MockCastSocket socket;
   socket.set_id(1);
 
+  EXPECT_CALL(observer_, OnSinkAddedOrUpdated(cast_sink, &socket));
   media_sink_service_impl_.OnChannelOpenSucceeded(
       cast_sink, &socket, CastMediaSinkServiceImpl::SinkSource::kMdns);
 
@@ -186,6 +183,7 @@
   // Current round of Dns discovery finds service1 and service 2.
   // Fail to open channel 1.
   base::HistogramTester tester;
+  EXPECT_CALL(observer_, OnSinkAddedOrUpdated(cast_sink2, &socket2));
   media_sink_service_impl_.OnChannelOpenSucceeded(
       cast_sink2, &socket2, CastMediaSinkServiceImpl::SinkSource::kMdns);
   EXPECT_THAT(
@@ -194,6 +192,7 @@
       ElementsAre(Bucket(
           static_cast<int>(CastMediaSinkServiceImpl::SinkSource::kMdns), 1)));
 
+  EXPECT_CALL(observer_, OnSinkAddedOrUpdated(cast_sink3, &socket3));
   media_sink_service_impl_.OnChannelOpenSucceeded(
       cast_sink3, &socket3, CastMediaSinkServiceImpl::SinkSource::kDial);
   EXPECT_THAT(
@@ -207,6 +206,7 @@
 
   extra_data.discovered_by_dial = false;
   cast_sink3.set_cast_data(extra_data);
+  EXPECT_CALL(observer_, OnSinkAddedOrUpdated(cast_sink3, &socket3));
   media_sink_service_impl_.OnChannelOpenSucceeded(
       cast_sink3, &socket3, CastMediaSinkServiceImpl::SinkSource::kMdns);
   EXPECT_THAT(
@@ -241,6 +241,7 @@
   cast_channel::MockCastSocket socket2;
   socket2.set_id(2);
 
+  EXPECT_CALL(observer_, OnSinkAddedOrUpdated(cast_sink2, &socket2));
   media_sink_service_impl_.OnChannelOpenSucceeded(
       cast_sink2, &socket2, CastMediaSinkServiceImpl::SinkSource::kMdns);
 
@@ -256,6 +257,7 @@
   cast_channel::MockCastSocket socket1;
   socket1.set_id(1);
 
+  EXPECT_CALL(observer_, OnSinkAddedOrUpdated(cast_sink1, &socket1));
   media_sink_service_impl_.OnChannelOpenSucceeded(
       cast_sink1, &socket1, CastMediaSinkServiceImpl::SinkSource::kMdns);
   EXPECT_TRUE(mock_timer_->IsRunning());
@@ -360,14 +362,17 @@
   clock.Advance(delta);
   base::HistogramTester tester;
 
+  EXPECT_CALL(observer_, OnSinkAddedOrUpdated(cast_sink2, &socket2));
   media_sink_service_impl_.OnChannelOpened(
       cast_sink2, nullptr, CastMediaSinkServiceImpl::SinkSource::kMdns,
       start_time, &socket2);
   tester.ExpectUniqueSample(CastAnalytics::kHistogramCastMdnsChannelOpenSuccess,
                             delta.InMilliseconds(), 1);
 
+  // There is already a socket open for |ip_endpoint2|.
   EXPECT_CALL(*mock_cast_socket_service_,
-              OpenSocketInternal(ip_endpoint2, _, _));
+              OpenSocketInternal(ip_endpoint2, _, _))
+      .Times(0);
   EXPECT_CALL(*mock_cast_socket_service_,
               OpenSocketInternal(ip_endpoint3, _, _));
 
@@ -383,9 +388,11 @@
   socket3.set_id(3);
   socket1.SetErrorState(cast_channel::ChannelError::NONE);
   socket3.SetErrorState(cast_channel::ChannelError::NONE);
+  EXPECT_CALL(observer_, OnSinkAddedOrUpdated(cast_sink1, &socket1));
   media_sink_service_impl_.OnChannelOpened(
       cast_sink1, nullptr, CastMediaSinkServiceImpl::SinkSource::kMdns,
       start_time, &socket1);
+  EXPECT_CALL(observer_, OnSinkAddedOrUpdated(cast_sink3, &socket3));
   media_sink_service_impl_.OnChannelOpened(
       cast_sink3, nullptr, CastMediaSinkServiceImpl::SinkSource::kMdns,
       start_time, &socket3);
@@ -402,12 +409,14 @@
   cast_channel::MockCastSocket socket;
   socket.set_id(1);
 
+  EXPECT_CALL(observer_, OnSinkAddedOrUpdated(cast_sink, &socket));
   media_sink_service_impl_.OnChannelOpenSucceeded(
       cast_sink, &socket, CastMediaSinkServiceImpl::SinkSource::kMdns);
 
   EXPECT_EQ(1u, media_sink_service_impl_.current_sinks_map_.size());
 
   socket.SetIPEndpoint(ip_endpoint1);
+  EXPECT_CALL(observer_, OnSinkRemoved(cast_sink));
   media_sink_service_impl_.OnChannelOpenFailed(ip_endpoint1);
   EXPECT_TRUE(media_sink_service_impl_.current_sinks_map_.empty());
 }
@@ -435,33 +444,6 @@
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
 }
 
-TEST_F(CastMediaSinkServiceImplTest, TestOnChannelErrorMayRetryForCastSink) {
-  auto cast_sink = CreateCastSink(1);
-  net::IPEndPoint ip_endpoint1 = CreateIPEndPoint(1);
-  cast_channel::MockCastSocket socket;
-  socket.set_id(1);
-  socket.SetIPEndpoint(ip_endpoint1);
-  socket.SetErrorState(cast_channel::ChannelError::CHANNEL_NOT_OPEN);
-
-  // There is an existing cast sink in |current_sinks_map_|.
-  media_sink_service_impl_.current_sinks_map_[ip_endpoint1] = cast_sink;
-  EXPECT_CALL(socket, ready_state())
-      .WillRepeatedly(Return(cast_channel::ReadyState::CLOSED));
-  EXPECT_CALL(*mock_cast_socket_service_,
-              OpenSocketInternal(ip_endpoint1, _, _))
-      .WillRepeatedly(
-          Invoke([&](const auto& ip_endpoint, auto* net_log, auto open_cb) {
-            std::move(open_cb).Run(&socket);
-          }));
-
-  media_sink_service_impl_.OnError(
-      socket, cast_channel::ChannelError::CHANNEL_NOT_OPEN);
-
-  mock_time_task_runner_->FastForwardUntilNoTasksRemain();
-  EXPECT_EQ(4, media_sink_service_impl_.failure_count_map_[ip_endpoint1]);
-  EXPECT_TRUE(media_sink_service_impl_.current_sinks_map_.empty());
-}
-
 TEST_F(CastMediaSinkServiceImplTest, TestOnChannelErrorNoRetryForMissingSink) {
   net::IPEndPoint ip_endpoint1 = CreateIPEndPoint(1);
   cast_channel::MockCastSocket socket;
@@ -1118,11 +1100,15 @@
   ExpectOpenSocketInternal(&socket1_dial);
   media_sink_service_impl_.OnDialSinkAdded(sink1_dial);
 
-  // The same sink is then discovered via mdns.
+  // The same sink is then discovered via mdns. However we won't open channel
+  // again.
   cast_channel::MockCastSocket socket1_cast;
   socket1_cast.SetIPEndpoint(ip_endpoint1_cast);
   socket1_cast.set_id(2);
-  ExpectOpenSocketInternal(&socket1_cast);
+
+  EXPECT_CALL(*mock_cast_socket_service_,
+              OpenSocketInternal(ip_endpoint1_cast, _, _))
+      .Times(0);
   media_sink_service_impl_.OpenChannels(
       sink_list1, CastMediaSinkServiceImpl::SinkSource::kMdns);
 
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_unittest.cc b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_unittest.cc
index a968f8a..02646b5 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_unittest.cc
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_unittest.cc
@@ -56,9 +56,11 @@
  public:
   MockCastMediaSinkServiceImpl(
       const OnSinksDiscoveredCallback& callback,
+      CastMediaSinkServiceImpl::Observer* observer,
       cast_channel::CastSocketService* cast_socket_service,
       DiscoveryNetworkMonitor* network_monitor)
       : CastMediaSinkServiceImpl(callback,
+                                 observer,
                                  cast_socket_service,
                                  network_monitor),
         sinks_discovered_cb_(callback) {}
@@ -87,11 +89,13 @@
   ~TestCastMediaSinkService() override = default;
 
   std::unique_ptr<CastMediaSinkServiceImpl, base::OnTaskRunnerDeleter>
-  CreateImpl(const OnSinksDiscoveredCallback& sinks_discovered_cb) override {
+  CreateImpl(const OnSinksDiscoveredCallback& sinks_discovered_cb,
+             CastMediaSinkServiceImpl::Observer* observer) override {
     auto mock_impl = std::unique_ptr<MockCastMediaSinkServiceImpl,
                                      base::OnTaskRunnerDeleter>(
-        new MockCastMediaSinkServiceImpl(
-            sinks_discovered_cb, cast_socket_service_, network_monitor_),
+        new MockCastMediaSinkServiceImpl(sinks_discovered_cb, observer,
+                                         cast_socket_service_,
+                                         network_monitor_),
         base::OnTaskRunnerDeleter(cast_socket_service_->task_runner()));
     mock_impl_ = mock_impl.get();
     return mock_impl;
@@ -121,7 +125,8 @@
     EXPECT_CALL(test_dns_sd_registry_, AddObserver(media_sink_service_.get()));
     EXPECT_CALL(test_dns_sd_registry_, RegisterDnsSdListener(_));
     media_sink_service_->SetDnsSdRegistryForTest(&test_dns_sd_registry_);
-    media_sink_service_->Start(mock_sink_discovered_ui_cb_.Get());
+    media_sink_service_->Start(mock_sink_discovered_ui_cb_.Get(),
+                               /* observer */ nullptr);
     mock_impl_ = media_sink_service_->mock_impl();
     ASSERT_TRUE(mock_impl_);
     EXPECT_CALL(*mock_impl_, DoStart()).WillOnce(InvokeWithoutArgs([this]() {
diff --git a/chrome/browser/media/router/media_router_feature.cc b/chrome/browser/media/router/media_router_feature.cc
index 9e8793a4..f878f43 100644
--- a/chrome/browser/media/router/media_router_feature.cc
+++ b/chrome/browser/media/router/media_router_feature.cc
@@ -28,6 +28,9 @@
 const base::Feature kEnableCastDiscovery{"EnableCastDiscovery",
                                          base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kCastMediaRouteProvider{"CastMediaRouteProvider",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls if local media casting is enabled.
 const base::Feature kEnableCastLocalMedia{"EnableCastLocalMedia",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
@@ -72,6 +75,10 @@
   return base::FeatureList::IsEnabled(kEnableCastDiscovery);
 }
 
+bool CastMediaRouteProviderEnabled() {
+  return base::FeatureList::IsEnabled(kCastMediaRouteProvider);
+}
+
 // Returns true if local media casting is enabled.
 bool CastLocalMediaEnabled() {
   return base::FeatureList::IsEnabled(kEnableCastLocalMedia);
diff --git a/chrome/browser/media/router/media_router_feature.h b/chrome/browser/media/router/media_router_feature.h
index 42f8d88..8b315386 100644
--- a/chrome/browser/media/router/media_router_feature.h
+++ b/chrome/browser/media/router/media_router_feature.h
@@ -20,6 +20,7 @@
 
 extern const base::Feature kEnableDialSinkQuery;
 extern const base::Feature kEnableCastDiscovery;
+extern const base::Feature kCastMediaRouteProvider;
 extern const base::Feature kEnableCastLocalMedia;
 
 // Returns true if browser side DIAL sink query is enabled.
@@ -28,6 +29,10 @@
 // Returns true if browser side Cast discovery is enabled.
 bool CastDiscoveryEnabled();
 
+// Returns true if browser side Cast Media Route Provider and sink query are
+// enabled.
+bool CastMediaRouteProviderEnabled();
+
 // Returns true if local media casting is enabled.
 bool CastLocalMediaEnabled();
 
diff --git a/chrome/browser/media/router/mojo/media_router_desktop.cc b/chrome/browser/media/router/mojo/media_router_desktop.cc
index 6ab67097..bfed187 100644
--- a/chrome/browser/media/router/mojo/media_router_desktop.cc
+++ b/chrome/browser/media/router/mojo/media_router_desktop.cc
@@ -10,10 +10,12 @@
 #include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/media/router/mojo/media_route_controller.h"
 #include "chrome/browser/media/router/mojo/media_router_mojo_metrics.h"
+#include "chrome/browser/media/router/providers/cast/cast_media_route_provider.h"
 #include "chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/media_router/media_source_helper.h"
+#include "components/cast_channel/cast_socket_service.h"
 #include "extensions/common/extension.h"
 #if defined(OS_WIN)
 #include "chrome/browser/media/router/mojo/media_route_provider_util_win.h"
@@ -64,6 +66,7 @@
 
 MediaRouterDesktop::MediaRouterDesktop(content::BrowserContext* context)
     : MediaRouterMojoImpl(context),
+      cast_provider_(nullptr, base::OnTaskRunnerDeleter(nullptr)),
       media_sink_service_(DualMediaSinkService::GetInstance()),
       weak_factory_(this) {
   InitializeMediaRouteProviders();
@@ -77,6 +80,7 @@
 MediaRouterDesktop::MediaRouterDesktop(content::BrowserContext* context,
                                        DualMediaSinkService* media_sink_service)
     : MediaRouterMojoImpl(context),
+      cast_provider_(nullptr, base::OnTaskRunnerDeleter(nullptr)),
       media_sink_service_(media_sink_service),
       weak_factory_(this) {
   InitializeMediaRouteProviders();
@@ -93,6 +97,8 @@
   // to disable it in the provider.
   config->enable_cast_discovery = !media_router::CastDiscoveryEnabled();
   config->enable_dial_sink_query = !media_router::DialSinkQueryEnabled();
+  config->enable_cast_sink_query =
+      !media_router::CastMediaRouteProviderEnabled();
   std::move(callback).Run(instance_id(), std::move(config));
 
   SyncStateToMediaRouteProvider(provider_id);
@@ -107,6 +113,22 @@
   }
 }
 
+void MediaRouterDesktop::OnSinksReceived(
+    MediaRouteProviderId provider_id,
+    const std::string& media_source,
+    const std::vector<MediaSinkInternal>& internal_sinks,
+    const std::vector<url::Origin>& origins) {
+  media_sink_service_status_.UpdateAvailableSinks(provider_id, media_source,
+                                                  internal_sinks);
+  MediaRouterMojoImpl::OnSinksReceived(provider_id, media_source,
+                                       internal_sinks, origins);
+}
+
+void MediaRouterDesktop::GetMediaSinkServiceStatus(
+    mojom::MediaRouter::GetMediaSinkServiceStatusCallback callback) {
+  std::move(callback).Run(media_sink_service_status_.GetStatusAsJSONString());
+}
+
 void MediaRouterDesktop::RegisterExtensionMediaRouteProvider(
     mojom::MediaRouteProviderPtr extension_provider_ptr) {
   ProvideSinksToExtension();
@@ -170,12 +192,16 @@
            << " devices...";
   media_route_providers_[MediaRouteProviderId::EXTENSION]->ProvideSinks(
       provider_name, sinks);
+
+  media_sink_service_status_.UpdateDiscoveredSinks(provider_name, sinks);
 }
 
 void MediaRouterDesktop::InitializeMediaRouteProviders() {
   InitializeExtensionMediaRouteProviderProxy();
   if (base::FeatureList::IsEnabled(features::kLocalScreenCasting))
     InitializeWiredDisplayMediaRouteProvider();
+  if (CastMediaRouteProviderEnabled())
+    InitializeCastMediaRouteProvider();
 }
 
 void MediaRouterDesktop::InitializeExtensionMediaRouteProviderProxy() {
@@ -194,11 +220,26 @@
   wired_display_provider_ = std::make_unique<WiredDisplayMediaRouteProvider>(
       mojo::MakeRequest(&wired_display_provider_ptr),
       std::move(media_router_ptr), Profile::FromBrowserContext(context()));
-  RegisterMediaRouteProvider(
-      MediaRouteProviderId::WIRED_DISPLAY,
-      std::move(wired_display_provider_ptr),
-      base::BindOnce([](const std::string& instance_id,
-                        mojom::MediaRouteProviderConfigPtr config) {}));
+  RegisterMediaRouteProvider(MediaRouteProviderId::WIRED_DISPLAY,
+                             std::move(wired_display_provider_ptr),
+                             base::DoNothing());
+}
+
+void MediaRouterDesktop::InitializeCastMediaRouteProvider() {
+  auto task_runner =
+      cast_channel::CastSocketService::GetInstance()->task_runner();
+  mojom::MediaRouterPtr media_router_ptr;
+  MediaRouterMojoImpl::BindToMojoRequest(mojo::MakeRequest(&media_router_ptr));
+  mojom::MediaRouteProviderPtr cast_provider_ptr;
+  cast_provider_ =
+      std::unique_ptr<CastMediaRouteProvider, base::OnTaskRunnerDeleter>(
+          new CastMediaRouteProvider(
+              mojo::MakeRequest(&cast_provider_ptr),
+              media_router_ptr.PassInterface(),
+              media_sink_service_->cast_app_discovery_service(), task_runner),
+          base::OnTaskRunnerDeleter(task_runner));
+  RegisterMediaRouteProvider(MediaRouteProviderId::CAST,
+                             std::move(cast_provider_ptr), base::DoNothing());
 }
 
 #if defined(OS_WIN)
diff --git a/chrome/browser/media/router/mojo/media_router_desktop.h b/chrome/browser/media/router/mojo/media_router_desktop.h
index a33e3baf..143d0c0 100644
--- a/chrome/browser/media/router/mojo/media_router_desktop.h
+++ b/chrome/browser/media/router/mojo/media_router_desktop.h
@@ -8,6 +8,7 @@
 #include "base/gtest_prod_util.h"
 #include "build/build_config.h"
 #include "chrome/browser/media/router/mojo/media_router_mojo_impl.h"
+#include "chrome/browser/media/router/mojo/media_sink_service_status.h"
 #include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h"
 #include "chrome/browser/media/router/providers/extension/extension_media_route_provider_proxy.h"
 
@@ -20,6 +21,7 @@
 }
 
 namespace media_router {
+class CastMediaRouteProvider;
 class DualMediaSinkService;
 class WiredDisplayMediaRouteProvider;
 
@@ -71,6 +73,12 @@
       MediaRouteProviderId provider_id,
       mojom::MediaRouteProviderPtr media_route_provider_ptr,
       mojom::MediaRouter::RegisterMediaRouteProviderCallback callback) override;
+  void OnSinksReceived(MediaRouteProviderId provider_id,
+                       const std::string& media_source,
+                       const std::vector<MediaSinkInternal>& internal_sinks,
+                       const std::vector<url::Origin>& origins) override;
+  void GetMediaSinkServiceStatus(
+      mojom::MediaRouter::GetMediaSinkServiceStatusCallback callback) override;
 
   // Registers a Mojo pointer to the extension MRP with
   // |extension_provider_proxy_| and does initializations specific to the
@@ -101,6 +109,7 @@
   // Helper methods for InitializeMediaRouteProviders().
   void InitializeExtensionMediaRouteProviderProxy();
   void InitializeWiredDisplayMediaRouteProvider();
+  void InitializeCastMediaRouteProvider();
 
 #if defined(OS_WIN)
   // Ensures that mDNS discovery is enabled in the MRPM extension. This can be
@@ -121,6 +130,10 @@
   // MediaRouteProvider for casting to local screens.
   std::unique_ptr<WiredDisplayMediaRouteProvider> wired_display_provider_;
 
+  // MediaRouteProvider for casting to Cast devices.
+  std::unique_ptr<CastMediaRouteProvider, base::OnTaskRunnerDeleter>
+      cast_provider_;
+
   DualMediaSinkService* media_sink_service_;
   DualMediaSinkService::Subscription media_sink_service_subscription_;
 
@@ -128,6 +141,10 @@
   // initial event page wakeup attempt.
   bool provider_version_was_recorded_ = false;
 
+  // A status object that keeps track of sinks discovered by media sink
+  // services.
+  MediaSinkServiceStatus media_sink_service_status_;
+
 #if defined(OS_WIN)
   // A flag to ensure that mDNS discovery is only enabled on Windows when there
   // will be appropriate context for the user to associate a firewall prompt
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
index f3a61cd..f53490c 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/media/router/mojo/media_route_controller.h"
 #include "chrome/browser/media/router/mojo/media_route_provider_util_win.h"
 #include "chrome/browser/media/router/mojo/media_router_mojo_metrics.h"
+#include "chrome/browser/media/router/mojo/media_sink_service_status.h"
 #include "chrome/browser/media/router/route_message_observer.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
@@ -200,6 +201,13 @@
 
   MediaRouterMetrics::RecordMediaSinkType(sink->icon_type());
   MediaRouteProviderId provider_id = sink->provider_id();
+  // This is a hack to ensure the extension handles the CreateRoute call until
+  // the CastMediaRouteProvider supports it.
+  // TODO(crbug.com/698940): Remove this hack when CastMediaRouteProvider
+  // supports route management.
+  if (provider_id == MediaRouteProviderId::CAST)
+    provider_id = MediaRouteProviderId::EXTENSION;
+
   int tab_id = SessionTabHelper::IdForTab(web_contents);
   std::string presentation_id = MediaRouterBase::CreatePresentationId();
   auto callback = base::BindOnce(
@@ -897,6 +905,12 @@
   connector->ConnectToService(std::move(source_request), std::move(remoter));
 }
 
+void MediaRouterMojoImpl::GetMediaSinkServiceStatus(
+    mojom::MediaRouter::GetMediaSinkServiceStatusCallback callback) {
+  MediaSinkServiceStatus status;
+  std::move(callback).Run(status.GetStatusAsJSONString());
+}
+
 void MediaRouterMojoImpl::BindToMojoRequest(
     mojo::InterfaceRequest<mojom::MediaRouter> request) {
   bindings_.AddBinding(this, std::move(request));
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.h b/chrome/browser/media/router/mojo/media_router_mojo_impl.h
index f51b2d7b..7c898a47 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.h
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.h
@@ -127,6 +127,12 @@
 
   content::BrowserContext* context() const { return context_; }
 
+  // mojom::MediaRouter implementation.
+  void OnSinksReceived(MediaRouteProviderId provider_id,
+                       const std::string& media_source,
+                       const std::vector<MediaSinkInternal>& internal_sinks,
+                       const std::vector<url::Origin>& origins) override;
+
   // Mojo pointers to media route providers. Providers are added via
   // RegisterMediaRouteProvider().
   base::flat_map<MediaRouteProviderId, mojom::MediaRouteProviderPtr>
@@ -327,10 +333,6 @@
 
   // mojom::MediaRouter implementation.
   void OnIssue(const IssueInfo& issue) override;
-  void OnSinksReceived(MediaRouteProviderId provider_id,
-                       const std::string& media_source,
-                       const std::vector<MediaSinkInternal>& internal_sinks,
-                       const std::vector<url::Origin>& origins) override;
   void OnRoutesUpdated(
       MediaRouteProviderId provider_id,
       const std::vector<MediaRoute>& routes,
@@ -353,6 +355,8 @@
       int32_t tab_id,
       media::mojom::MirrorServiceRemoterPtr remoter,
       media::mojom::MirrorServiceRemotingSourceRequest source_request) override;
+  void GetMediaSinkServiceStatus(
+      mojom::MediaRouter::GetMediaSinkServiceStatusCallback callback) override;
 
   // Result callback when Mojo TerminateRoute is invoked.
   // |route_id|: ID of MediaRoute passed to the TerminateRoute request.
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_metrics.cc b/chrome/browser/media/router/mojo/media_router_mojo_metrics.cc
index 4df6832..0ced200 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_metrics.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_metrics.cc
@@ -84,6 +84,8 @@
                                 result_code, RouteRequestResult::TOTAL_COUNT);
       break;
     case MediaRouteProviderId::EXTENSION:
+    // TODO(crbug.com/809249): Implement Cast-specific metric.
+    case MediaRouteProviderId::CAST:
     case MediaRouteProviderId::UNKNOWN:
       UMA_HISTOGRAM_ENUMERATION(kHistogramProviderCreateRouteResult,
                                 result_code, RouteRequestResult::TOTAL_COUNT);
@@ -102,6 +104,8 @@
                                 result_code, RouteRequestResult::TOTAL_COUNT);
       break;
     case MediaRouteProviderId::EXTENSION:
+    // TODO(crbug.com/809249): Implement Cast-specific metric.
+    case MediaRouteProviderId::CAST:
     case MediaRouteProviderId::UNKNOWN:
       UMA_HISTOGRAM_ENUMERATION(kHistogramProviderJoinRouteResult, result_code,
                                 RouteRequestResult::TOTAL_COUNT);
@@ -121,6 +125,8 @@
           RouteRequestResult::TOTAL_COUNT);
       break;
     case MediaRouteProviderId::EXTENSION:
+    // TODO(crbug.com/809249): Implement Cast-specific metric.
+    case MediaRouteProviderId::CAST:
     case MediaRouteProviderId::UNKNOWN:
       UMA_HISTOGRAM_ENUMERATION(kHistogramProviderTerminateRouteResult,
                                 result_code, RouteRequestResult::TOTAL_COUNT);
diff --git a/chrome/browser/media/router/mojo/media_sink_service_status.cc b/chrome/browser/media/router/mojo/media_sink_service_status.cc
new file mode 100644
index 0000000..5d2266e
--- /dev/null
+++ b/chrome/browser/media/router/mojo/media_sink_service_status.cc
@@ -0,0 +1,154 @@
+// 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/media/router/mojo/media_sink_service_status.h"
+
+#include "base/json/json_string_value_serializer.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+
+namespace media_router {
+
+namespace {
+
+constexpr size_t kMaxAvailableSinksSize = 100;
+constexpr char kCastPrefix[] = "cast:<";
+constexpr char kDialPrefix[] = "dial:<";
+
+// Helper function to convert |value| to JSON string.
+std::string ToJSONString(const base::Value& value) {
+  std::string json;
+  JSONStringValueSerializer serializer(&json);
+  serializer.set_pretty_print(true);
+  if (!serializer.Serialize(value)) {
+    DVLOG(1) << "Failed to serialize log to JSON.";
+    return "";
+  }
+  return json;
+}
+
+// Returns UUID if |sink_id| is in the format of "cast:<UUID>" or "dial:<UUID>";
+// otherwise returns |sink_id| as UUID.
+base::StringPiece ExtractUUID(const base::StringPiece& sink_id) {
+  if (!sink_id.ends_with(">"))
+    return sink_id;
+
+  size_t prefix_length = 0;
+  if (sink_id.starts_with(kCastPrefix))
+    prefix_length = sizeof(kCastPrefix) - 1;
+  if (sink_id.starts_with(kDialPrefix))
+    prefix_length = sizeof(kDialPrefix) - 1;
+
+  if (prefix_length == 0)
+    return sink_id;
+
+  base::StringPiece uuid = sink_id;
+  uuid.remove_prefix(prefix_length);
+  uuid.remove_suffix(1);
+  return uuid;
+}
+
+// Returns the last four characters of UUID. UUID is extracted from |sink_id|.
+std::string TruncateSinkId(const std::string& sink_id) {
+  std::string uuid = ExtractUUID(sink_id).as_string();
+  return uuid.length() <= 4 ? uuid : uuid.substr(uuid.length() - 4);
+}
+
+// Helper function to convert |sink_internal| to JSON format represented by
+// base::Value.
+base::Value ToValue(const MediaSinkInternal& sink_internal) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  const MediaSink& sink = sink_internal.sink();
+  dict.SetKey("id", base::Value(TruncateSinkId(sink.id())));
+  dict.SetKey("name", base::Value(sink.name()));
+  if (sink.description())
+    dict.SetKey("description", base::Value(*sink.description()));
+  if (sink.domain())
+    dict.SetKey("domain", base::Value(*sink.domain()));
+  dict.SetKey("icon_type", base::Value(static_cast<int>(sink.icon_type())));
+
+  if (sink_internal.is_dial_sink()) {
+    DialSinkExtraData extra_data = sink_internal.dial_data();
+    dict.SetKey("ip_address", base::Value(extra_data.ip_address.ToString()));
+    dict.SetKey("model_name", base::Value(extra_data.model_name));
+    dict.SetKey("app_url", base::Value(extra_data.app_url.spec()));
+  }
+
+  if (sink_internal.is_cast_sink()) {
+    CastSinkExtraData extra_data = sink_internal.cast_data();
+    dict.SetKey("ip_endpoint", base::Value(extra_data.ip_endpoint.ToString()));
+    dict.SetKey("model_name", base::Value(extra_data.model_name));
+    dict.SetKey("capabilities", base::Value(extra_data.capabilities));
+    dict.SetKey("channel_id", base::Value(extra_data.cast_channel_id));
+    dict.SetKey("discovered_by_dial",
+                base::Value(extra_data.discovered_by_dial));
+  }
+  return dict;
+}
+
+// Helper function to convert |sinks| to JSON format represented by
+// base::Value.
+base::Value ConvertDiscoveredSinksToValues(
+    const base::flat_map<std::string, std::vector<MediaSinkInternal>>& sinks) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  for (const auto& sinks_it : sinks) {
+    base::ListValue list;
+    for (const auto& inner_sink : sinks_it.second)
+      list.GetList().push_back(ToValue(inner_sink));
+    dict.SetKey(sinks_it.first, std::move(list));
+  }
+  return dict;
+}
+
+// Helper function to convert |available_sinks| to a dictionary of availability
+// strings in JSON format represented by base::Value.
+base::Value ConvertAvailableSinksToValues(
+    const base::MRUCache<std::string, std::vector<MediaSinkInternal>>&
+        available_sinks) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  for (const auto& sinks_it : available_sinks) {
+    base::Value list(base::Value::Type::LIST);
+    for (const auto& inner_sink : sinks_it.second) {
+      std::string sink_id = inner_sink.sink().id();
+      list.GetList().push_back(base::Value(TruncateSinkId(sink_id)));
+    }
+    dict.SetKey(sinks_it.first, std::move(list));
+  }
+  return dict;
+}
+
+}  // namespace
+
+MediaSinkServiceStatus::MediaSinkServiceStatus()
+    : available_sinks_(kMaxAvailableSinksSize) {}
+MediaSinkServiceStatus::~MediaSinkServiceStatus() = default;
+
+void MediaSinkServiceStatus::UpdateDiscoveredSinks(
+    const std::string& provider_name,
+    const std::vector<MediaSinkInternal>& discovered_sinks) {
+  discovered_sinks_[provider_name] = discovered_sinks;
+}
+
+void MediaSinkServiceStatus::UpdateAvailableSinks(
+    MediaRouteProviderId provider_id,
+    const std::string& media_source,
+    const std::vector<MediaSinkInternal>& available_sinks) {
+  std::string key =
+      base::StrCat({ProviderIdToString(provider_id), ":", media_source});
+  available_sinks_.Put(key, available_sinks);
+}
+
+std::string MediaSinkServiceStatus::GetStatusAsJSONString() const {
+  base::DictionaryValue status_dict;
+  status_dict.SetKey("discovered_sinks",
+                     ConvertDiscoveredSinksToValues(discovered_sinks_));
+  status_dict.SetKey("available_sinks",
+                     ConvertAvailableSinksToValues(available_sinks_));
+
+  return ToJSONString(status_dict);
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/router/mojo/media_sink_service_status.h b/chrome/browser/media/router/mojo/media_sink_service_status.h
new file mode 100644
index 0000000..2847e61
--- /dev/null
+++ b/chrome/browser/media/router/mojo/media_sink_service_status.h
@@ -0,0 +1,49 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_ROUTER_MOJO_MEDIA_SINK_SERVICE_STATUS_H_
+#define CHROME_BROWSER_MEDIA_ROUTER_MOJO_MEDIA_SINK_SERVICE_STATUS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/mru_cache.h"
+#include "chrome/common/media_router/discovery/media_sink_internal.h"
+#include "chrome/common/media_router/media_route_provider_helper.h"
+
+namespace media_router {
+
+// Keeps track of media sinks reported by media sink service. This class
+// provides some debug info about in-browser discovery.
+class MediaSinkServiceStatus {
+ public:
+  MediaSinkServiceStatus();
+  ~MediaSinkServiceStatus();
+
+  // Called when a media sink service reports discovered sinks to MR.
+  void UpdateDiscoveredSinks(
+      const std::string& provider_name,
+      const std::vector<MediaSinkInternal>& discovered_sinks);
+  // Called when a media sink service reports available sinks for an app to MR.
+  void UpdateAvailableSinks(
+      MediaRouteProviderId provider_id,
+      const std::string& media_source,
+      const std::vector<MediaSinkInternal>& available_sinks);
+
+  // Returns current status as a JSON string.
+  std::string GetStatusAsJSONString() const;
+
+ private:
+  // Map of sinks sent to extension, keyed by provider name;
+  base::flat_map<std::string, std::vector<MediaSinkInternal>> discovered_sinks_;
+  // Map of available sinks, keyed by media source
+  base::MRUCache<std::string, std::vector<MediaSinkInternal>> available_sinks_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaSinkServiceStatus);
+};
+
+}  // namespace media_router
+
+#endif  // CHROME_BROWSER_MEDIA_ROUTER_MOJO_MEDIA_SINK_SERVICE_STATUS_H_
diff --git a/chrome/browser/media/router/mojo/media_sink_service_status_unittest.cc b/chrome/browser/media/router/mojo/media_sink_service_status_unittest.cc
new file mode 100644
index 0000000..07b9f52
--- /dev/null
+++ b/chrome/browser/media/router/mojo/media_sink_service_status_unittest.cc
@@ -0,0 +1,163 @@
+// 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/media/router/mojo/media_sink_service_status.h"
+
+#include "base/json/json_string_value_serializer.h"
+#include "base/values.h"
+#include "chrome/browser/media/router/test/test_helper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media_router {
+
+namespace {
+
+std::unique_ptr<base::Value> DeserializeJSONString(const std::string& str) {
+  JSONStringValueDeserializer deserializer(str);
+  int error_code = 0;
+  std::string error_message;
+  std::unique_ptr<base::Value> value =
+      deserializer.Deserialize(&error_code, &error_message);
+  EXPECT_TRUE(value.get());
+  EXPECT_EQ(0, error_code);
+  EXPECT_TRUE(error_message.empty());
+  return value;
+}
+
+// Helper function to compare two JSON strings. The two strings may have
+// different format and spaces. Still returns true if their contents are the
+// same.
+void VerifyEqualJSONString(const std::string& expected_str,
+                           const std::string& str) {
+  std::unique_ptr<base::Value> expected_value =
+      DeserializeJSONString(expected_str);
+  std::unique_ptr<base::Value> actual_value = DeserializeJSONString(str);
+  ASSERT_TRUE(expected_value);
+  ASSERT_TRUE(actual_value);
+  EXPECT_EQ(*expected_value, *actual_value);
+}
+
+}  // namespace
+
+TEST(MediaSinkServiceStatusTest, TestGetStatusAsJSONStringEmptyStatus) {
+  const char expected_str[] = R"(
+      {
+        "available_sinks": { },
+        "discovered_sinks": { }
+      })";
+
+  MediaSinkServiceStatus status;
+  std::string str = status.GetStatusAsJSONString();
+  VerifyEqualJSONString(expected_str, str);
+}
+
+TEST(MediaSinkServiceStatusTest, TestGetStatusAsJSONStringEmptySinks) {
+  const char expected_str[] = R"(
+      {
+        "available_sinks": {
+          "EXTENSION:dial:youtube" : ["3610", "id2"]
+        },
+        "discovered_sinks": { }
+      })";
+
+  MediaSinkInternal dial_sink1 = CreateDialSink(1);
+  dial_sink1.sink().set_sink_id("dial:<de51d94921f15f8af6dbf65592bb3610>");
+  MediaSinkInternal dial_sink2 = CreateDialSink(2);
+  std::vector<MediaSinkInternal> available_sinks = {dial_sink1, dial_sink2};
+
+  MediaSinkServiceStatus status;
+  status.UpdateAvailableSinks(MediaRouteProviderId::EXTENSION, "dial:youtube",
+                              available_sinks);
+
+  std::string str = status.GetStatusAsJSONString();
+  VerifyEqualJSONString(expected_str, str);
+}
+
+TEST(MediaSinkServiceStatusTest, TestGetStatusAsJSONStringEmptyAvailability) {
+  const char expected_str[] = R"(
+      {
+        "available_sinks": { },
+        "discovered_sinks": {
+          "dial": [
+            {
+              "app_url":"http://192.168.0.101/apps",
+              "icon_type":7,
+              "id":"id1",
+              "ip_address":"192.168.0.101",
+              "model_name":"model name 1",
+              "name":"friendly name 1"
+            },
+            {
+              "app_url":"http://192.168.0.102/apps",
+              "icon_type":7,
+              "id":"id2",
+              "ip_address":"192.168.0.102",
+              "model_name":"model name 2",
+              "name":"friendly name 2"
+            }
+          ]
+        }
+      })";
+
+  MediaSinkInternal dial_sink1 = CreateDialSink(1);
+  MediaSinkInternal dial_sink2 = CreateDialSink(2);
+  std::vector<MediaSinkInternal> discovered_sinks = {dial_sink1, dial_sink2};
+
+  MediaSinkServiceStatus status;
+  status.UpdateDiscoveredSinks("dial", discovered_sinks);
+
+  std::string str = status.GetStatusAsJSONString();
+  VerifyEqualJSONString(expected_str, str);
+}
+
+TEST(MediaSinkServiceStatusTest, TestGetStatusAsJSONStringMultipleProviders) {
+  const char expected_str[] = R"(
+      {
+        "available_sinks": {
+          "EXTENSION:cast:netflix" : ["id2"],
+          "EXTENSION:dial:youtube" : ["id1"]
+        },
+        "discovered_sinks": {
+          "dial": [
+            {
+              "app_url":"http://192.168.0.101/apps",
+              "icon_type":7,
+              "id":"id1",
+              "ip_address":"192.168.0.101",
+              "model_name":"model name 1",
+              "name":"friendly name 1"
+            }
+          ],
+          "cast": [
+            {
+              "app_url":"http://192.168.0.102/apps",
+              "icon_type":7,
+              "id":"id2",
+              "ip_address":"192.168.0.102",
+              "model_name":"model name 2",
+              "name":"friendly name 2"
+            }
+          ]
+        }
+      })";
+
+  MediaSinkInternal dial_sink1 = CreateDialSink(1);
+  MediaSinkInternal dial_sink2 = CreateDialSink(2);
+  std::vector<MediaSinkInternal> sinks1 = {dial_sink1};
+  std::vector<MediaSinkInternal> sinks2 = {dial_sink2};
+
+  MediaSinkServiceStatus status;
+  status.UpdateDiscoveredSinks("dial", sinks1);
+  status.UpdateDiscoveredSinks("cast", sinks2);
+  status.UpdateAvailableSinks(MediaRouteProviderId::EXTENSION, "dial:youtube",
+                              sinks1);
+  status.UpdateAvailableSinks(MediaRouteProviderId::EXTENSION, "cast:netflix",
+                              sinks2);
+
+  std::string str = status.GetStatusAsJSONString();
+  VerifyEqualJSONString(expected_str, str);
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/router/providers/cast/cast_app_availability_tracker.cc b/chrome/browser/media/router/providers/cast/cast_app_availability_tracker.cc
new file mode 100644
index 0000000..d8ce89a7
--- /dev/null
+++ b/chrome/browser/media/router/providers/cast/cast_app_availability_tracker.cc
@@ -0,0 +1,128 @@
+// 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/media/router/providers/cast/cast_app_availability_tracker.h"
+
+namespace media_router {
+
+CastAppAvailabilityTracker::CastAppAvailabilityTracker() {}
+CastAppAvailabilityTracker::~CastAppAvailabilityTracker() = default;
+
+base::flat_set<std::string> CastAppAvailabilityTracker::RegisterSource(
+    const CastMediaSource& source) {
+  if (base::ContainsKey(registered_sources_, source.source_id()))
+    return base::flat_set<std::string>();
+
+  registered_sources_.emplace(source.source_id(), source);
+
+  base::flat_set<std::string> new_app_ids;
+  for (const auto& app_info : source.app_infos()) {
+    const auto& app_id = app_info.app_id;
+    if (++registration_count_by_app_id_[app_id] == 1)
+      new_app_ids.insert(app_id);
+  }
+  return new_app_ids;
+}
+
+void CastAppAvailabilityTracker::UnregisterSource(
+    const CastMediaSource& source) {
+  auto it = registered_sources_.find(source.source_id());
+  if (it == registered_sources_.end())
+    return;
+
+  for (const auto& app_info : it->second.app_infos()) {
+    const std::string& app_id = app_info.app_id;
+    auto count_it = registration_count_by_app_id_.find(app_id);
+    DCHECK(count_it != registration_count_by_app_id_.end());
+    if (--(count_it->second) == 0)
+      registration_count_by_app_id_.erase(count_it);
+  }
+
+  registered_sources_.erase(it);
+}
+
+std::vector<CastMediaSource> CastAppAvailabilityTracker::UpdateAppAvailability(
+    const MediaSink::Id& sink_id,
+    const std::string& app_id,
+    AppAvailability availability) {
+  auto& availabilities = app_availabilities_[sink_id];
+  auto it = availabilities.find(app_id);
+
+  // Updated if value changes from unknown / unavailable to available, or from
+  // available to unavailble.
+  AppAvailability old_availability =
+      it != availabilities.end() ? it->second : AppAvailability::kUnavailable;
+  bool updated = old_availability != availability;
+  availabilities[app_id] = availability;
+
+  if (!updated)
+    return std::vector<CastMediaSource>();
+
+  std::vector<CastMediaSource> affected_sources;
+  for (const auto& source : registered_sources_) {
+    if (source.second.ContainsApp(app_id))
+      affected_sources.push_back(source.second);
+  }
+  return affected_sources;
+}
+
+std::vector<CastMediaSource> CastAppAvailabilityTracker::RemoveResultsForSink(
+    const MediaSink::Id& sink_id) {
+  auto it = app_availabilities_.find(sink_id);
+  if (it == app_availabilities_.end())
+    return std::vector<CastMediaSource>();
+
+  // Find all app IDs that were available on the sink.
+  std::vector<std::string> affected_app_ids;
+  for (const auto& availability : it->second) {
+    if (availability.second == AppAvailability::kAvailable)
+      affected_app_ids.push_back(availability.first);
+  }
+
+  // Find all registered sources whose query results need to be updated.
+  std::vector<CastMediaSource> affected_sources;
+  for (const auto& source : registered_sources_) {
+    if (source.second.ContainsAnyAppFrom(affected_app_ids))
+      affected_sources.push_back(source.second);
+  }
+
+  app_availabilities_.erase(it);
+  return affected_sources;
+}
+
+bool CastAppAvailabilityTracker::IsAvailabilityKnown(
+    const MediaSink::Id& sink_id,
+    const std::string& app_id) const {
+  auto availabilities_it = app_availabilities_.find(sink_id);
+  return availabilities_it != app_availabilities_.end() &&
+         base::ContainsKey(availabilities_it->second, app_id);
+}
+
+std::vector<std::string> CastAppAvailabilityTracker::GetRegisteredApps() const {
+  std::vector<std::string> registered_apps;
+  for (const auto& app_ids_and_count : registration_count_by_app_id_)
+    registered_apps.push_back(app_ids_and_count.first);
+
+  return registered_apps;
+}
+
+base::flat_set<MediaSink::Id> CastAppAvailabilityTracker::GetAvailableSinks(
+    const CastMediaSource& source) const {
+  base::flat_set<MediaSink::Id> sink_ids;
+  // For each sink, check if there is at least one available app in |source|.
+  for (const auto& availabilities : app_availabilities_) {
+    for (const auto& app_info : source.app_infos()) {
+      const auto& availabilities_map = availabilities.second;
+      auto availability_it = availabilities_map.find(app_info.app_id);
+      if (availability_it != availabilities_map.end() &&
+          availability_it->second == AppAvailability::kAvailable) {
+        sink_ids.insert(availabilities.first);
+        break;
+      }
+    }
+  }
+  return sink_ids;
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/router/providers/cast/cast_app_availability_tracker.h b/chrome/browser/media/router/providers/cast/cast_app_availability_tracker.h
new file mode 100644
index 0000000..0c66ee2
--- /dev/null
+++ b/chrome/browser/media/router/providers/cast/cast_app_availability_tracker.h
@@ -0,0 +1,114 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_CAST_APP_AVAILABILITY_TRACKER_H_
+#define CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_CAST_APP_AVAILABILITY_TRACKER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+#include "chrome/common/media_router/discovery/media_sink_internal.h"
+#include "chrome/common/media_router/media_source.h"
+#include "chrome/common/media_router/providers/cast/cast_media_source.h"
+
+namespace media_router {
+
+// Possible app availability values.
+// TODO(crbug.com/809249): Determine whether it's necessary to implement RESCAN
+// statuses, or just do rescan triggered on user gesture.
+enum class AppAvailability { kUnavailable, kAvailable };
+
+// Tracks sink queries and their extracted Cast app IDs and their availabilities
+// on discovered sinks.
+// Example usage:
+///
+// (1) A page creates a PresentationRequest with URL "cast:foo". To register the
+// source to be tracked:
+//   CastAppAvailabilityTracker tracker;
+//   auto source = CastMediaSource::From("cast:foo");
+//   auto new_app_ids = tracker.RegisterSource(*source);
+//
+// (2) The set of app IDs returned by the tracker can then be used by the caller
+// to send an app availability request to each of the discovered sinks.
+//
+// (3) Once the caller knows the availability value for a (sink, app) pair, it
+// may inform the tracker to update its results:
+//   auto affected_sources =
+//       tracker.UpdateAppAvailability(sink_id, app_id, availability);
+//
+// (4) The tracker returns a subset of discovered sources that were affected by
+// the update. The caller can then call |GetAvailableSinks()| to get the updated
+// results for each affected source.
+//
+// (5a): At any time, the caller may call |RemoveResultsForSink()| to remove
+// cached results pertaining to the sink, when it detects that a sink is removed
+// or no longer valid.
+//
+// (5b): At any time, the caller may call |GetAvailableSinks()| (even before the
+// source is registered) to determine if there are cached results available.
+class CastAppAvailabilityTracker {
+ public:
+  CastAppAvailabilityTracker();
+  ~CastAppAvailabilityTracker();
+
+  // Registers |source| with the tracker. Returns a list of new app IDs that
+  // were previously not known to the tracker.
+  base::flat_set<std::string> RegisterSource(const CastMediaSource& source);
+
+  // Unregisters the source given by |source| with the tracker.
+  void UnregisterSource(const CastMediaSource& source);
+
+  // Updates the availability of |app_id| on |sink_id| with |availability|.
+  // Returns a list of registered CastMediaSources for which the set of
+  // available sinks might have been updated by this call. The caller should
+  // call |GetAvailableSinks| with the returned CastMediaSources to get the
+  // updated lists.
+  std::vector<CastMediaSource> UpdateAppAvailability(
+      const MediaSink::Id& sink_id,
+      const std::string& app_id,
+      AppAvailability availability);
+
+  // Removes all results associated with |sink_id|, i.e. when the sink becomes
+  // invalid.
+  // Returns a list of registered CastMediaSources for which the set of
+  // available sinks might have been updated by this call. The caller should
+  // call |GetAvailableSinks| with the returned CastMediaSources to get the
+  // updated lists.
+  std::vector<CastMediaSource> RemoveResultsForSink(
+      const MediaSink::Id& sink_id);
+
+  // Returns whether availability status is known for |app_id| on |sink_id|.
+  bool IsAvailabilityKnown(const MediaSink::Id& sink_id,
+                           const std::string& app_id) const;
+
+  // Returns a list of registered app IDs.
+  std::vector<std::string> GetRegisteredApps() const;
+
+  // Returns a list of sink IDs compatible with |source|, using the current
+  // availability info.
+  base::flat_set<MediaSink::Id> GetAvailableSinks(
+      const CastMediaSource& source) const;
+
+ private:
+  // App ID to availability value.
+  using AppAvailabilityMap = base::flat_map<std::string, AppAvailability>;
+
+  // Registered sources and corresponding CastMediaSource's.
+  base::flat_map<MediaSource::Id, CastMediaSource> registered_sources_;
+
+  // App IDs tracked and the number of registered sources containing them.
+  base::flat_map<std::string, int> registration_count_by_app_id_;
+
+  // IDs and app availabilities of known sinks.
+  base::flat_map<MediaSink::Id, AppAvailabilityMap> app_availabilities_;
+
+  DISALLOW_COPY_AND_ASSIGN(CastAppAvailabilityTracker);
+};
+
+}  // namespace media_router
+
+#endif  // CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_CAST_APP_AVAILABILITY_TRACKER_H_
diff --git a/chrome/browser/media/router/providers/cast/cast_app_availability_tracker_unittest.cc b/chrome/browser/media/router/providers/cast/cast_app_availability_tracker_unittest.cc
new file mode 100644
index 0000000..8ea4023
--- /dev/null
+++ b/chrome/browser/media/router/providers/cast/cast_app_availability_tracker_unittest.cc
@@ -0,0 +1,177 @@
+// 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/media/router/providers/cast/cast_app_availability_tracker.h"
+
+#include "chrome/common/media_router/providers/cast/cast_media_source.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media_router {
+
+namespace {
+
+MATCHER_P(CastMediaSourcesEqual, expected, "") {
+  if (expected.size() != arg.size())
+    return false;
+  return std::equal(
+      expected.begin(), expected.end(), arg.begin(),
+      [](const CastMediaSource& source1, const CastMediaSource& source2) {
+        return source1.source_id() == source2.source_id();
+      });
+}
+
+}  // namespace
+
+class CastAppAvailabilityTrackerTest : public testing::Test {
+ public:
+  CastAppAvailabilityTrackerTest() {}
+  ~CastAppAvailabilityTrackerTest() override = default;
+
+ protected:
+  CastAppAvailabilityTracker tracker_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CastAppAvailabilityTrackerTest);
+};
+
+TEST_F(CastAppAvailabilityTrackerTest, RegisterSource) {
+  auto source1 = CastMediaSource::From("cast:AAAAAAAA?clientId=1");
+  ASSERT_TRUE(source1);
+  auto source2 = CastMediaSource::From("cast:AAAAAAAA?clientId=2");
+  ASSERT_TRUE(source2);
+
+  base::flat_set<std::string> expected_app_ids = {"AAAAAAAA"};
+  EXPECT_EQ(expected_app_ids, tracker_.RegisterSource(*source1));
+
+  expected_app_ids.clear();
+  EXPECT_EQ(expected_app_ids, tracker_.RegisterSource(*source1));
+  EXPECT_EQ(expected_app_ids, tracker_.RegisterSource(*source2));
+
+  tracker_.UnregisterSource(*source1);
+  tracker_.UnregisterSource(*source2);
+
+  expected_app_ids = {"AAAAAAAA"};
+  EXPECT_EQ(expected_app_ids, tracker_.RegisterSource(*source1));
+  EXPECT_EQ(expected_app_ids, tracker_.GetRegisteredApps());
+}
+
+TEST_F(CastAppAvailabilityTrackerTest, RegisterSourceReturnsMultipleAppIds) {
+  auto source1 = CastMediaSource::From("urn:x-org.chromium.media:source:tab:1");
+  ASSERT_TRUE(source1);
+
+  // Mirorring app ids.
+  base::flat_set<std::string> expected_app_ids = {"0F5096E8", "85CDB22F"};
+  EXPECT_EQ(expected_app_ids, tracker_.RegisterSource(*source1));
+  EXPECT_EQ(expected_app_ids, tracker_.GetRegisteredApps());
+}
+
+TEST_F(CastAppAvailabilityTrackerTest, MultipleAppIdsAlreadyTrackingOne) {
+  // One of the mirroring app IDs.
+  auto source1 = CastMediaSource::From("cast:0F5096E8");
+  ASSERT_TRUE(source1);
+
+  base::flat_set<std::string> new_app_ids = {"0F5096E8"};
+  base::flat_set<std::string> registered_app_ids = {"0F5096E8"};
+  EXPECT_EQ(new_app_ids, tracker_.RegisterSource(*source1));
+  EXPECT_EQ(registered_app_ids, tracker_.GetRegisteredApps());
+
+  auto source2 = CastMediaSource::From("urn:x-org.chromium.media:source:tab:1");
+  ASSERT_TRUE(source2);
+
+  new_app_ids = {"85CDB22F"};
+  registered_app_ids = {"0F5096E8", "85CDB22F"};
+
+  EXPECT_EQ(new_app_ids, tracker_.RegisterSource(*source2));
+  EXPECT_EQ(registered_app_ids, tracker_.GetRegisteredApps());
+}
+
+TEST_F(CastAppAvailabilityTrackerTest, UpdateAppAvailability) {
+  auto source1 = CastMediaSource::From("cast:AAAAAAAA?clientId=1");
+  ASSERT_TRUE(source1);
+  auto source2 = CastMediaSource::From("cast:AAAAAAAA?clientId=2");
+  ASSERT_TRUE(source2);
+  auto source3 = CastMediaSource::From("cast:BBBBBBBB?clientId=3");
+  ASSERT_TRUE(source3);
+
+  tracker_.RegisterSource(*source3);
+
+  // |source3| not affected.
+  EXPECT_THAT(tracker_.UpdateAppAvailability("sinkId1", "AAAAAAAA",
+                                             AppAvailability::kAvailable),
+              CastMediaSourcesEqual(std::vector<CastMediaSource>()));
+
+  base::flat_set<MediaSink::Id> sinks_1 = {"sinkId1"};
+  base::flat_set<MediaSink::Id> sinks_1_2 = {"sinkId1", "sinkId2"};
+  std::vector<CastMediaSource> sources_1 = {*source1};
+  std::vector<CastMediaSource> sources_1_2 = {*source1, *source2};
+
+  // Tracker returns available sinks even though sources aren't registered.
+  EXPECT_EQ(sinks_1, tracker_.GetAvailableSinks(*source1));
+  EXPECT_EQ(sinks_1, tracker_.GetAvailableSinks(*source2));
+  EXPECT_TRUE(tracker_.GetAvailableSinks(*source3).empty());
+
+  tracker_.RegisterSource(*source1);
+  // Only |source1| is registered for this app.
+  EXPECT_THAT(tracker_.UpdateAppAvailability("sinkId2", "AAAAAAAA",
+                                             AppAvailability::kAvailable),
+              CastMediaSourcesEqual(sources_1));
+  EXPECT_EQ(sinks_1_2, tracker_.GetAvailableSinks(*source1));
+  EXPECT_EQ(sinks_1_2, tracker_.GetAvailableSinks(*source2));
+  EXPECT_TRUE(tracker_.GetAvailableSinks(*source3).empty());
+
+  tracker_.RegisterSource(*source2);
+  EXPECT_THAT(tracker_.UpdateAppAvailability("sinkId2", "AAAAAAAA",
+                                             AppAvailability::kUnavailable),
+              CastMediaSourcesEqual(sources_1_2));
+  EXPECT_EQ(sinks_1, tracker_.GetAvailableSinks(*source1));
+  EXPECT_EQ(sinks_1, tracker_.GetAvailableSinks(*source2));
+  EXPECT_TRUE(tracker_.GetAvailableSinks(*source3).empty());
+}
+
+TEST_F(CastAppAvailabilityTrackerTest, IsAvailabilityKnown) {
+  EXPECT_FALSE(tracker_.IsAvailabilityKnown("sinkId1", "AAAAAAAA"));
+
+  tracker_.UpdateAppAvailability("sinkId1", "AAAAAAAA",
+                                 AppAvailability::kAvailable);
+  EXPECT_TRUE(tracker_.IsAvailabilityKnown("sinkId1", "AAAAAAAA"));
+  EXPECT_FALSE(tracker_.IsAvailabilityKnown("sinkId1", "BBBBBBBB"));
+  EXPECT_FALSE(tracker_.IsAvailabilityKnown("sinkId2", "AAAAAAAA"));
+
+  tracker_.UpdateAppAvailability("sinkId1", "AAAAAAAA",
+                                 AppAvailability::kUnavailable);
+  EXPECT_TRUE(tracker_.IsAvailabilityKnown("sinkId1", "AAAAAAAA"));
+  EXPECT_FALSE(tracker_.IsAvailabilityKnown("sinkId1", "BBBBBBBB"));
+  EXPECT_FALSE(tracker_.IsAvailabilityKnown("sinkId2", "AAAAAAAA"));
+
+  tracker_.UpdateAppAvailability("sinkId2", "AAAAAAAA",
+                                 AppAvailability::kUnavailable);
+  EXPECT_TRUE(tracker_.IsAvailabilityKnown("sinkId1", "AAAAAAAA"));
+  EXPECT_FALSE(tracker_.IsAvailabilityKnown("sinkId1", "BBBBBBBB"));
+  EXPECT_TRUE(tracker_.IsAvailabilityKnown("sinkId2", "AAAAAAAA"));
+}
+
+TEST_F(CastAppAvailabilityTrackerTest, RemoveResultsForSink) {
+  auto source1 = CastMediaSource::From("cast:AAAAAAAA?clientId=1");
+  ASSERT_TRUE(source1);
+
+  tracker_.UpdateAppAvailability("sinkId1", "AAAAAAAA",
+                                 AppAvailability::kAvailable);
+  EXPECT_TRUE(tracker_.IsAvailabilityKnown("sinkId1", "AAAAAAAA"));
+
+  base::flat_set<MediaSink::Id> expected_sink_ids = {"sinkId1"};
+  EXPECT_EQ(expected_sink_ids, tracker_.GetAvailableSinks(*source1));
+
+  // Unrelated sink ID.
+  tracker_.RemoveResultsForSink("sinkId2");
+  EXPECT_TRUE(tracker_.IsAvailabilityKnown("sinkId1", "AAAAAAAA"));
+  EXPECT_EQ(expected_sink_ids, tracker_.GetAvailableSinks(*source1));
+
+  expected_sink_ids.clear();
+  tracker_.RemoveResultsForSink("sinkId1");
+  EXPECT_FALSE(tracker_.IsAvailabilityKnown("sinkId1", "AAAAAAAA"));
+  EXPECT_EQ(expected_sink_ids, tracker_.GetAvailableSinks(*source1));
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/router/providers/cast/cast_app_discovery_service.cc b/chrome/browser/media/router/providers/cast/cast_app_discovery_service.cc
new file mode 100644
index 0000000..f4e3277
--- /dev/null
+++ b/chrome/browser/media/router/providers/cast/cast_app_discovery_service.cc
@@ -0,0 +1,165 @@
+// 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/media/router/providers/cast/cast_app_discovery_service.h"
+
+#include "components/cast_channel/cast_message_handler.h"
+#include "components/cast_channel/cast_socket.h"
+#include "components/cast_channel/cast_socket_service.h"
+
+namespace media_router {
+
+CastAppDiscoveryService::CastAppDiscoveryService(
+    cast_channel::CastMessageHandler* message_handler,
+    cast_channel::CastSocketService* socket_service)
+    : message_handler_(message_handler),
+      socket_service_(socket_service),
+      weak_ptr_factory_(this) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+CastAppDiscoveryService::~CastAppDiscoveryService() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+CastAppDiscoveryService::Subscription
+CastAppDiscoveryService::StartObservingMediaSinks(
+    const CastMediaSource& source,
+    const SinkQueryCallback& callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  const MediaSource::Id& source_id = source.source_id();
+
+  // Returned cached results immediately, if available.
+  base::flat_set<MediaSink::Id> cached_sink_ids =
+      availability_tracker_.GetAvailableSinks(source);
+  if (!cached_sink_ids.empty())
+    callback.Run(source_id, GetSinksByIds(cached_sink_ids));
+
+  auto& callback_list = sink_queries_[source_id];
+  if (!callback_list) {
+    callback_list = std::make_unique<SinkQueryCallbackList>();
+    callback_list->set_removal_callback(
+        base::BindRepeating(&CastAppDiscoveryService::MaybeRemoveSinkQueryEntry,
+                            base::Unretained(this), source));
+
+    // Note: even though we retain availability results for an app unregistered
+    // from the tracker, we will send app availability requests again when it
+    // is re-registered. This gives us a chance to refresh the results in case
+    // it changed.
+    base::flat_set<std::string> new_app_ids =
+        availability_tracker_.RegisterSource(source);
+    for (const auto& app_id : new_app_ids) {
+      // Note: The following logic assumes |sinks_| will not change as it is
+      // being iterated.
+      for (const auto& sink : sinks_) {
+        int channel_id = sink.second.cast_data().cast_channel_id;
+        cast_channel::CastSocket* socket =
+            socket_service_->GetSocket(channel_id);
+        if (!socket) {
+          DVLOG(1) << "Socket not found for id " << channel_id;
+          continue;
+        }
+
+        RequestAppAvailability(socket, app_id, sink.first);
+      }
+    }
+  }
+
+  return callback_list->Add(callback);
+}
+
+void CastAppDiscoveryService::MaybeRemoveSinkQueryEntry(
+    const CastMediaSource& source) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto it = sink_queries_.find(source.source_id());
+  CHECK(it != sink_queries_.end());
+
+  if (it->second->empty()) {
+    availability_tracker_.UnregisterSource(source);
+    sink_queries_.erase(it);
+  }
+}
+
+void CastAppDiscoveryService::OnSinkAddedOrUpdated(
+    const MediaSinkInternal& sink,
+    cast_channel::CastSocket* socket) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  const MediaSink::Id& sink_id = sink.sink().id();
+  sinks_.insert_or_assign(sink_id, sink);
+  for (const std::string& app_id : availability_tracker_.GetRegisteredApps()) {
+    if (availability_tracker_.IsAvailabilityKnown(sink_id, app_id))
+      continue;
+
+    RequestAppAvailability(socket, app_id, sink_id);
+  }
+}
+
+void CastAppDiscoveryService::OnSinkRemoved(const MediaSinkInternal& sink) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  const MediaSink::Id& sink_id = sink.sink().id();
+  sinks_.erase(sink_id);
+  UpdateSinkQueries(availability_tracker_.RemoveResultsForSink(sink_id));
+}
+
+void CastAppDiscoveryService::RequestAppAvailability(
+    cast_channel::CastSocket* socket,
+    const std::string& app_id,
+    const MediaSink::Id& sink_id) {
+  message_handler_->RequestAppAvailability(
+      socket, app_id,
+      base::BindOnce(&CastAppDiscoveryService::UpdateAppAvailability,
+                     weak_ptr_factory_.GetWeakPtr(), sink_id));
+}
+
+void CastAppDiscoveryService::UpdateAppAvailability(
+    const MediaSink::Id& sink_id,
+    const std::string& app_id,
+    cast_channel::GetAppAvailabilityResult result) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!base::ContainsKey(sinks_, sink_id))
+    return;
+
+  if (result == cast_channel::GetAppAvailabilityResult::kUnknown) {
+    // TODO(crbug.com/779892): Implement retry on user gesture.
+    DVLOG(2) << "App availability unknown for sink: " << sink_id
+             << ", app: " << app_id;
+    return;
+  }
+
+  AppAvailability availability =
+      result == cast_channel::GetAppAvailabilityResult::kAvailable
+          ? AppAvailability::kAvailable
+          : AppAvailability::kUnavailable;
+
+  DVLOG(1) << "App " << app_id << " on sink " << sink_id << " is "
+           << (availability == AppAvailability::kAvailable);
+  UpdateSinkQueries(availability_tracker_.UpdateAppAvailability(sink_id, app_id,
+                                                                availability));
+}
+
+void CastAppDiscoveryService::UpdateSinkQueries(
+    const std::vector<CastMediaSource>& sources) {
+  for (const auto& source : sources) {
+    const MediaSource::Id& source_id = source.source_id();
+    auto it = sink_queries_.find(source_id);
+    if (it == sink_queries_.end())
+      continue;
+    base::flat_set<MediaSink::Id> sink_ids =
+        availability_tracker_.GetAvailableSinks(source);
+    it->second->Notify(source_id, GetSinksByIds(sink_ids));
+  }
+}
+
+std::vector<MediaSinkInternal> CastAppDiscoveryService::GetSinksByIds(
+    const base::flat_set<MediaSink::Id>& sink_ids) const {
+  std::vector<MediaSinkInternal> sinks;
+  for (const auto& sink_id : sink_ids) {
+    auto it = sinks_.find(sink_id);
+    if (it != sinks_.end())
+      sinks.push_back(it->second);
+  }
+  return sinks;
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/router/providers/cast/cast_app_discovery_service.h b/chrome/browser/media/router/providers/cast/cast_app_discovery_service.h
new file mode 100644
index 0000000..3a4c3934
--- /dev/null
+++ b/chrome/browser/media/router/providers/cast/cast_app_discovery_service.h
@@ -0,0 +1,103 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_CAST_APP_DISCOVERY_SERVICE_H_
+#define CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_CAST_APP_DISCOVERY_SERVICE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/callback_list.h"
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
+#include "chrome/browser/media/router/providers/cast/cast_app_availability_tracker.h"
+#include "chrome/common/media_router/media_sink.h"
+#include "chrome/common/media_router/media_source.h"
+#include "chrome/common/media_router/providers/cast/cast_media_source.h"
+#include "components/cast_channel/cast_message_util.h"
+
+namespace cast_channel {
+class CastMessageHandler;
+class CastSocket;
+}  // namespace cast_channel
+
+namespace media_router {
+
+// Keeps track of sink queries and listens to CastMediaSinkServiceImpl for sink
+// updates, and issues app availability requests based on these signals. This
+// class may be created on any sequence. All other methods must be called on the
+// CastSocketService sequence.
+class CastAppDiscoveryService : public CastMediaSinkServiceImpl::Observer {
+ public:
+  using SinkQueryFunc = void(const MediaSource::Id& source_id,
+                             const std::vector<MediaSinkInternal>& sinks);
+  using SinkQueryCallback = base::RepeatingCallback<SinkQueryFunc>;
+  using SinkQueryCallbackList = base::CallbackList<SinkQueryFunc>;
+  using Subscription = std::unique_ptr<SinkQueryCallbackList::Subscription>;
+
+  CastAppDiscoveryService(cast_channel::CastMessageHandler* message_handler,
+                          cast_channel::CastSocketService* socket_service);
+  ~CastAppDiscoveryService() override;
+
+  // Adds a sink query for |source|. Results will be continuously returned via
+  // |callback| until the returned Subscription is destroyed by the caller.
+  // If there are cached results available, |callback| will be invoked before
+  // this method returns.
+  Subscription StartObservingMediaSinks(const CastMediaSource& source,
+                                        const SinkQueryCallback& callback);
+
+ private:
+  friend class CastAppDiscoveryServiceTest;
+
+  // CastMediaSinkServiceImpl::Observer
+  void OnSinkAddedOrUpdated(const MediaSinkInternal& sink,
+                            cast_channel::CastSocket* socket) override;
+  void OnSinkRemoved(const MediaSinkInternal& sink) override;
+
+  // Issues an app availability request for |app_id| to the sink given by
+  // |sink_id| via |socket|.
+  void RequestAppAvailability(cast_channel::CastSocket* socket,
+                              const std::string& app_id,
+                              const MediaSink::Id& sink_id);
+
+  // Updates the availability result for |sink_id| and |app_id| with |result|,
+  // and notifies callbacks with updated sink query results.
+  void UpdateAppAvailability(const MediaSink::Id& sink_id,
+                             const std::string& app_id,
+                             cast_channel::GetAppAvailabilityResult result);
+
+  // Updates the sink query results for |sources|.
+  void UpdateSinkQueries(const std::vector<CastMediaSource>& sources);
+
+  // Removes the entry from |sink_queries_| if there are no more callbacks
+  // associated with |source|.
+  void MaybeRemoveSinkQueryEntry(const CastMediaSource& source);
+
+  // Gets a list of sinks corresponding to |sink_ids|.
+  std::vector<MediaSinkInternal> GetSinksByIds(
+      const base::flat_set<MediaSink::Id>& sink_ids) const;
+
+  // Registered sink queries and their associated callbacks.
+  base::flat_map<MediaSource::Id, std::unique_ptr<SinkQueryCallbackList>>
+      sink_queries_;
+
+  cast_channel::CastMessageHandler* const message_handler_;
+  cast_channel::CastSocketService* const socket_service_;
+
+  CastAppAvailabilityTracker availability_tracker_;
+  base::flat_map<MediaSink::Id, MediaSinkInternal> sinks_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<CastAppDiscoveryService> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(CastAppDiscoveryService);
+};
+
+}  // namespace media_router
+
+#endif  // CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_CAST_APP_DISCOVERY_SERVICE_H_
diff --git a/chrome/browser/media/router/providers/cast/cast_app_discovery_service_unittest.cc b/chrome/browser/media/router/providers/cast/cast_app_discovery_service_unittest.cc
new file mode 100644
index 0000000..57250c80
--- /dev/null
+++ b/chrome/browser/media/router/providers/cast/cast_app_discovery_service_unittest.cc
@@ -0,0 +1,216 @@
+// 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/media/router/providers/cast/cast_app_discovery_service.h"
+
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "chrome/browser/media/router/test/test_helper.h"
+#include "chrome/common/media_router/providers/cast/cast_media_source.h"
+#include "components/cast_channel/cast_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Invoke;
+
+namespace media_router {
+
+class CastAppDiscoveryServiceTest : public testing::Test {
+ public:
+  CastAppDiscoveryServiceTest()
+      : task_runner_(base::MakeRefCounted<base::TestSimpleTaskRunner>()),
+        socket_service_(task_runner_),
+        message_handler_(&socket_service_),
+        app_discovery_service_(
+            std::make_unique<CastAppDiscoveryService>(&message_handler_,
+                                                      &socket_service_)),
+        source_a_1_(CastMediaSource::From("cast:AAAAAAAA?clientId=1")),
+        source_a_2_(CastMediaSource::From("cast:AAAAAAAA?clientId=2")),
+        source_b_1_(CastMediaSource::From("cast:BBBBBBBB?clientId=1")) {
+    ON_CALL(socket_service_, GetSocket(_))
+        .WillByDefault(testing::Return(&socket_));
+  }
+
+  ~CastAppDiscoveryServiceTest() override { task_runner_->RunPendingTasks(); }
+
+  MOCK_METHOD2(OnSinkQueryUpdated,
+               void(const MediaSource::Id&,
+                    const std::vector<MediaSinkInternal>&));
+
+  void AddOrUpdateSink(const MediaSinkInternal& sink) {
+    app_discovery_service_->OnSinkAddedOrUpdated(sink, &socket_);
+  }
+
+  void RemoveSink(const MediaSinkInternal& sink) {
+    app_discovery_service_->OnSinkRemoved(sink);
+  }
+
+  CastAppDiscoveryService::Subscription StartObservingMediaSinksInitially() {
+    auto subscription = app_discovery_service_->StartObservingMediaSinks(
+        *source_a_1_,
+        base::BindRepeating(&CastAppDiscoveryServiceTest::OnSinkQueryUpdated,
+                            base::Unretained(this)));
+    task_runner_->RunPendingTasks();
+    return subscription;
+  }
+
+ protected:
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  cast_channel::MockCastSocketService socket_service_;
+  cast_channel::MockCastSocket socket_;
+  cast_channel::MockCastMessageHandler message_handler_;
+  std::unique_ptr<CastAppDiscoveryService> app_discovery_service_;
+  std::unique_ptr<CastMediaSource> source_a_1_;
+  std::unique_ptr<CastMediaSource> source_a_2_;
+  std::unique_ptr<CastMediaSource> source_b_1_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CastAppDiscoveryServiceTest);
+};
+
+TEST_F(CastAppDiscoveryServiceTest, StartObservingMediaSinks) {
+  auto subscription1 = StartObservingMediaSinksInitially();
+
+  // Adding a sink after app registered causes app availability request to be
+  // sent.
+  MediaSinkInternal sink1 = CreateCastSink(1);
+  cast_channel::GetAppAvailabilityCallback cb;
+  EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _))
+      .WillOnce(
+          Invoke([&cb](cast_channel::CastSocket*, const std::string&,
+                       cast_channel::GetAppAvailabilityCallback& callback) {
+            cb = std::move(callback);
+          }));
+
+  AddOrUpdateSink(sink1);
+
+  // Same app ID should not trigger another request.
+  EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, _, _)).Times(0);
+  auto subscription2 = app_discovery_service_->StartObservingMediaSinks(
+      *source_a_2_,
+      base::BindRepeating(&CastAppDiscoveryServiceTest::OnSinkQueryUpdated,
+                          base::Unretained(this)));
+
+  std::vector<MediaSinkInternal> sinks_1 = {sink1};
+  EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_1_->source_id(), sinks_1));
+  EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_2_->source_id(), sinks_1));
+  std::move(cb).Run("AAAAAAAA",
+                    cast_channel::GetAppAvailabilityResult::kAvailable);
+
+  // No more updates for |source_a_1_|.
+  subscription1.reset();
+  EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_1_->source_id(), _)).Times(0);
+  EXPECT_CALL(*this,
+              OnSinkQueryUpdated(source_a_2_->source_id(), testing::IsEmpty()));
+  RemoveSink(sink1);
+}
+
+TEST_F(CastAppDiscoveryServiceTest, StartObservingMediaSinksAfterSinkAdded) {
+  // No registered apps.
+  MediaSinkInternal sink1 = CreateCastSink(1);
+  EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, _, _)).Times(0);
+  AddOrUpdateSink(sink1);
+
+  EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _));
+  auto subscription1 = app_discovery_service_->StartObservingMediaSinks(
+      *source_a_1_,
+      base::BindRepeating(&CastAppDiscoveryServiceTest::OnSinkQueryUpdated,
+                          base::Unretained(this)));
+
+  EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "BBBBBBBB", _));
+  auto subscription2 = app_discovery_service_->StartObservingMediaSinks(
+      *source_b_1_,
+      base::BindRepeating(&CastAppDiscoveryServiceTest::OnSinkQueryUpdated,
+                          base::Unretained(this)));
+
+  // Adding new sink causes availability requests for 2 apps to be sent to the
+  // new sink.
+  MediaSinkInternal sink2 = CreateCastSink(2);
+  EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _));
+  EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "BBBBBBBB", _));
+  AddOrUpdateSink(sink2);
+}
+
+TEST_F(CastAppDiscoveryServiceTest, StartObservingMediaSinksCachedValue) {
+  auto subscription1 = StartObservingMediaSinksInitially();
+
+  // Adding a sink after app registered causes app availability request to be
+  // sent.
+  MediaSinkInternal sink1 = CreateCastSink(1);
+  cast_channel::GetAppAvailabilityCallback cb;
+  EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _))
+      .WillOnce(
+          Invoke([&cb](cast_channel::CastSocket*, const std::string&,
+                       cast_channel::GetAppAvailabilityCallback& callback) {
+            cb = std::move(callback);
+          }));
+  AddOrUpdateSink(sink1);
+
+  std::vector<MediaSinkInternal> sinks_1 = {sink1};
+  EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_1_->source_id(), sinks_1));
+  std::move(cb).Run("AAAAAAAA",
+                    cast_channel::GetAppAvailabilityResult::kAvailable);
+
+  // Same app ID should not trigger another request, but it should return
+  // cached value.
+  EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, _, _)).Times(0);
+  EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_2_->source_id(), sinks_1));
+  auto subscription2 = app_discovery_service_->StartObservingMediaSinks(
+      *source_a_2_,
+      base::BindRepeating(&CastAppDiscoveryServiceTest::OnSinkQueryUpdated,
+                          base::Unretained(this)));
+
+  // Same source as |source_a_1_|. The callback will be invoked.
+  auto source3 = CastMediaSource::From("cast:AAAAAAAA?clientId=1");
+  ASSERT_TRUE(source3);
+  EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, _, _)).Times(0);
+  EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_1_->source_id(), sinks_1));
+  auto subscription3 = app_discovery_service_->StartObservingMediaSinks(
+      *source3,
+      base::BindRepeating(&CastAppDiscoveryServiceTest::OnSinkQueryUpdated,
+                          base::Unretained(this)));
+}
+
+TEST_F(CastAppDiscoveryServiceTest, AvailabilityUnknownOrUnavailable) {
+  auto subscription1 = StartObservingMediaSinksInitially();
+
+  // Adding a sink after app registered causes app availability request to be
+  // sent.
+  MediaSinkInternal sink1 = CreateCastSink(1);
+  EXPECT_CALL(*this, OnSinkQueryUpdated(_, _)).Times(0);
+  EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _))
+      .WillOnce(Invoke([](cast_channel::CastSocket*, const std::string&,
+                          cast_channel::GetAppAvailabilityCallback& callback) {
+        std::move(callback).Run(
+            "AAAAAAAA", cast_channel::GetAppAvailabilityResult::kUnknown);
+      }));
+  AddOrUpdateSink(sink1);
+
+  // Sink updated and unknown app availability will cause request to be sent
+  // again.
+  EXPECT_CALL(*this, OnSinkQueryUpdated(_, _)).Times(0);
+  EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _))
+      .WillOnce(Invoke([](cast_channel::CastSocket*, const std::string&,
+                          cast_channel::GetAppAvailabilityCallback& callback) {
+        std::move(callback).Run(
+            "AAAAAAAA", cast_channel::GetAppAvailabilityResult::kUnavailable);
+      }));
+  AddOrUpdateSink(sink1);
+
+  // Known availability -- no request sent.
+  EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _))
+      .Times(0);
+  AddOrUpdateSink(sink1);
+
+  // Removing the sink will also remove previous availability information.
+  // Next time sink is added, request will be sent.
+  EXPECT_CALL(*this, OnSinkQueryUpdated(_, _)).Times(0);
+  RemoveSink(sink1);
+
+  EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _));
+  AddOrUpdateSink(sink1);
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc b/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc
new file mode 100644
index 0000000..7c097c3
--- /dev/null
+++ b/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc
@@ -0,0 +1,230 @@
+// 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/media/router/providers/cast/cast_media_route_provider.h"
+
+#include "base/stl_util.h"
+#include "chrome/common/media_router/providers/cast/cast_media_source.h"
+#include "url/origin.h"
+
+namespace media_router {
+
+namespace {
+
+static constexpr MediaRouteProviderId kProviderId = MediaRouteProviderId::CAST;
+
+// Returns a list of origins that are valid for |source_id|. An empty list
+// means all origins are valid.
+std::vector<url::Origin> GetOrigins(const MediaSource::Id& source_id) {
+  // Use of the mirroring app as a Cast URL is permitted for Slides as a
+  // temporary workaround only. The eventual goal is to support their usecase
+  // using generic Presentation API.
+  // See also cast_media_source.cc.
+  static const char kMirroringAppPrefix[] = "cast:0F5096E8";
+  return base::StartsWith(source_id, kMirroringAppPrefix,
+                          base::CompareCase::SENSITIVE)
+             ? std::vector<url::Origin>(
+                   {url::Origin::Create(GURL("https://docs.google.com"))})
+             : std::vector<url::Origin>();
+}
+
+}  // namespace
+
+CastMediaRouteProvider::CastMediaRouteProvider(
+    mojom::MediaRouteProviderRequest request,
+    mojom::MediaRouterPtrInfo media_router,
+    CastAppDiscoveryService* app_discovery_service,
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
+    : binding_(this), app_discovery_service_(app_discovery_service) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+  DCHECK(app_discovery_service_);
+
+  task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(&CastMediaRouteProvider::Init, base::Unretained(this),
+                     std::move(request), std::move(media_router)));
+}
+
+void CastMediaRouteProvider::Init(mojom::MediaRouteProviderRequest request,
+                                  mojom::MediaRouterPtrInfo media_router) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  binding_.Bind(std::move(request));
+  media_router_.Bind(std::move(media_router));
+
+  // TODO(crbug.com/816702): This needs to be set properly according to sinks
+  // discovered.
+  media_router_->OnSinkAvailabilityUpdated(
+      kProviderId, mojom::MediaRouter::SinkAvailability::PER_SOURCE);
+}
+
+CastMediaRouteProvider::~CastMediaRouteProvider() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(sink_queries_.empty());
+}
+
+void CastMediaRouteProvider::CreateRoute(const std::string& media_source,
+                                         const std::string& sink_id,
+                                         const std::string& presentation_id,
+                                         const url::Origin& origin,
+                                         int32_t tab_id,
+                                         base::TimeDelta timeout,
+                                         bool incognito,
+                                         CreateRouteCallback callback) {
+  NOTIMPLEMENTED();
+  std::move(callback).Run(
+      base::nullopt, std::string("Not implemented"),
+      RouteRequestResult::ResultCode::NO_SUPPORTED_PROVIDER);
+}
+
+void CastMediaRouteProvider::JoinRoute(const std::string& media_source,
+                                       const std::string& presentation_id,
+                                       const url::Origin& origin,
+                                       int32_t tab_id,
+                                       base::TimeDelta timeout,
+                                       bool incognito,
+                                       JoinRouteCallback callback) {
+  NOTIMPLEMENTED();
+  std::move(callback).Run(
+      base::nullopt, std::string("Not implemented"),
+      RouteRequestResult::ResultCode::NO_SUPPORTED_PROVIDER);
+}
+
+void CastMediaRouteProvider::ConnectRouteByRouteId(
+    const std::string& media_source,
+    const std::string& route_id,
+    const std::string& presentation_id,
+    const url::Origin& origin,
+    int32_t tab_id,
+    base::TimeDelta timeout,
+    bool incognito,
+    ConnectRouteByRouteIdCallback callback) {
+  NOTIMPLEMENTED();
+  std::move(callback).Run(
+      base::nullopt, std::string("Not implemented"),
+      RouteRequestResult::ResultCode::NO_SUPPORTED_PROVIDER);
+}
+
+void CastMediaRouteProvider::TerminateRoute(const std::string& route_id,
+                                            TerminateRouteCallback callback) {
+  NOTIMPLEMENTED();
+  std::move(callback).Run(
+      std::string("Not implemented"),
+      RouteRequestResult::ResultCode::NO_SUPPORTED_PROVIDER);
+}
+
+void CastMediaRouteProvider::SendRouteMessage(
+    const std::string& media_route_id,
+    const std::string& message,
+    SendRouteMessageCallback callback) {
+  NOTIMPLEMENTED();
+  std::move(callback).Run(false);
+}
+
+void CastMediaRouteProvider::SendRouteBinaryMessage(
+    const std::string& media_route_id,
+    const std::vector<uint8_t>& data,
+    SendRouteBinaryMessageCallback callback) {
+  NOTIMPLEMENTED();
+  std::move(callback).Run(false);
+}
+
+void CastMediaRouteProvider::StartObservingMediaSinks(
+    const std::string& media_source) {
+  DVLOG(1) << __func__ << ", media_source: " << media_source;
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (base::ContainsKey(sink_queries_, media_source))
+    return;
+
+  std::unique_ptr<CastMediaSource> cast_source =
+      CastMediaSource::From(media_source);
+  if (!cast_source)
+    return;
+
+  // A broadcast request is not an actual sink query; it is used to send a
+  // app precache message to receivers.
+  if (cast_source->broadcast_request()) {
+    // TODO(crbug.com/809249): Implement broadcast, or figure out a re-design
+    // that does not use sink queries.
+    return;
+  }
+
+  sink_queries_[media_source] =
+      app_discovery_service_->StartObservingMediaSinks(
+          *cast_source,
+          base::BindRepeating(&CastMediaRouteProvider::OnSinkQueryUpdated,
+                              base::Unretained(this)));
+}
+
+void CastMediaRouteProvider::StopObservingMediaSinks(
+    const std::string& media_source) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  sink_queries_.erase(media_source);
+}
+
+void CastMediaRouteProvider::StartObservingMediaRoutes(
+    const std::string& media_source) {
+  NOTIMPLEMENTED();
+}
+
+void CastMediaRouteProvider::StopObservingMediaRoutes(
+    const std::string& media_source) {
+  NOTIMPLEMENTED();
+}
+
+void CastMediaRouteProvider::StartListeningForRouteMessages(
+    const std::string& route_id) {
+  NOTIMPLEMENTED();
+}
+
+void CastMediaRouteProvider::StopListeningForRouteMessages(
+    const std::string& route_id) {
+  NOTIMPLEMENTED();
+}
+
+void CastMediaRouteProvider::DetachRoute(const std::string& route_id) {
+  NOTIMPLEMENTED();
+}
+
+void CastMediaRouteProvider::EnableMdnsDiscovery() {
+  NOTIMPLEMENTED();
+}
+
+void CastMediaRouteProvider::UpdateMediaSinks(const std::string& media_source) {
+  NOTIMPLEMENTED();
+}
+
+void CastMediaRouteProvider::SearchSinks(
+    const std::string& sink_id,
+    const std::string& media_source,
+    mojom::SinkSearchCriteriaPtr search_criteria,
+    SearchSinksCallback callback) {
+  std::move(callback).Run(std::string());
+}
+
+void CastMediaRouteProvider::ProvideSinks(
+    const std::string& provider_name,
+    const std::vector<media_router::MediaSinkInternal>& sinks) {
+  NOTIMPLEMENTED();
+}
+
+void CastMediaRouteProvider::CreateMediaRouteController(
+    const std::string& route_id,
+    mojom::MediaControllerRequest media_controller,
+    mojom::MediaStatusObserverPtr observer,
+    CreateMediaRouteControllerCallback callback) {
+  NOTIMPLEMENTED();
+  std::move(callback).Run(false);
+}
+
+void CastMediaRouteProvider::OnSinkQueryUpdated(
+    const MediaSource::Id& source_id,
+    const std::vector<MediaSinkInternal>& sinks) {
+  DVLOG(1) << __func__ << ", source_id: " << source_id
+           << ", #sinks: " << sinks.size();
+  media_router_->OnSinksReceived(MediaRouteProviderId::CAST, source_id, sinks,
+                                 GetOrigins(source_id));
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/router/providers/cast/cast_media_route_provider.h b/chrome/browser/media/router/providers/cast/cast_media_route_provider.h
new file mode 100644
index 0000000..5dd7b23
--- /dev/null
+++ b/chrome/browser/media/router/providers/cast/cast_media_route_provider.h
@@ -0,0 +1,114 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_CAST_MEDIA_ROUTE_PROVIDER_H_
+#define CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_CAST_MEDIA_ROUTE_PROVIDER_H_
+
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "chrome/browser/media/router/providers/cast/cast_app_discovery_service.h"
+#include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h"
+#include "chrome/common/media_router/mojo/media_router.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace url {
+class Origin;
+}
+
+namespace media_router {
+
+// MediaRouteProvider for Cast sinks. This class may be created on any sequence.
+// All other methods, however, must be called on the task runner provided
+// during construction.
+class CastMediaRouteProvider : public mojom::MediaRouteProvider {
+ public:
+  CastMediaRouteProvider(
+      mojom::MediaRouteProviderRequest request,
+      mojom::MediaRouterPtrInfo media_router,
+      CastAppDiscoveryService* app_discovery_service,
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+  ~CastMediaRouteProvider() override;
+
+  // mojom::MediaRouteProvider:
+  void CreateRoute(const std::string& media_source,
+                   const std::string& sink_id,
+                   const std::string& presentation_id,
+                   const url::Origin& origin,
+                   int32_t tab_id,
+                   base::TimeDelta timeout,
+                   bool incognito,
+                   CreateRouteCallback callback) override;
+  void JoinRoute(const std::string& media_source,
+                 const std::string& presentation_id,
+                 const url::Origin& origin,
+                 int32_t tab_id,
+                 base::TimeDelta timeout,
+                 bool incognito,
+                 JoinRouteCallback callback) override;
+  void ConnectRouteByRouteId(const std::string& media_source,
+                             const std::string& route_id,
+                             const std::string& presentation_id,
+                             const url::Origin& origin,
+                             int32_t tab_id,
+                             base::TimeDelta timeout,
+                             bool incognito,
+                             ConnectRouteByRouteIdCallback callback) override;
+  void TerminateRoute(const std::string& route_id,
+                      TerminateRouteCallback callback) override;
+  void SendRouteMessage(const std::string& media_route_id,
+                        const std::string& message,
+                        SendRouteMessageCallback callback) override;
+  void SendRouteBinaryMessage(const std::string& media_route_id,
+                              const std::vector<uint8_t>& data,
+                              SendRouteBinaryMessageCallback callback) override;
+  void StartObservingMediaSinks(const std::string& media_source) override;
+  void StopObservingMediaSinks(const std::string& media_source) override;
+  void StartObservingMediaRoutes(const std::string& media_source) override;
+  void StopObservingMediaRoutes(const std::string& media_source) override;
+  void StartListeningForRouteMessages(const std::string& route_id) override;
+  void StopListeningForRouteMessages(const std::string& route_id) override;
+  void DetachRoute(const std::string& route_id) override;
+  void EnableMdnsDiscovery() override;
+  void UpdateMediaSinks(const std::string& media_source) override;
+  void SearchSinks(const std::string& sink_id,
+                   const std::string& media_source,
+                   mojom::SinkSearchCriteriaPtr search_criteria,
+                   SearchSinksCallback callback) override;
+  void ProvideSinks(
+      const std::string& provider_name,
+      const std::vector<media_router::MediaSinkInternal>& sinks) override;
+  void CreateMediaRouteController(
+      const std::string& route_id,
+      mojom::MediaControllerRequest media_controller,
+      mojom::MediaStatusObserverPtr observer,
+      CreateMediaRouteControllerCallback callback) override;
+
+ private:
+  void Init(mojom::MediaRouteProviderRequest request,
+            mojom::MediaRouterPtrInfo media_router);
+
+  // Notifies |media_router_| that results for a sink query has been updated.
+  void OnSinkQueryUpdated(const MediaSource::Id& source_id,
+                          const std::vector<MediaSinkInternal>& sinks);
+
+  // Binds |this| to the Mojo request passed into the ctor.
+  mojo::Binding<mojom::MediaRouteProvider> binding_;
+
+  // Mojo pointer to the Media Router.
+  mojom::MediaRouterPtr media_router_;
+
+  // Non-owned pointer to the CastAppDiscoveryService instance.
+  CastAppDiscoveryService* const app_discovery_service_;
+
+  // Registered sink queries.
+  base::flat_map<MediaSource::Id, CastAppDiscoveryService::Subscription>
+      sink_queries_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  DISALLOW_COPY_AND_ASSIGN(CastMediaRouteProvider);
+};
+
+}  // namespace media_router
+
+#endif  // CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_CAST_MEDIA_ROUTE_PROVIDER_H_
diff --git a/chrome/browser/media/router/providers/cast/chrome_cast_message_handler.cc b/chrome/browser/media/router/providers/cast/chrome_cast_message_handler.cc
new file mode 100644
index 0000000..a144900
--- /dev/null
+++ b/chrome/browser/media/router/providers/cast/chrome_cast_message_handler.cc
@@ -0,0 +1,22 @@
+// 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/media/router/providers/cast/chrome_cast_message_handler.h"
+
+#include "chrome/common/chrome_content_client.h"
+#include "components/cast_channel/cast_message_handler.h"
+#include "components/cast_channel/cast_socket_service.h"
+#include "components/version_info/version_info.h"
+
+namespace media_router {
+
+cast_channel::CastMessageHandler* GetCastMessageHandler() {
+  static cast_channel::CastMessageHandler* instance =
+      new cast_channel::CastMessageHandler(
+          cast_channel::CastSocketService::GetInstance(), GetUserAgent(),
+          version_info::GetVersionNumber());
+  return instance;
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/router/providers/cast/chrome_cast_message_handler.h b/chrome/browser/media/router/providers/cast/chrome_cast_message_handler.h
new file mode 100644
index 0000000..9c47ef1
--- /dev/null
+++ b/chrome/browser/media/router/providers/cast/chrome_cast_message_handler.h
@@ -0,0 +1,20 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_CHROME_CAST_MESSAGE_HANDLER_H_
+#define CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_CHROME_CAST_MESSAGE_HANDLER_H_
+
+namespace cast_channel {
+class CastMessageHandler;
+}
+
+namespace media_router {
+
+// Returns the singleton instance of CastMessageHandler with Chrome as the
+// embedder, creating one if necessary.
+cast_channel::CastMessageHandler* GetCastMessageHandler();
+
+}  // namespace media_router
+
+#endif  // CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_CHROME_CAST_MESSAGE_HANDLER_H_
diff --git a/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc b/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc
index 81625f6..f1f5bb1 100644
--- a/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc
+++ b/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc
@@ -7,6 +7,9 @@
 #include "chrome/browser/media/router/discovery/dial/dial_media_sink_service.h"
 #include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h"
 #include "chrome/browser/media/router/media_router_feature.h"
+#include "chrome/browser/media/router/providers/cast/cast_app_discovery_service.h"
+#include "chrome/browser/media/router/providers/cast/chrome_cast_message_handler.h"
+#include "components/cast_channel/cast_socket_service.h"
 #include "content/public/browser/browser_thread.h"
 
 namespace media_router {
@@ -65,11 +68,19 @@
 
 DualMediaSinkService::DualMediaSinkService() {
   OnDialSinkAddedCallback dial_sink_added_cb;
-  if (media_router::CastDiscoveryEnabled()) {
+  if (CastDiscoveryEnabled()) {
+    if (CastMediaRouteProviderEnabled()) {
+      cast_channel::CastSocketService* cast_socket_service =
+          cast_channel::CastSocketService::GetInstance();
+      cast_app_discovery_service_ = std::make_unique<CastAppDiscoveryService>(
+          GetCastMessageHandler(), cast_socket_service);
+    }
+
     cast_media_sink_service_ = std::make_unique<CastMediaSinkService>();
     cast_media_sink_service_->Start(
         base::BindRepeating(&DualMediaSinkService::OnSinksDiscovered,
-                            base::Unretained(this), "cast"));
+                            base::Unretained(this), "cast"),
+        cast_app_discovery_service_.get());
     dial_sink_added_cb = cast_media_sink_service_->GetDialSinkAddedCallback();
   }
 
diff --git a/chrome/browser/media/router/providers/cast/dual_media_sink_service.h b/chrome/browser/media/router/providers/cast/dual_media_sink_service.h
index b51aa10d..b36ac96d 100644
--- a/chrome/browser/media/router/providers/cast/dual_media_sink_service.h
+++ b/chrome/browser/media/router/providers/cast/dual_media_sink_service.h
@@ -22,8 +22,9 @@
 
 namespace media_router {
 
-class DialMediaSinkService;
+class CastAppDiscoveryService;
 class CastMediaSinkService;
+class DialMediaSinkService;
 
 // This class uses DialMediaSinkService and CastMediaSinkService to discover
 // sinks used by the Cast MediaRouteProvider. It also encapsulates the setup
@@ -46,6 +47,10 @@
   static DualMediaSinkService* GetInstance();
   static void SetInstanceForTest(DualMediaSinkService* instance_for_test);
 
+  CastAppDiscoveryService* cast_app_discovery_service() {
+    return cast_app_discovery_service_.get();
+  }
+
   // Returns the current list of sinks, keyed by provider name.
   const base::flat_map<std::string, std::vector<MediaSinkInternal>>&
   current_sinks() {
@@ -91,6 +96,7 @@
                          std::vector<MediaSinkInternal> sinks);
 
   std::unique_ptr<CastMediaSinkService> cast_media_sink_service_;
+  std::unique_ptr<CastAppDiscoveryService> cast_app_discovery_service_;
   std::unique_ptr<DialMediaSinkService> dial_media_sink_service_;
 
   OnSinksDiscoveredProviderCallbackList sinks_discovered_callbacks_;
diff --git a/chrome/browser/media/router/test/mock_mojo_media_router.h b/chrome/browser/media/router/test/mock_mojo_media_router.h
index fb16950..010acb2 100644
--- a/chrome/browser/media/router/test/mock_mojo_media_router.h
+++ b/chrome/browser/media/router/test/mock_mojo_media_router.h
@@ -63,6 +63,13 @@
       void(int32_t tab_id,
            media::mojom::MirrorServiceRemoterPtr& remoter,
            media::mojom::MirrorServiceRemotingSourceRequest& source_request));
+  void GetMediaSinkServiceStatus(
+      mojom::MediaRouter::GetMediaSinkServiceStatusCallback callback) override {
+    GetMediaSinkServiceStatusInternal(callback);
+  }
+  MOCK_METHOD1(
+      GetMediaSinkServiceStatusInternal,
+      void(mojom::MediaRouter::GetMediaSinkServiceStatusCallback& callback));
 };
 
 }  // namespace media_router
diff --git a/chrome/browser/media/router/test/test_helper.cc b/chrome/browser/media/router/test/test_helper.cc
index a89fd5e..b6262dfb 100644
--- a/chrome/browser/media/router/test/test_helper.cc
+++ b/chrome/browser/media/router/test/test_helper.cc
@@ -83,7 +83,7 @@
 
 MediaSinkInternal CreateDialSink(int num) {
   std::string friendly_name = base::StringPrintf("friendly name %d", num);
-  std::string unique_id = base::StringPrintf("id %d", num);
+  std::string unique_id = base::StringPrintf("dial:<id%d>", num);
   net::IPEndPoint ip_endpoint = CreateIPEndPoint(num);
 
   media_router::MediaSink sink(unique_id, friendly_name,
@@ -96,4 +96,20 @@
   return media_router::MediaSinkInternal(sink, extra_data);
 }
 
+MediaSinkInternal CreateCastSink(int num) {
+  std::string friendly_name = base::StringPrintf("friendly name %d", num);
+  std::string unique_id = base::StringPrintf("cast:<id%d>", num);
+  net::IPEndPoint ip_endpoint = CreateIPEndPoint(num);
+
+  MediaSink sink(unique_id, friendly_name, SinkIconType::CAST);
+  CastSinkExtraData extra_data;
+  extra_data.ip_endpoint = ip_endpoint;
+  extra_data.port = ip_endpoint.port();
+  extra_data.model_name = base::StringPrintf("model name %d", num);
+  extra_data.cast_channel_id = num;
+  extra_data.capabilities = cast_channel::CastDeviceCapability::AUDIO_OUT |
+                            cast_channel::CastDeviceCapability::VIDEO_OUT;
+  return MediaSinkInternal(sink, extra_data);
+}
+
 }  // namespace media_router
diff --git a/chrome/browser/media/router/test/test_helper.h b/chrome/browser/media/router/test/test_helper.h
index 9530a09..3eb45ae 100644
--- a/chrome/browser/media/router/test/test_helper.h
+++ b/chrome/browser/media/router/test/test_helper.h
@@ -16,6 +16,7 @@
 #include "chrome/browser/media/router/discovery/dial/dial_media_sink_service.h"
 #include "chrome/browser/media/router/discovery/dial/dial_url_fetcher.h"
 #include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h"
+#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
 #include "chrome/browser/media/router/issue_manager.h"
 #include "chrome/browser/media/router/issues_observer.h"
 #include "chrome/browser/media/router/media_routes_observer.h"
@@ -129,7 +130,9 @@
   MockCastMediaSinkService();
   ~MockCastMediaSinkService() override;
 
-  MOCK_METHOD1(Start, void(const OnSinksDiscoveredCallback&));
+  MOCK_METHOD2(Start,
+               void(const OnSinksDiscoveredCallback&,
+                    CastMediaSinkServiceImpl::Observer*));
   MOCK_METHOD0(OnUserGesture, void());
   MOCK_METHOD0(StartMdnsDiscovery, void());
 };
@@ -166,6 +169,9 @@
 // }
 MediaSinkInternal CreateDialSink(int num);
 
+// Helper function to create a Cast sink.
+MediaSinkInternal CreateCastSink(int num);
+
 }  // namespace media_router
 
 #endif  // CHROME_BROWSER_MEDIA_ROUTER_TEST_TEST_HELPER_H_
diff --git a/chrome/browser/offline_pages/offline_page_request_job.cc b/chrome/browser/offline_pages/offline_page_request_job.cc
index 6de2cbb3..1b06847 100644
--- a/chrome/browser/offline_pages/offline_page_request_job.cc
+++ b/chrome/browser/offline_pages/offline_page_request_job.cc
@@ -33,6 +33,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/resource_type.h"
 #include "net/base/file_stream.h"
+#include "net/base/filename_util.h"
 #include "net/base/io_buffer.h"
 #include "net/base/network_change_notifier.h"
 #include "net/http/http_request_headers.h"
@@ -134,7 +135,7 @@
 void UpdateDigest(
     const scoped_refptr<OfflinePageRequestJob::ThreadSafeArchiveValidator>&
         validator,
-    const scoped_refptr<net::IOBuffer>& buffer,
+    scoped_refptr<net::IOBuffer> buffer,
     size_t len) {
   validator->Update(buffer->data(), len);
 }
@@ -349,6 +350,11 @@
                             has_range_header);
 }
 
+void ReportIntentDataChangedAfterValidation(bool changed) {
+  UMA_HISTOGRAM_BOOLEAN(
+      "OfflinePages.RequestJob.IntentDataChangedAfterValidation", changed);
+}
+
 OfflinePageModel* GetOfflinePageModel(
     content::ResourceRequestInfo::WebContentsGetter web_contents_getter) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -533,6 +539,24 @@
           OfflinePageRequestJob::NetworkState::PROHIBITIVELY_SLOW_NETWORK);
 }
 
+void ClearOfflinePageData(
+    content::ResourceRequestInfo::WebContentsGetter web_contents_getter) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // |web_contents_getter| is passed from IO thread. We need to check if
+  // web contents is still valid.
+  content::WebContents* web_contents = web_contents_getter.Run();
+  if (!web_contents)
+    return;
+
+  // Save an cached copy of OfflinePageItem such that Tab code can get
+  // the loaded offline page immediately.
+  OfflinePageTabHelper* tab_helper =
+      OfflinePageTabHelper::FromWebContents(web_contents);
+  DCHECK(tab_helper);
+  tab_helper->ClearOfflinePage();
+}
+
 }  // namespace
 
 // static
@@ -590,7 +614,6 @@
       delegate_(new DefaultOfflinePageRequestJobDelegate()),
       previews_decider_(previews_decider),
       candidate_index_(0),
-      remaining_bytes_(0),
       has_range_header_(false),
       weak_ptr_factory_(this) {}
 
@@ -634,26 +657,11 @@
 
 int OfflinePageRequestJob::ReadRawData(net::IOBuffer* dest, int dest_size) {
   DCHECK_NE(dest_size, 0);
-  DCHECK_GE(remaining_bytes_, 0);
 
-  if (remaining_bytes_ < dest_size)
-    dest_size = remaining_bytes_;
-
-  // If we should copy zero bytes because |remaining_bytes_| is zero, short
-  // circuit here.
-  if (!dest_size)
-    return 0;
-
-  int result = stream_->Read(
+  return stream_->Read(
       dest, dest_size,
       base::Bind(&OfflinePageRequestJob::DidReadForServing,
                  weak_ptr_factory_.GetWeakPtr(), base::WrapRefCounted(dest)));
-  if (result >= 0) {
-    remaining_bytes_ -= result;
-    DCHECK_GE(remaining_bytes_, 0);
-  }
-
-  return result;
 }
 
 void OfflinePageRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
@@ -746,14 +754,24 @@
     return;
   }
 
-  // Open the file if the validation was skipped and the file was not opened
-  // yet.
+  // No need to open the file if it has already been opened for the validation.
   if (stream_) {
     DidOpenForServing(net::OK);
     return;
   }
-  OpenFile(base::Bind(&OfflinePageRequestJob::DidOpenForServing,
-                      weak_ptr_factory_.GetWeakPtr()));
+
+  // If a file:// or content:// intent is being processed, open the file:// or
+  // content:// denoted in the intent instead. Otherwise, open the archive file
+  // associated with the offline page.
+  base::FilePath file_path;
+  if (IsProcessingFileOrContentUrlIntent()) {
+    bool valid = net::FileURLToFilePath(offline_header_.intent_url, &file_path);
+    DCHECK(valid);
+  } else {
+    file_path = GetCurrentOfflinePage().file_path;
+  }
+  OpenFile(file_path, base::Bind(&OfflinePageRequestJob::DidOpenForServing,
+                                 weak_ptr_factory_.GetWeakPtr()));
 }
 
 void OfflinePageRequestJob::VisitTrustedOfflinePage() {
@@ -763,16 +781,7 @@
   ReportOfflinePageSize(network_state_, GetCurrentOfflinePage());
   ReportExistenceOfRangeHeader(has_range_header_);
 
-  const content::ResourceRequestInfo* info =
-      content::ResourceRequestInfo::ForRequest(request());
-  ChromeNavigationUIData* navigation_data =
-      static_cast<ChromeNavigationUIData*>(info->GetNavigationUIData());
-  if (navigation_data) {
-    std::unique_ptr<OfflinePageNavigationUIData> offline_page_data =
-        std::make_unique<OfflinePageNavigationUIData>(true);
-    navigation_data->SetOfflinePageNavigationUIData(
-        std::move(offline_page_data));
-  }
+  SetOfflinePageNavigationUIData(true /*is_offline_page*/);
 
   content::BrowserThread::PostTask(
       content::BrowserThread::UI, FROM_HERE,
@@ -781,6 +790,20 @@
                      GetCurrentOfflinePage()));
 }
 
+void OfflinePageRequestJob::SetOfflinePageNavigationUIData(
+    bool is_offline_page) {
+  const content::ResourceRequestInfo* info =
+      content::ResourceRequestInfo::ForRequest(request());
+  ChromeNavigationUIData* navigation_data =
+      static_cast<ChromeNavigationUIData*>(info->GetNavigationUIData());
+  if (navigation_data) {
+    std::unique_ptr<OfflinePageNavigationUIData> offline_page_data =
+        std::make_unique<OfflinePageNavigationUIData>(is_offline_page);
+    navigation_data->SetOfflinePageNavigationUIData(
+        std::move(offline_page_data));
+  }
+}
+
 void OfflinePageRequestJob::Redirect(const GURL& redirected_url) {
   receive_redirect_headers_end_ = base::TimeTicks::Now();
   redirect_response_time_ = base::Time::Now();
@@ -816,12 +839,18 @@
   if (!info)
     return AccessEntryPoint::UNKNOWN;
 
-  std::string offline_header_value;
-  request()->extra_request_headers().GetHeader(kOfflinePageHeader,
-                                               &offline_header_value);
-  OfflinePageHeader offline_header(offline_header_value);
-  if (offline_header.reason == OfflinePageHeader::Reason::DOWNLOAD)
-    return AccessEntryPoint::DOWNLOADS;
+  switch (offline_header_.reason) {
+    case OfflinePageHeader::Reason::DOWNLOAD:
+      return AccessEntryPoint::DOWNLOADS;
+    case OfflinePageHeader::Reason::NOTIFICATION:
+      return AccessEntryPoint::NOTIFICATION;
+    case OfflinePageHeader::Reason::FILE_URL_INTENT:
+      return AccessEntryPoint::FILE_URL_INTENT;
+    case OfflinePageHeader::Reason::CONTENT_URL_INTENT:
+      return AccessEntryPoint::CONTENT_URL_INTENT;
+    default:
+      break;
+  }
 
   ui::PageTransition transition = info->GetPageTransition();
   if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_LINK)) {
@@ -851,21 +880,57 @@
   return candidates_[candidate_index_].offline_page;
 }
 
-void OfflinePageRequestJob::OpenFile(const net::CompletionCallback& callback) {
+bool OfflinePageRequestJob::IsProcessingFileOrContentUrlIntent() const {
+  return offline_header_.reason == OfflinePageHeader::Reason::FILE_URL_INTENT ||
+         offline_header_.reason ==
+             OfflinePageHeader::Reason::CONTENT_URL_INTENT;
+}
+
+void OfflinePageRequestJob::OpenFile(const base::FilePath& file_path,
+                                     const net::CompletionCallback& callback) {
   if (!stream_)
     stream_ = std::make_unique<net::FileStream>(file_task_runner_);
 
   int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
               base::File::FLAG_EXCLUSIVE_READ | base::File::FLAG_ASYNC;
-  int result =
-      stream_->Open(GetCurrentOfflinePage().file_path, flags, callback);
+  int result = stream_->Open(file_path, flags, callback);
   if (result != net::ERR_IO_PENDING)
     callback.Run(result);
 }
 
+void OfflinePageRequestJob::UpdateDigestOnBackground(
+    scoped_refptr<net::IOBuffer> buffer,
+    size_t len,
+    base::OnceCallback<void(void)> digest_updated_callback) {
+  DCHECK_GT(len, 0u);
+
+  if (!archive_validator_)
+    archive_validator_ = new ThreadSafeArchiveValidator();
+
+  // Delegate to background task runner to update the hash since it is time
+  // consuming. Once it is done, |digest_updated_callback| will be called to
+  // continue the reading.
+  file_task_runner_->PostTaskAndReply(
+      FROM_HERE, base::BindOnce(&UpdateDigest, archive_validator_, buffer, len),
+      std::move(digest_updated_callback));
+}
+
+void OfflinePageRequestJob::FinalizeDigestOnBackground(
+    base::OnceCallback<void(const std::string&)> digest_finalized_callback) {
+  DCHECK(archive_validator_.get());
+
+  // Delegate to background task runner to finalize the hash to get the digest
+  // since it is time consuming. Once it is done, |digest_finalized_callback|
+  // will be called with the digest.
+  base::PostTaskAndReplyWithResult(
+      file_task_runner_.get(), FROM_HERE,
+      base::BindOnce(&ThreadSafeArchiveValidator::Finish, archive_validator_),
+      std::move(digest_finalized_callback));
+}
+
 void OfflinePageRequestJob::ValidateFile() {
   // If the archive file is in internal directory, the offline page can be
-  // deemed as trust without going through valication.
+  // deemed as trusted without going through valication.
   if (candidates_[candidate_index_].archive_is_in_internal_dir) {
     OnTrustedOfflinePageFound();
     return;
@@ -878,6 +943,18 @@
     return;
   }
 
+  // If a file:// or content:// URL intent is being viewed, skip the validation.
+  // The digest for the file:// or content:// denoted in the intent was computed
+  // and used to find the offline page. However, we will not validate and read
+  // from the archive archive file assoicated with the offline page since it may
+  // not exist or even got modified. We will read from the file:// or content://
+  // denoted in the intent  and compute the digest of the read data to make sure
+  // it does not get changed.
+  if (IsProcessingFileOrContentUrlIntent()) {
+    OnFileValidationDone(FileValidationResult::FILE_VALIDATION_SUCCEEDED);
+    return;
+  }
+
   GetFileSizeForValidation();
 }
 
@@ -904,7 +981,8 @@
   }
 
   // Open file to compute the digest.
-  OpenFile(base::Bind(&OfflinePageRequestJob::DidOpenForValidation,
+  OpenFile(GetCurrentOfflinePage().file_path,
+           base::Bind(&OfflinePageRequestJob::DidOpenForValidation,
                       weak_ptr_factory_.GetWeakPtr()));
 }
 
@@ -919,8 +997,6 @@
   if (!buffer_)
     buffer_ = new net::IOBuffer(kMaxBufferSizeForValidation);
 
-  archive_validator_ = new ThreadSafeArchiveValidator();
-
   ReadForValidation();
 }
 
@@ -942,29 +1018,21 @@
   }
 
   if (result > 0) {
-    // Delegate to background task runner to update the hash since it is time
-    // consuming. Once it is done, ReadForValidation will be called to continue
-    // the reading.
-    file_task_runner_->PostTaskAndReply(
-        FROM_HERE,
-        base::BindOnce(&UpdateDigest, archive_validator_, buffer_, result),
+    UpdateDigestOnBackground(
+        buffer_, result,
         base::BindOnce(&OfflinePageRequestJob::ReadForValidation,
                        weak_ptr_factory_.GetWeakPtr()));
     return;
   }
 
   // When |result| is 0 (net::OK), it indicates EOF. We need to finalize the
-  // hash to get the actual digest. This is done by delegating to background
-  // task runner since it is time consuming. DidComputeActualDigest will be
-  // called with the actual digest.
-  base::PostTaskAndReplyWithResult(
-      file_task_runner_.get(), FROM_HERE,
-      base::BindOnce(&ThreadSafeArchiveValidator::Finish, archive_validator_),
-      base::BindOnce(&OfflinePageRequestJob::DidComputeActualDigest,
-                     weak_ptr_factory_.GetWeakPtr()));
+  // hash to get the actual digest.
+  FinalizeDigestOnBackground(base::BindOnce(
+      &OfflinePageRequestJob::DidComputeActualDigestForValidation,
+      weak_ptr_factory_.GetWeakPtr()));
 }
 
-void OfflinePageRequestJob::DidComputeActualDigest(
+void OfflinePageRequestJob::DidComputeActualDigestForValidation(
     const std::string& actual_digest) {
   DCHECK(!GetCurrentOfflinePage().digest.empty());
   bool is_trusted = actual_digest == GetCurrentOfflinePage().digest;
@@ -1001,10 +1069,18 @@
 void OfflinePageRequestJob::DidOpenForServing(int result) {
   ReportOpenResult(result);
 
-  // If the file failed to open, fall back to the default handling.
+  // Handle the file opening failure.
   if (result != net::OK) {
     ReportRequestResult(RequestResult::FILE_NOT_FOUND, network_state_);
-    FallbackToDefault();
+
+    // If the file:// or content:// intent is being processed, don't fall
+    // back to the default handling. Instead, we should fail the request.
+    if (IsProcessingFileOrContentUrlIntent()) {
+      NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
+                                             net::ERR_FAILED));
+    } else {
+      FallbackToDefault();
+    }
     return;
   }
 
@@ -1012,17 +1088,6 @@
   // all the necessary reporting and bookkeeping for using this offline page.
   VisitTrustedOfflinePage();
 
-  remaining_bytes_ = GetCurrentOfflinePage().file_size;
-  DCHECK_GE(remaining_bytes_, 0);
-
-  // We didn't need to call stream_->Seek() if there is no data to read, so we
-  // pass to DidSeekForServing() the value that would mean seek success. This
-  // way we skip the code handling seek failure.
-  if (remaining_bytes_ == 0) {
-    DidSeekForServing(0);
-    return;
-  }
-
   // Note that we always seek to the beginning of the file because the file may
   // have already been read for validation purpose.
   int seek_result =
@@ -1043,19 +1108,60 @@
     return;
   }
 
-  set_expected_content_size(remaining_bytes_);
+  set_expected_content_size(GetCurrentOfflinePage().file_size);
   NotifyHeadersComplete();
 }
 
 void OfflinePageRequestJob::DidReadForServing(scoped_refptr<net::IOBuffer> buf,
                                               int result) {
-  if (result >= 0) {
-    remaining_bytes_ -= result;
-    DCHECK_GE(remaining_bytes_, 0);
+  ReportReadResult(result);
+
+  if (result < 0 || !IsProcessingFileOrContentUrlIntent()) {
+    buf = nullptr;
+    NotifyReadRawDataComplete(result);
+    return;
   }
 
-  ReportReadResult(result);
-  buf = nullptr;
+  // At this point, we have result >= 0 && IsProcessingFileOrContentUrlIntent()
+  // which means the read succeeds for processing the file:// or content:// URL
+  // intent. We need to compute the digest to ensure that the file:// or
+  // content:// we read is not modified since the time we received the intent,
+  // validated the data provided by file:// or content:// URL, and decided to
+  // turn it into the corresponding http/https URL and let OfflinePageRequestJob
+  // handle it.
+  if (result > 0) {
+    UpdateDigestOnBackground(
+        buf, result,
+        base::BindOnce(&OfflinePageRequestJob::NotifyReadRawDataComplete,
+                       weak_ptr_factory_.GetWeakPtr(), result));
+
+  } else {
+    // When |result| is 0 (net::OK), it indicates EOF. We need to finalize the
+    // hash to get the actual digest.
+    FinalizeDigestOnBackground(
+        base::BindOnce(&OfflinePageRequestJob::DidComputeActualDigestForServing,
+                       weak_ptr_factory_.GetWeakPtr(), result));
+  }
+}
+
+void OfflinePageRequestJob::NotifyReadRawDataComplete(int result) {
+  ReadRawDataComplete(result);
+}
+
+void OfflinePageRequestJob::DidComputeActualDigestForServing(
+    int result,
+    const std::string& actual_digest) {
+  // If the actual digest does not match, fail the request job.
+  bool mismatch = actual_digest != GetCurrentOfflinePage().digest;
+  ReportIntentDataChangedAfterValidation(mismatch);
+  if (mismatch) {
+    SetOfflinePageNavigationUIData(false /*is_offline_page*/);
+    content::BrowserThread::PostTask(
+        content::BrowserThread::UI, FROM_HERE,
+        base::Bind(&ClearOfflinePageData,
+                   delegate_->GetWebContentsGetter(request())));
+    result = net::ERR_FAILED;
+  }
 
   ReadRawDataComplete(result);
 }
diff --git a/chrome/browser/offline_pages/offline_page_request_job.h b/chrome/browser/offline_pages/offline_page_request_job.h
index ef29f818..3964bca5 100644
--- a/chrome/browser/offline_pages/offline_page_request_job.h
+++ b/chrome/browser/offline_pages/offline_page_request_job.h
@@ -100,6 +100,12 @@
     // Launched due to hitting the reload button or hitting enter in the
     // omnibox.
     RELOAD = 6,
+    // Launched due to clicking a notification.
+    NOTIFICATION = 7,
+    // Launched due to processing a file URL intent to view MHTML file.
+    FILE_URL_INTENT = 8,
+    // Launched due to processing a content URL intent to view MHTML content.
+    CONTENT_URL_INTENT = 9,
     COUNT
   };
 
@@ -206,11 +212,21 @@
 
   const OfflinePageItem& GetCurrentOfflinePage() const;
 
+  bool IsProcessingFileOrContentUrlIntent() const;
+
   void OnTrustedOfflinePageFound();
   void VisitTrustedOfflinePage();
+  void SetOfflinePageNavigationUIData(bool is_offline_page);
   void Redirect(const GURL& redirected_url);
 
-  void OpenFile(const net::CompletionCallback& callback);
+  void OpenFile(const base::FilePath& file_path,
+                const net::CompletionCallback& callback);
+  void UpdateDigestOnBackground(
+      scoped_refptr<net::IOBuffer> buffer,
+      size_t len,
+      base::OnceCallback<void(void)> digest_updated_callback);
+  void FinalizeDigestOnBackground(
+      base::OnceCallback<void(const std::string&)> digest_finalized_callback);
 
   // All the work related to validations.
   void ValidateFile();
@@ -219,13 +235,16 @@
   void DidOpenForValidation(int result);
   void ReadForValidation();
   void DidReadForValidation(int result);
-  void DidComputeActualDigest(const std::string& actual_digest);
+  void DidComputeActualDigestForValidation(const std::string& actual_digest);
   void OnFileValidationDone(FileValidationResult result);
 
   // All the work related to serving from the archive file.
   void DidOpenForServing(int result);
   void DidSeekForServing(int64_t result);
   void DidReadForServing(scoped_refptr<net::IOBuffer> buf, int result);
+  void NotifyReadRawDataComplete(int result);
+  void DidComputeActualDigestForServing(int result,
+                                        const std::string& actual_digest);
 
   std::unique_ptr<Delegate> delegate_;
 
@@ -252,7 +271,6 @@
   // For the purpose of serving from the archive file.
   base::FilePath file_path_;
   std::unique_ptr<net::FileStream> stream_;
-  int64_t remaining_bytes_;
   bool has_range_header_;
 
   base::WeakPtrFactory<OfflinePageRequestJob> weak_ptr_factory_;
diff --git a/chrome/browser/offline_pages/offline_page_request_job_unittest.cc b/chrome/browser/offline_pages/offline_page_request_job_unittest.cc
index 57866af..9ff30e9 100644
--- a/chrome/browser/offline_pages/offline_page_request_job_unittest.cc
+++ b/chrome/browser/offline_pages/offline_page_request_job_unittest.cc
@@ -45,6 +45,7 @@
 #include "content/public/common/previews_state.h"
 #include "content/public/common/resource_type.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "net/base/filename_util.h"
 #include "net/http/http_request_headers.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
@@ -137,28 +138,32 @@
 
 class TestURLRequestDelegate : public net::URLRequest::Delegate {
  public:
-  typedef base::Callback<void(const std::string&)> ReadCompletedCallback;
+  typedef base::Callback<void(const std::string&, int)> ReadCompletedCallback;
 
   explicit TestURLRequestDelegate(const ReadCompletedCallback& callback)
       : read_completed_callback_(callback),
-        buffer_(new net::IOBuffer(kBufSize)) {}
+        buffer_(new net::IOBuffer(kBufSize)),
+        request_status_(net::ERR_IO_PENDING) {}
 
   void OnResponseStarted(net::URLRequest* request, int net_error) override {
     DCHECK_NE(net::ERR_IO_PENDING, net_error);
     if (net_error != net::OK) {
-      OnReadCompleted(request, 0);
+      OnReadCompleted(request, net_error);
       return;
     }
     // Initiate the first read.
     int bytes_read = request->Read(buffer_.get(), kBufSize);
-    if (bytes_read >= 0)
+    if (bytes_read >= 0) {
       OnReadCompleted(request, bytes_read);
-    else if (bytes_read != net::ERR_IO_PENDING)
+    } else if (bytes_read != net::ERR_IO_PENDING) {
+      request_status_ = bytes_read;
       OnResponseCompleted();
+    }
   }
 
   void OnReadCompleted(net::URLRequest* request, int bytes_read) override {
-    data_received_.append(buffer_->data(), bytes_read);
+    if (bytes_read > 0)
+      data_received_.append(buffer_->data(), bytes_read);
 
     // If it was not end of stream, request to read more.
     while (bytes_read > 0) {
@@ -167,19 +172,23 @@
         data_received_.append(buffer_->data(), bytes_read);
     }
 
+    request_status_ = (bytes_read >= 0) ? net::OK : bytes_read;
     if (bytes_read != net::ERR_IO_PENDING)
       OnResponseCompleted();
   }
 
  private:
   void OnResponseCompleted() {
+    if (request_status_ != net::OK)
+      data_received_.clear();
     if (!read_completed_callback_.is_null())
-      read_completed_callback_.Run(data_received_);
+      read_completed_callback_.Run(data_received_, request_status_);
   }
 
   ReadCompletedCallback read_completed_callback_;
   scoped_refptr<net::IOBuffer> buffer_;
   std::string data_received_;
+  int request_status_;
 
   DISALLOW_COPY_AND_ASSIGN(TestURLRequestDelegate);
 };
@@ -417,6 +426,9 @@
   // full header string.
   std::string UseOfflinePageHeader(OfflinePageHeader::Reason reason,
                                    int64_t offline_id);
+  std::string UseOfflinePageHeaderForIntent(OfflinePageHeader::Reason reason,
+                                            int64_t offline_id,
+                                            const GURL& intent_url);
 
   net::TestURLRequestContext* url_request_context() {
     return test_url_request_context_.get();
@@ -425,6 +437,7 @@
   OfflinePageTabHelper* offline_page_tab_helper() const {
     return offline_page_tab_helper_;
   }
+  int request_status() const { return request_status_; }
   int bytes_read() const { return data_received_.length(); }
   const std::string& data_received() const { return data_received_; }
   bool is_offline_page_set_in_navigation_data() const {
@@ -453,6 +466,8 @@
   static base::FilePath private_archives_dir_;
   static base::FilePath public_archives_dir_;
 
+  OfflinePageRequestJob::AccessEntryPoint GetExpectedAccessEntryPoint() const;
+
   void OnSavePageDone(SavePageResult result, int64_t offline_id);
   std::unique_ptr<net::URLRequest> CreateRequest(
       const GURL& url,
@@ -460,6 +475,7 @@
       content::ResourceType resource_type);
   void OnGetPageByOfflineIdDone(const OfflinePageItem* pages);
   void ReadCompleted(const std::string& data_received,
+                     int request_status,
                      bool is_offline_page_set_in_navigation_data);
 
   // Runs on IO thread.
@@ -471,8 +487,9 @@
                             const std::string& method,
                             const net::HttpRequestHeaders& extra_headers,
                             content::ResourceType resource_type);
-  void ReadCompletedOnIO(const std::string& data_received);
+  void ReadCompletedOnIO(const std::string& data_received, int request_status);
   void TearDownOnReadCompletedOnIO(const std::string& data_received,
+                                   int request_status,
                                    bool is_offline_page_set_in_navigation_data);
 
   content::TestBrowserThreadBundle thread_bundle_;
@@ -483,6 +500,7 @@
   OfflinePageTabHelper* offline_page_tab_helper_;  // Not owned.
   int64_t last_offline_id_;
   std::string data_received_;
+  int request_status_;
   bool is_offline_page_set_in_navigation_data_;
   OfflinePageItem page_;
   OfflinePageHeader offline_page_header_;
@@ -504,6 +522,7 @@
   base::ScopedTempDir public_archives_temp_base_dir_;
   base::ScopedTempDir temp_dir_;
   base::FilePath temp_file_path_;
+  int file_name_sequence_num_ = 0;
 
   bool async_operation_completed_ = false;
   base::Closure async_operation_completed_callback_;
@@ -515,6 +534,7 @@
     : thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD),
       profile_manager_(TestingBrowserProcess::GetGlobal()),
       last_offline_id_(0),
+      request_status_(net::ERR_IO_PENDING),
       is_offline_page_set_in_navigation_data_(false),
       network_change_notifier_(new TestNetworkChangeNotifier),
       test_previews_decider_(new TestPreviewsDecider) {}
@@ -604,8 +624,13 @@
     const base::Closure& callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
-  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-  temp_file_path_ = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("test.mht"));
+  if (!temp_dir_.IsValid()) {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+  }
+  std::string file_name("test");
+  file_name += base::IntToString(file_name_sequence_num_++);
+  file_name += ".mht";
+  temp_file_path_ = temp_dir_.GetPath().AppendASCII(file_name);
   ASSERT_TRUE(base::WriteFile(temp_file_path_, content.c_str(),
                               content.length()) != -1);
   callback.Run();
@@ -801,6 +826,7 @@
     int64_t expected_offline_id,
     int expected_file_size,
     OfflinePageRequestJob::AggregatedRequestResult expected_request_result) {
+  EXPECT_EQ(net::OK, request_status_);
   EXPECT_EQ(expected_file_size, bytes_read());
   EXPECT_TRUE(is_offline_page_set_in_navigation_data());
   ASSERT_TRUE(offline_page_tab_helper()->GetOfflinePageForTest());
@@ -812,9 +838,7 @@
     ExpectOneUniqueSampleForAggregatedRequestResult(expected_request_result);
   }
   OfflinePageRequestJob::AccessEntryPoint expected_entry_point =
-      is_connected_with_good_network()
-          ? OfflinePageRequestJob::AccessEntryPoint::DOWNLOADS
-          : OfflinePageRequestJob::AccessEntryPoint::LINK;
+      GetExpectedAccessEntryPoint();
   ExpectAccessEntryPoint(expected_entry_point);
   if (is_connected_with_good_network()) {
     ExpectOnlinePageSizeUniqueSample(expected_file_size / 1024, 1);
@@ -826,6 +850,22 @@
   ExpectOfflinePageAccessCount(expected_offline_id, 1);
 }
 
+OfflinePageRequestJob::AccessEntryPoint
+OfflinePageRequestJobTest::GetExpectedAccessEntryPoint() const {
+  switch (offline_page_header_.reason) {
+    case OfflinePageHeader::Reason::DOWNLOAD:
+      return OfflinePageRequestJob::AccessEntryPoint::DOWNLOADS;
+    case OfflinePageHeader::Reason::NOTIFICATION:
+      return OfflinePageRequestJob::AccessEntryPoint::NOTIFICATION;
+    case OfflinePageHeader::Reason::FILE_URL_INTENT:
+      return OfflinePageRequestJob::AccessEntryPoint::FILE_URL_INTENT;
+    case OfflinePageHeader::Reason::CONTENT_URL_INTENT:
+      return OfflinePageRequestJob::AccessEntryPoint::CONTENT_URL_INTENT;
+    default:
+      return OfflinePageRequestJob::AccessEntryPoint::LINK;
+  }
+}
+
 std::string OfflinePageRequestJobTest::UseOfflinePageHeader(
     OfflinePageHeader::Reason reason,
     int64_t offline_id) {
@@ -836,6 +876,18 @@
   return offline_page_header_.GetCompleteHeaderString();
 }
 
+std::string OfflinePageRequestJobTest::UseOfflinePageHeaderForIntent(
+    OfflinePageHeader::Reason reason,
+    int64_t offline_id,
+    const GURL& intent_url) {
+  DCHECK_NE(OfflinePageHeader::Reason::NONE, reason);
+  DCHECK(offline_id);
+  offline_page_header_.reason = reason;
+  offline_page_header_.id = base::Int64ToString(offline_id);
+  offline_page_header_.intent_url = intent_url;
+  return offline_page_header_.GetCompleteHeaderString();
+}
+
 int64_t OfflinePageRequestJobTest::SavePublicPage(
     const GURL& url,
     const GURL& original_url,
@@ -993,7 +1045,8 @@
 }
 
 void OfflinePageRequestJobTest::ReadCompletedOnIO(
-    const std::string& data_received) {
+    const std::string& data_received,
+    int request_status) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
   bool is_offline_page_set_in_navigation_data = false;
@@ -1013,12 +1066,13 @@
   content::BrowserThread::PostTask(
       content::BrowserThread::IO, FROM_HERE,
       base::Bind(&OfflinePageRequestJobTest::TearDownOnReadCompletedOnIO,
-                 base::Unretained(this), data_received,
+                 base::Unretained(this), data_received, request_status,
                  is_offline_page_set_in_navigation_data));
 }
 
 void OfflinePageRequestJobTest::TearDownOnReadCompletedOnIO(
     const std::string& data_received,
+    int request_status,
     bool is_offline_page_set_in_navigation_data) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
@@ -1027,16 +1081,18 @@
   content::BrowserThread::PostTask(
       content::BrowserThread::UI, FROM_HERE,
       base::Bind(&OfflinePageRequestJobTest::ReadCompleted,
-                 base::Unretained(this), data_received,
+                 base::Unretained(this), data_received, request_status,
                  is_offline_page_set_in_navigation_data));
 }
 
 void OfflinePageRequestJobTest::ReadCompleted(
     const std::string& data_received,
+    int request_status,
     bool is_offline_page_set_in_navigation_data) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   data_received_ = data_received;
+  request_status_ = request_status;
   is_offline_page_set_in_navigation_data_ =
       is_offline_page_set_in_navigation_data;
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -1637,7 +1693,7 @@
 
   // There're 2 offline pages matching kUrl. The most recently created one
   // should fail on mistmatched digest. The second most recently created offline
-  // page (kTestClientId9) should be.
+  // page should work.
   LoadPage(kUrl);
 
   ExpectOfflinePageServed(offline_id1, kFileSize1,
@@ -1709,4 +1765,142 @@
   EXPECT_EQ(expected_data, data_received());
 }
 
+TEST_F(OfflinePageRequestJobTest, LoadFromFileUrlIntent) {
+  SimulateHasNetworkConnectivity(true);
+
+  std::string expected_data(MakeContentOfSize(2 * 1024));
+  ArchiveValidator archive_validator;
+  archive_validator.Update(expected_data.c_str(), expected_data.length());
+  std::string expected_digest = archive_validator.Finish();
+  int expected_size = expected_data.length();
+
+  // Create a file with unmodified data. The path to this file will be feed
+  // into "intent_url" of extra headers.
+  base::FilePath unmodified_file_path = CreateFileWithContent(expected_data);
+
+  // Create a file with modified data. An offline page is created to associate
+  // with this file, but with size and digest matching the unmodified version.
+  std::string modified_data(expected_data);
+  modified_data[10] = '@';
+  base::FilePath modified_file_path = CreateFileWithContent(modified_data);
+
+  int64_t offline_id = SavePublicPage(kUrl, GURL(), modified_file_path,
+                                      expected_size, expected_digest);
+
+  // Load an URL with custom header that contains "intent_url" pointing to
+  // unmodified file. Expect the file from the intent URL is fetched.
+  net::HttpRequestHeaders extra_headers;
+  extra_headers.AddHeaderFromString(UseOfflinePageHeaderForIntent(
+      OfflinePageHeader::Reason::FILE_URL_INTENT, offline_id,
+      net::FilePathToFileURL(unmodified_file_path)));
+  LoadPageWithHeaders(kUrl, extra_headers);
+
+  ExpectOfflinePageServed(offline_id, expected_size,
+                          OfflinePageRequestJob::AggregatedRequestResult::
+                              SHOW_OFFLINE_ON_CONNECTED_NETWORK);
+  EXPECT_EQ(expected_data, data_received());
+}
+
+TEST_F(OfflinePageRequestJobTest, IntentFileNotFound) {
+  SimulateHasNetworkConnectivity(true);
+
+  std::string expected_data(MakeContentOfSize(2 * 1024));
+  ArchiveValidator archive_validator;
+  archive_validator.Update(expected_data.c_str(), expected_data.length());
+  std::string expected_digest = archive_validator.Finish();
+  int expected_size = expected_data.length();
+
+  // Create a file with unmodified data. An offline page is created to associate
+  // with this file.
+  base::FilePath unmodified_file_path = CreateFileWithContent(expected_data);
+
+  // Get a path pointing to non-existing file. This path will be feed into
+  // "intent_url" of extra headers.
+  base::FilePath nonexistent_file_path =
+      unmodified_file_path.DirName().AppendASCII("nonexistent");
+
+  int64_t offline_id = SavePublicPage(kUrl, GURL(), unmodified_file_path,
+                                      expected_size, expected_digest);
+
+  // Load an URL with custom header that contains "intent_url" pointing to
+  // non-existent file. Expect the request fails.
+  net::HttpRequestHeaders extra_headers;
+  extra_headers.AddHeaderFromString(UseOfflinePageHeaderForIntent(
+      OfflinePageHeader::Reason::FILE_URL_INTENT, offline_id,
+      net::FilePathToFileURL(nonexistent_file_path)));
+  LoadPageWithHeaders(kUrl, extra_headers);
+
+  ExpectOpenFileErrorCode(net::ERR_FILE_NOT_FOUND);
+  EXPECT_EQ(net::ERR_FAILED, request_status());
+  EXPECT_EQ(0, bytes_read());
+  EXPECT_FALSE(is_offline_page_set_in_navigation_data());
+  EXPECT_FALSE(offline_page_tab_helper()->GetOfflinePageForTest());
+}
+
+TEST_F(OfflinePageRequestJobTest, IntentFileModifiedInTheMiddle) {
+  SimulateHasNetworkConnectivity(true);
+
+  std::string expected_data(MakeContentOfSize(2 * 1024));
+  ArchiveValidator archive_validator;
+  archive_validator.Update(expected_data.c_str(), expected_data.length());
+  std::string expected_digest = archive_validator.Finish();
+  int expected_size = expected_data.length();
+
+  // Create a file with modified data in the middle. An offline page is created
+  // to associate with this modified file, but with size and digest matching the
+  // unmodified version.
+  std::string modified_data(expected_data);
+  modified_data[10] = '@';
+  base::FilePath modified_file_path = CreateFileWithContent(modified_data);
+
+  int64_t offline_id = SavePublicPage(kUrl, GURL(), modified_file_path,
+                                      expected_size, expected_digest);
+
+  // Load an URL with custom header that contains "intent_url" pointing to
+  // modified file. Expect the request fails.
+  net::HttpRequestHeaders extra_headers;
+  extra_headers.AddHeaderFromString(UseOfflinePageHeaderForIntent(
+      OfflinePageHeader::Reason::FILE_URL_INTENT, offline_id,
+      net::FilePathToFileURL(modified_file_path)));
+  LoadPageWithHeaders(kUrl, extra_headers);
+
+  EXPECT_EQ(net::ERR_FAILED, request_status());
+  EXPECT_EQ(0, bytes_read());
+  EXPECT_FALSE(is_offline_page_set_in_navigation_data());
+  EXPECT_FALSE(offline_page_tab_helper()->GetOfflinePageForTest());
+}
+
+TEST_F(OfflinePageRequestJobTest, IntentFileModifiedWithMoreDataAppended) {
+  SimulateHasNetworkConnectivity(true);
+
+  std::string expected_data(MakeContentOfSize(2 * 1024));
+  ArchiveValidator archive_validator;
+  archive_validator.Update(expected_data.c_str(), expected_data.length());
+  std::string expected_digest = archive_validator.Finish();
+  int expected_size = expected_data.length();
+
+  // Create a file with more data appended. An offline page is created to
+  // associate with this modified file, but with size and digest matching the
+  // unmodified version.
+  std::string modified_data(expected_data);
+  modified_data += "foo";
+  base::FilePath modified_file_path = CreateFileWithContent(modified_data);
+
+  int64_t offline_id = SavePublicPage(kUrl, GURL(), modified_file_path,
+                                      expected_size, expected_digest);
+
+  // Load an URL with custom header that contains "intent_url" pointing to
+  // modified file. Expect the request fails.
+  net::HttpRequestHeaders extra_headers;
+  extra_headers.AddHeaderFromString(UseOfflinePageHeaderForIntent(
+      OfflinePageHeader::Reason::FILE_URL_INTENT, offline_id,
+      net::FilePathToFileURL(modified_file_path)));
+  LoadPageWithHeaders(kUrl, extra_headers);
+
+  EXPECT_EQ(net::ERR_FAILED, request_status());
+  EXPECT_EQ(0, bytes_read());
+  EXPECT_FALSE(is_offline_page_set_in_navigation_data());
+  EXPECT_FALSE(offline_page_tab_helper()->GetOfflinePageForTest());
+}
+
 }  // namespace offline_pages
diff --git a/chrome/browser/offline_pages/offline_page_tab_helper.cc b/chrome/browser/offline_pages/offline_page_tab_helper.cc
index ca19f3e..4be14ccb 100644
--- a/chrome/browser/offline_pages/offline_page_tab_helper.cc
+++ b/chrome/browser/offline_pages/offline_page_tab_helper.cc
@@ -312,6 +312,11 @@
   provisional_offline_info_.is_showing_offline_preview = is_offline_preview;
 }
 
+void OfflinePageTabHelper::ClearOfflinePage() {
+  provisional_offline_info_.Clear();
+  offline_info_.Clear();
+}
+
 bool OfflinePageTabHelper::IsShowingTrustedOfflinePage() const {
   return offline_info_.offline_page && offline_info_.is_trusted;
 }
diff --git a/chrome/browser/offline_pages/offline_page_tab_helper.h b/chrome/browser/offline_pages/offline_page_tab_helper.h
index 454fd65..956c0508 100644
--- a/chrome/browser/offline_pages/offline_page_tab_helper.h
+++ b/chrome/browser/offline_pages/offline_page_tab_helper.h
@@ -36,6 +36,8 @@
                       bool is_trusted,
                       bool is_offline_preview);
 
+  void ClearOfflinePage();
+
   const OfflinePageItem* offline_page() {
     return offline_info_.offline_page.get();
   }
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
index 9e0a05a..e3093d3c 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
@@ -161,7 +161,11 @@
   if (change_type != TabChangeType::kAll)
     return;
   auto it = tabs_.find(contents);
-  DCHECK(it != tabs_.end());
+  // The WebContents destructor might cause this function to be called, at this
+  // point TabClosingAt has already been called and so this WebContents has
+  // been removed from |tabs_|.
+  if (it == tabs_.end())
+    return;
   TabLifecycleUnit* lifecycle_unit = it->second.get();
   lifecycle_unit->SetRecentlyAudible(contents->WasRecentlyAudible());
 }
diff --git a/chrome/browser/resource_coordinator/tab_manager.cc b/chrome/browser/resource_coordinator/tab_manager.cc
index 387fa9a9..143a574e 100644
--- a/chrome/browser/resource_coordinator/tab_manager.cc
+++ b/chrome/browser/resource_coordinator/tab_manager.cc
@@ -482,7 +482,13 @@
     DCHECK(tab_lifecycle_unit_external);
     content::WebContents* content =
         tab_lifecycle_unit_external->GetWebContents();
-    DCHECK(content);
+    // TODO(fdoray): Check if TabLifecycleUnitSource should override
+    // WebContentsObserver::WebContentsDestroyed() as in some situations a
+    // WebContents might get destroyed without a call to
+    // TabStripModelObserver::TabClosingAt, in this case we'll have a
+    // TabLifecycleUnitExternal that points to a null WebContents.
+    if (content == nullptr)
+      return;
 
     content::RenderProcessHost* render_process_host =
         content->GetMainFrame()->GetProcess();
diff --git a/chrome/browser/resources/md_extensions/sidebar.html b/chrome/browser/resources/md_extensions/sidebar.html
index 5e89ab45..e243da81 100644
--- a/chrome/browser/resources/md_extensions/sidebar.html
+++ b/chrome/browser/resources/md_extensions/sidebar.html
@@ -34,10 +34,11 @@
         /* Ensure the focus outline appears correctly (crbug.com/655503). */
         -webkit-margin-end: 4px;
         -webkit-padding-start: 24px;
+        align-items: center;
         color: inherit;
-        display: block;
+        display: flex;
         font-weight: 500;
-        line-height: 40px;
+        min-height: 40px;
         position: relative;
         text-decoration: none;
       }
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js
index 6bb7629..638e1f95 100644
--- a/chrome/browser/resources/print_preview/data/destination_store.js
+++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -720,7 +720,7 @@
                   (caps) => this.onCapabilitiesSet_(
                       destination.origin, destination.id, caps),
                   () => this.onGetCapabilitiesFail_(
-                      destination.origin, destination.origin));
+                      destination.origin, destination.id));
         } else {
           assert(
               this.cloudPrintInterface_ != null,
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js
index 0972809..3f6fcb1 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.js
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -89,6 +89,8 @@
     CrPolicyStrings = {
       controlledSettingExtension:
           loadTimeData.getString('controlledSettingExtension'),
+      controlledSettingExtensionWithoutName:
+          loadTimeData.getString('controlledSettingExtensionWithoutName'),
       controlledSettingPolicy:
           loadTimeData.getString('controlledSettingPolicy'),
       controlledSettingRecommendedMatches:
diff --git a/chrome/browser/subresource_filter/subresource_filter_test_harness.cc b/chrome/browser/subresource_filter/subresource_filter_test_harness.cc
index a5cb2de..7bd3eb79 100644
--- a/chrome/browser/subresource_filter/subresource_filter_test_harness.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_test_harness.cc
@@ -90,8 +90,7 @@
           base::ThreadTaskRunnerHandle::Get());
   auto ruleset_service = std::make_unique<subresource_filter::RulesetService>(
       &pref_service_, base::ThreadTaskRunnerHandle::Get(),
-      base::ThreadTaskRunnerHandle::Get(), content_service.get(),
-      ruleset_service_dir_.GetPath());
+      content_service.get(), ruleset_service_dir_.GetPath());
   content_service->set_ruleset_service(std::move(ruleset_service));
   TestingBrowserProcess::GetGlobal()->SetRulesetService(
       std::move(content_service));
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 3c4217e..be9b478d 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3463,6 +3463,8 @@
       "app_list/search/arc/arc_playstore_search_provider.h",
       "app_list/search/arc/arc_playstore_search_result.cc",
       "app_list/search/arc/arc_playstore_search_result.h",
+      "app_list/search/arc/icon_decode_request.cc",
+      "app_list/search/arc/icon_decode_request.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",
diff --git a/chrome/browser/ui/app_list/app_list_controller_browsertest.cc b/chrome/browser/ui/app_list/app_list_controller_browsertest.cc
index 1574efb..b94f1e13 100644
--- a/chrome/browser/ui/app_list/app_list_controller_browsertest.cc
+++ b/chrome/browser/ui/app_list/app_list_controller_browsertest.cc
@@ -4,8 +4,6 @@
 
 #include <stddef.h>
 
-#include "ash/app_list/model/search/search_model.h"
-#include "ash/app_list/model/search/search_result.h"
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/path_service.h"
@@ -15,6 +13,7 @@
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
+#include "chrome/browser/ui/app_list/app_list_model_updater.h"
 #include "chrome/browser/ui/app_list/app_list_service.h"
 #include "chrome/browser/ui/app_list/test/chrome_app_list_test_support.h"
 #include "chrome/browser/ui/browser.h"
@@ -26,11 +25,10 @@
 #include "chromeos/chromeos_switches.h"
 #include "components/user_manager/user_names.h"
 #include "ui/app_list/app_list_switches.h"
-#include "ui/base/models/list_model_observer.h"
 
 // Browser Test for AppListController that runs on all platforms supporting
 // app_list.
-typedef InProcessBrowserTest AppListControllerBrowserTest;
+using AppListControllerBrowserTest = InProcessBrowserTest;
 
 // Test the CreateNewWindow function of the controller delegate.
 IN_PROC_BROWSER_TEST_F(AppListControllerBrowserTest, CreateNewWindow) {
@@ -51,59 +49,7 @@
 }
 
 // Browser Test for AppListController that observes search result changes.
-class AppListControllerSearchResultsBrowserTest
-    : public ExtensionBrowserTest,
-      public ui::ListModelObserver {
- public:
-  AppListControllerSearchResultsBrowserTest()
-      : observed_result_(nullptr), observed_results_list_(nullptr) {}
-
-  void WatchResultsLookingForItem(
-      app_list::SearchModel::SearchResults* search_results,
-      const std::string& extension_name) {
-    EXPECT_FALSE(observed_results_list_);
-    observed_results_list_ = search_results;
-    observed_results_list_->AddObserver(this);
-    item_to_observe_ = base::ASCIIToUTF16(extension_name);
-  }
-
-  void StopWatchingResults() {
-    EXPECT_TRUE(observed_results_list_);
-    observed_results_list_->RemoveObserver(this);
-  }
-
- protected:
-  void AttemptToLocateItem() {
-    observed_result_ = nullptr;
-
-    for (size_t i = 0; i < observed_results_list_->item_count(); ++i) {
-      if (observed_results_list_->GetItemAt(i)->title() != item_to_observe_)
-        continue;
-
-      // Ensure there is at most one.
-      EXPECT_FALSE(observed_result_);
-      observed_result_ = observed_results_list_->GetItemAt(i);
-    }
-  }
-
-  // Overridden from ui::ListModelObserver:
-  void ListItemsAdded(size_t start, size_t count) override {
-    AttemptToLocateItem();
-  }
-  void ListItemsRemoved(size_t start, size_t count) override {
-    AttemptToLocateItem();
-  }
-  void ListItemMoved(size_t index, size_t target_index) override {}
-  void ListItemsChanged(size_t start, size_t count) override {}
-
-  app_list::SearchResult* observed_result_;
-
- private:
-  base::string16 item_to_observe_;
-  app_list::SearchModel::SearchResults* observed_results_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppListControllerSearchResultsBrowserTest);
-};
+using AppListControllerSearchResultsBrowserTest = ExtensionBrowserTest;
 
 // Test showing search results, and uninstalling one of them while displayed.
 IN_PROC_BROWSER_TEST_F(AppListControllerSearchResultsBrowserTest,
@@ -113,36 +59,39 @@
   test_extension_path = test_extension_path.AppendASCII("extensions")
       .AppendASCII("platform_apps")
       .AppendASCII("minimal");
-  const extensions::Extension* extension =
-      InstallExtension(test_extension_path,
-                       1 /* expected_change: new install */);
-  ASSERT_TRUE(extension);
 
   AppListService* service = AppListService::Get();
   ASSERT_TRUE(service);
+  AppListModelUpdater* model_updater = test::GetModelUpdater(service);
+  ASSERT_TRUE(model_updater);
+
+  // Install the extension.
+  const extensions::Extension* extension = InstallExtension(
+      test_extension_path, 1 /* expected_change: new install */);
+  ASSERT_TRUE(extension);
+
+  const std::string title = extension->name();
+
+  // Show the app list first, otherwise we won't have a search box to update.
   service->ShowForProfile(browser()->profile());
-
-  app_list::SearchModel* model = test::GetSearchModel(service);
-  ASSERT_TRUE(model);
-  WatchResultsLookingForItem(model->results(), extension->name());
-
-  // Ensure a search finds the extension.
-  EXPECT_FALSE(observed_result_);
-  model->search_box()->Update(base::ASCIIToUTF16("minimal"),
-                              true /* initiated_by_user */);
-  EXPECT_TRUE(observed_result_);
-
-  // Ensure the UI is updated. This is via PostTask in views.
   base::RunLoop().RunUntilIdle();
 
-  // Now uninstall and ensure this browser test observes it.
+  // Currently the search box is empty, so we have no result.
+  EXPECT_FALSE(model_updater->GetResultByTitle(title));
+
+  // Now a search finds the extension.
+  model_updater->UpdateSearchBox(base::ASCIIToUTF16(title),
+                                 true /* initiated_by_user */);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(model_updater->GetResultByTitle(title));
+
+  // Uninstall the extension.
   UninstallExtension(extension->id());
-
-  // Allow async AppSearchProvider::UpdateResults to run.
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_FALSE(observed_result_);
-  StopWatchingResults();
+  // We cannot find the extension any more.
+  EXPECT_FALSE(model_updater->GetResultByTitle(title));
+
   service->DismissAppList();
 }
 
diff --git a/chrome/browser/ui/app_list/app_list_model_updater.h b/chrome/browser/ui/app_list/app_list_model_updater.h
index d816486..eb339a3 100644
--- a/chrome/browser/ui/app_list/app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/app_list_model_updater.h
@@ -120,6 +120,8 @@
   virtual bool SearchEngineIsGoogle() = 0;
   virtual app_list::SearchResult* FindSearchResult(
       const std::string& result_id) = 0;
+  virtual app_list::SearchResult* GetResultByTitle(
+      const std::string& title) = 0;
 
   virtual void SetDelegate(AppListModelUpdaterDelegate* delegate) = 0;
 };
diff --git a/chrome/browser/ui/app_list/arc/arc_app_dialog.h b/chrome/browser/ui/app_list/arc/arc_app_dialog.h
index ce5a2d7b..f0ffc9e 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_dialog.h
+++ b/chrome/browser/ui/app_list/arc/arc_app_dialog.h
@@ -14,6 +14,8 @@
 
 namespace arc {
 
+using ArcUsbConfirmCallback = base::OnceCallback<void(bool)>;
+
 // Shows a dialog for user to confirm uninstallation of ARC app.
 // Currently, ARC app can only be manually uninstalled from AppList. But it
 // would be simple to enable the dialog to shown from other source.
@@ -21,6 +23,17 @@
                                AppListControllerDelegate* controller,
                                const std::string& app_id);
 
+// Shows permission request dialog for scan USB device list.
+void ShowUsbScanDeviceListPermissionDialog(Profile* profile,
+                                           const std::string& app_id,
+                                           ArcUsbConfirmCallback callback);
+
+// Shows permission request dialog for targeting device name.
+void ShowUsbAccessPermissionDialog(Profile* profile,
+                                   const std::string& app_id,
+                                   const base::string16& device_name,
+                                   ArcUsbConfirmCallback callback);
+
 // Test purpose methods.
 bool IsArcAppDialogViewAliveForTest();
 
diff --git a/chrome/browser/ui/app_list/arc/arc_usb_host_permission_manager.cc b/chrome/browser/ui/app_list/arc/arc_usb_host_permission_manager.cc
index 2d9b6ea..2335aa8bf 100644
--- a/chrome/browser/ui/app_list/arc/arc_usb_host_permission_manager.cc
+++ b/chrome/browser/ui/app_list/arc/arc_usb_host_permission_manager.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_dialog.h"
 #include "chrome/browser/ui/app_list/arc/arc_usb_host_permission_manager_factory.h"
 #include "components/arc/usb/usb_host_bridge.h"
 #include "extensions/browser/api/device_permissions_manager.h"
@@ -254,7 +255,7 @@
     if (HasUsbScanDeviceListPermission(current_requesting_package_)) {
       OnUsbPermissionReceived(std::move(current_request), true);
     } else {
-      ShowScanDeviceListPermissionDialog(
+      ShowUsbScanDeviceListPermissionDialog(
           profile_, app_id,
           base::BindOnce(&ArcUsbHostPermissionManager::OnUsbPermissionReceived,
                          weak_ptr_factory_.GetWeakPtr(),
@@ -265,7 +266,7 @@
                                *current_request.usb_device_entry())) {
       OnUsbPermissionReceived(std::move(current_request), true);
     } else {
-      ShowAccessPermissionDialog(
+      ShowUsbAccessPermissionDialog(
           profile_, app_id, current_request.usb_device_entry()->device_name,
           base::BindOnce(&ArcUsbHostPermissionManager::OnUsbPermissionReceived,
                          weak_ptr_factory_.GetWeakPtr(),
diff --git a/chrome/browser/ui/app_list/arc/arc_usb_host_permission_manager.h b/chrome/browser/ui/app_list/arc/arc_usb_host_permission_manager.h
index 332a01d..a7d224d 100644
--- a/chrome/browser/ui/app_list/arc/arc_usb_host_permission_manager.h
+++ b/chrome/browser/ui/app_list/arc/arc_usb_host_permission_manager.h
@@ -24,8 +24,6 @@
 
 class ArcUsbHostBridge;
 
-using ArcUsbConfirmCallback = base::OnceCallback<void(bool)>;
-
 class ArcUsbHostPermissionManager : public ArcAppListPrefs::Observer,
                                     public ArcUsbHostUiDelegate,
                                     public KeyedService {
@@ -102,18 +100,6 @@
   static ArcUsbHostPermissionManager* GetForBrowserContext(
       content::BrowserContext* context);
 
-  // Shows permission request dialog for scan USB device list.
-  static void ShowScanDeviceListPermissionDialog(
-      Profile* profile,
-      const std::string& app_id,
-      ArcUsbConfirmCallback callback);
-
-  // Shows permission request dialog for targeting device name.
-  static void ShowAccessPermissionDialog(Profile* profile,
-                                         const std::string& app_id,
-                                         const base::string16& device_name,
-                                         ArcUsbConfirmCallback callback);
-
   // ArcUsbHostUiDelegate:
   void RequestUsbScanDeviceListPermission(
       const std::string& package_name,
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
index f6cae1b..38139be4 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
+++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
@@ -12,6 +12,7 @@
 #include "ash/app_list/model/app_list_model.h"
 #include "ash/app_list/model/app_list_model_observer.h"
 #include "ash/app_list/model/search/search_model.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/app_list/chrome_app_list_item.h"
 #include "extensions/common/constants.h"
 #include "ui/base/models/menu_model.h"
@@ -248,7 +249,27 @@
 
 app_list::SearchResult* ChromeAppListModelUpdater::FindSearchResult(
     const std::string& result_id) {
-  return search_model_->FindSearchResult(result_id);
+  return search_model_ ? search_model_->FindSearchResult(result_id) : nullptr;
+}
+
+app_list::SearchResult* ChromeAppListModelUpdater::GetResultByTitle(
+    const std::string& title) {
+  if (!search_model_)
+    return nullptr;
+
+  base::string16 target_title = base::ASCIIToUTF16(title);
+  // TODO(hejq): Currently we use a search result's type and diaplay type to
+  //             check whether it's a result of uninstalled result. We might
+  //             have an attribute to do this when we refactor SearchResult.
+  for (const auto& result : *search_model_->results()) {
+    if (result->title() == target_title &&
+        result->result_type() == app_list::SearchResult::RESULT_INSTALLED_APP &&
+        result->display_type() !=
+            app_list::SearchResult::DISPLAY_RECOMMENDATION) {
+      return result.get();
+    }
+  }
+  return nullptr;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.h b/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
index 2f24b7d..8103df0 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
@@ -83,6 +83,7 @@
                                int event_flags) override;
   app_list::SearchResult* FindSearchResult(
       const std::string& result_id) override;
+  app_list::SearchResult* GetResultByTitle(const std::string& title) override;
 
   // Methods for AppListSyncableService:
   void AddItemToOemFolder(
diff --git a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.cc b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.cc
index 9fe37b22..d6cebc2 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.cc
+++ b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.cc
@@ -8,29 +8,23 @@
 
 #include "base/metrics/user_metrics.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/image_decoder.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/arc/arc_playstore_app_context_menu.h"
+#include "chrome/browser/ui/app_list/search/arc/icon_decode_request.h"
 #include "chrome/browser/ui/app_list/search/search_util.h"
-#include "chrome/grit/component_extension_resources.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/common/app.mojom.h"
 #include "components/crx_file/id_util.h"
-#include "content/public/browser/browser_thread.h"
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/vector_icons/vector_icons.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/geometry/size.h"
-#include "ui/gfx/image/image_skia_operations.h"
-#include "ui/gfx/image/image_skia_rep.h"
-#include "ui/gfx/image/image_skia_source.h"
 #include "ui/gfx/paint_vector_icon.h"
 
-using content::BrowserThread;
-
 namespace {
+
 bool disable_safe_decoding_for_testing = false;
 // The id prefix to identify a Play Store search result.
 constexpr char kPlayAppPrefix[] = "play://";
@@ -63,107 +57,6 @@
 
 namespace app_list {
 
-////////////////////////////////////////////////////////////////////////////////
-// IconSource
-
-class IconSource : public gfx::ImageSkiaSource {
- public:
-  IconSource(const SkBitmap& decoded_bitmap, int resource_size_in_dip);
-  explicit IconSource(int resource_size_in_dip);
-  ~IconSource() override = default;
-
-  void SetDecodedImage(const SkBitmap& decoded_bitmap);
-
- private:
-  gfx::ImageSkiaRep GetImageForScale(float scale) override;
-
-  const int resource_size_in_dip_;
-  gfx::ImageSkia decoded_icon_;
-
-  DISALLOW_COPY_AND_ASSIGN(IconSource);
-};
-
-IconSource::IconSource(int resource_size_in_dip)
-    : resource_size_in_dip_(resource_size_in_dip) {}
-
-void IconSource::SetDecodedImage(const SkBitmap& decoded_bitmap) {
-  decoded_icon_.AddRepresentation(gfx::ImageSkiaRep(
-      decoded_bitmap, ui::GetScaleForScaleFactor(ui::SCALE_FACTOR_100P)));
-}
-
-gfx::ImageSkiaRep IconSource::GetImageForScale(float scale) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  // We use the icon if it was decoded successfully, otherwise use the default
-  // ARC icon.
-  const gfx::ImageSkia* icon_to_scale;
-  if (decoded_icon_.isNull()) {
-    int resource_id =
-        scale >= 1.5f ? IDR_ARC_SUPPORT_ICON_96 : IDR_ARC_SUPPORT_ICON_48;
-    icon_to_scale =
-        ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id);
-  } else {
-    icon_to_scale = &decoded_icon_;
-  }
-  DCHECK(icon_to_scale);
-
-  gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage(
-      *icon_to_scale, skia::ImageOperations::RESIZE_BEST,
-      gfx::Size(resource_size_in_dip_, resource_size_in_dip_));
-  return resized_image.GetRepresentation(scale);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// IconDecodeRequest
-
-class ArcPlayStoreSearchResult::IconDecodeRequest
-    : public ImageDecoder::ImageRequest {
- public:
-  explicit IconDecodeRequest(ArcPlayStoreSearchResult* search_result)
-      : search_result_(search_result) {}
-  ~IconDecodeRequest() override = default;
-
-  // ImageDecoder::ImageRequest overrides.
-  void OnImageDecoded(const SkBitmap& bitmap) override {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-    const gfx::Size resource_size(app_list::kGridIconDimension,
-                                  app_list::kGridIconDimension);
-    auto icon_source =
-        std::make_unique<IconSource>(app_list::kGridIconDimension);
-    icon_source->SetDecodedImage(bitmap);
-    const gfx::ImageSkia icon =
-        gfx::ImageSkia(std::move(icon_source), resource_size);
-    icon.EnsureRepsForSupportedScales();
-
-    search_result_->SetIcon(icon);
-  }
-
-  void OnDecodeImageFailed() override {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    DLOG(ERROR) << "Failed to decode an app icon image.";
-
-    const gfx::Size resource_size(app_list::kGridIconDimension,
-                                  app_list::kGridIconDimension);
-    auto icon_source =
-        std::make_unique<IconSource>(app_list::kGridIconDimension);
-    const gfx::ImageSkia icon =
-        gfx::ImageSkia(std::move(icon_source), resource_size);
-    icon.EnsureRepsForSupportedScales();
-
-    search_result_->SetIcon(icon);
-  }
-
- private:
-  // ArcPlayStoreSearchResult owns IconDecodeRequest, so it will outlive this.
-  ArcPlayStoreSearchResult* const search_result_;
-
-  DISALLOW_COPY_AND_ASSIGN(IconDecodeRequest);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-// ArcPlayStoreSearchResult
-
 // static
 void ArcPlayStoreSearchResult::DisableSafeDecodingForTesting() {
   disable_safe_decoding_for_testing = true;
@@ -175,7 +68,8 @@
     AppListControllerDelegate* list_controller)
     : data_(std::move(data)),
       profile_(profile),
-      list_controller_(list_controller) {
+      list_controller_(list_controller),
+      weak_ptr_factory_(this) {
   set_title(base::UTF8ToUTF16(label().value()));
   set_id(kPlayAppPrefix +
          crx_file::id_util::GenerateId(install_intent_uri().value()));
@@ -187,7 +81,8 @@
   SetRating(review_score());
   set_result_type(is_instant_app() ? RESULT_INSTANT_APP : RESULT_PLAYSTORE_APP);
 
-  icon_decode_request_ = std::make_unique<IconDecodeRequest>(this);
+  icon_decode_request_ = std::make_unique<IconDecodeRequest>(base::BindOnce(
+      &ArcPlayStoreSearchResult::SetIcon, weak_ptr_factory_.GetWeakPtr()));
   if (disable_safe_decoding_for_testing) {
     SkBitmap bitmap;
     if (!icon_png_data().empty() &&
diff --git a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.h b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.h
index 9c50df98..fed8c66 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.h
+++ b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "ash/app_list/model/search/search_result.h"
+#include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "chrome/browser/ui/app_list/app_context_menu_delegate.h"
 #include "components/arc/common/app.mojom.h"
@@ -20,6 +21,8 @@
 
 namespace app_list {
 
+class IconDecodeRequest;
+
 class ArcPlayStoreSearchResult : public SearchResult,
                                  public AppContextMenuDelegate {
  public:
@@ -44,8 +47,6 @@
   static void DisableSafeDecodingForTesting();
 
  private:
-  class IconDecodeRequest;
-
   const base::Optional<std::string>& install_intent_uri() const {
     return data_->install_intent_uri;
   }
@@ -69,6 +70,8 @@
   AppListControllerDelegate* const list_controller_;
   std::unique_ptr<ArcPlayStoreAppContextMenu> context_menu_;
 
+  base::WeakPtrFactory<ArcPlayStoreSearchResult> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(ArcPlayStoreSearchResult);
 };
 
diff --git a/chrome/browser/ui/app_list/search/arc/icon_decode_request.cc b/chrome/browser/ui/app_list/search/arc/icon_decode_request.cc
new file mode 100644
index 0000000..7fc64a9
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/arc/icon_decode_request.cc
@@ -0,0 +1,101 @@
+// 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/ui/app_list/search/arc/icon_decode_request.h"
+
+#include "ash/app_list/model/search/search_result.h"
+#include "chrome/grit/component_extension_resources.h"
+#include "content/public/browser/browser_thread.h"
+#include "ui/app_list/app_list_constants.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/image/image_skia_rep.h"
+#include "ui/gfx/image/image_skia_source.h"
+
+using content::BrowserThread;
+
+namespace app_list {
+
+namespace {
+
+class IconSource : public gfx::ImageSkiaSource {
+ public:
+  IconSource(const SkBitmap& decoded_bitmap, int resource_size_in_dip);
+  explicit IconSource(int resource_size_in_dip);
+  ~IconSource() override = default;
+
+  void SetDecodedImage(const SkBitmap& decoded_bitmap);
+
+ private:
+  gfx::ImageSkiaRep GetImageForScale(float scale) override;
+
+  const int resource_size_in_dip_;
+  gfx::ImageSkia decoded_icon_;
+
+  DISALLOW_COPY_AND_ASSIGN(IconSource);
+};
+
+IconSource::IconSource(int resource_size_in_dip)
+    : resource_size_in_dip_(resource_size_in_dip) {}
+
+void IconSource::SetDecodedImage(const SkBitmap& decoded_bitmap) {
+  decoded_icon_.AddRepresentation(gfx::ImageSkiaRep(
+      decoded_bitmap, ui::GetScaleForScaleFactor(ui::SCALE_FACTOR_100P)));
+}
+
+gfx::ImageSkiaRep IconSource::GetImageForScale(float scale) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  // We use the icon if it was decoded successfully, otherwise use the default
+  // ARC icon.
+  const gfx::ImageSkia* icon_to_scale;
+  if (decoded_icon_.isNull()) {
+    int resource_id =
+        scale >= 1.5f ? IDR_ARC_SUPPORT_ICON_96 : IDR_ARC_SUPPORT_ICON_48;
+    icon_to_scale =
+        ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id);
+  } else {
+    icon_to_scale = &decoded_icon_;
+  }
+  DCHECK(icon_to_scale);
+
+  gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage(
+      *icon_to_scale, skia::ImageOperations::RESIZE_BEST,
+      gfx::Size(resource_size_in_dip_, resource_size_in_dip_));
+  return resized_image.GetRepresentation(scale);
+}
+
+}  // namespace
+
+IconDecodeRequest::IconDecodeRequest(SetIconCallback set_icon_callback)
+    : set_icon_callback_(std::move(set_icon_callback)) {}
+
+IconDecodeRequest::~IconDecodeRequest() = default;
+
+void IconDecodeRequest::OnImageDecoded(const SkBitmap& bitmap) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  const gfx::Size resource_size(kGridIconDimension, kGridIconDimension);
+  auto icon_source = std::make_unique<IconSource>(kGridIconDimension);
+  icon_source->SetDecodedImage(bitmap);
+  const gfx::ImageSkia icon =
+      gfx::ImageSkia(std::move(icon_source), resource_size);
+  icon.EnsureRepsForSupportedScales();
+
+  std::move(set_icon_callback_).Run(icon);
+}
+
+void IconDecodeRequest::OnDecodeImageFailed() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DLOG(ERROR) << "Failed to decode an icon image.";
+
+  const gfx::Size resource_size(kGridIconDimension, kGridIconDimension);
+  auto icon_source = std::make_unique<IconSource>(kGridIconDimension);
+  const gfx::ImageSkia icon =
+      gfx::ImageSkia(std::move(icon_source), resource_size);
+  icon.EnsureRepsForSupportedScales();
+
+  std::move(set_icon_callback_).Run(icon);
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/arc/icon_decode_request.h b/chrome/browser/ui/app_list/search/arc/icon_decode_request.h
new file mode 100644
index 0000000..2dd1116
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/arc/icon_decode_request.h
@@ -0,0 +1,37 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_ARC_ICON_DECODE_REQUEST_H_
+#define CHROME_BROWSER_UI_APP_LIST_SEARCH_ARC_ICON_DECODE_REQUEST_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chrome/browser/image_decoder.h"
+
+namespace gfx {
+class ImageSkia;
+}  // namespace gfx
+
+namespace app_list {
+
+class IconDecodeRequest : public ImageDecoder::ImageRequest {
+ public:
+  using SetIconCallback = base::OnceCallback<void(const gfx::ImageSkia& icon)>;
+
+  explicit IconDecodeRequest(SetIconCallback set_icon_callback);
+  ~IconDecodeRequest() override;
+
+  // ImageDecoder::ImageRequest:
+  void OnImageDecoded(const SkBitmap& bitmap) override;
+  void OnDecodeImageFailed() override;
+
+ private:
+  SetIconCallback set_icon_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(IconDecodeRequest);
+};
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_ARC_ICON_DECODE_REQUEST_H_
diff --git a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc
index 2a2a531..2913925c 100644
--- a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc
+++ b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc
@@ -137,6 +137,11 @@
   return nullptr;
 }
 
+app_list::SearchResult* FakeAppListModelUpdater::GetResultByTitle(
+    const std::string& title) {
+  return nullptr;
+}
+
 void FakeAppListModelUpdater::PublishSearchResults(
     std::vector<std::unique_ptr<app_list::SearchResult>> results) {
   search_results_ = std::move(results);
diff --git a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h
index f413b5bd..a2a689b 100644
--- a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h
@@ -57,6 +57,7 @@
   bool SearchEngineIsGoogle() override;
   app_list::SearchResult* FindSearchResult(
       const std::string& result_id) override;
+  app_list::SearchResult* GetResultByTitle(const std::string& title) override;
   const std::vector<std::unique_ptr<app_list::SearchResult>>& search_results()
       const {
     return search_results_;
diff --git a/chrome/browser/ui/ash/login_screen_client.cc b/chrome/browser/ui/ash/login_screen_client.cc
index b8cb99f..ce63aa3 100644
--- a/chrome/browser/ui/ash/login_screen_client.cc
+++ b/chrome/browser/ui/ash/login_screen_client.cc
@@ -196,6 +196,21 @@
   login_screen_->IsReadyForPassword(std::move(callback));
 }
 
+void LoginScreenClient::SetPublicSessionDisplayName(
+    const AccountId& account_id,
+    const std::string& display_name) {
+  login_screen_->SetPublicSessionDisplayName(account_id, display_name);
+}
+
+void LoginScreenClient::SetPublicSessionLocales(
+    const AccountId& account_id,
+    std::unique_ptr<base::ListValue> locales,
+    const std::string& default_locale,
+    bool show_advanced_view) {
+  login_screen_->SetPublicSessionLocales(account_id, std::move(locales),
+                                         default_locale, show_advanced_view);
+}
+
 void LoginScreenClient::SetDelegate(Delegate* delegate) {
   delegate_ = delegate;
 }
diff --git a/chrome/browser/ui/ash/login_screen_client.h b/chrome/browser/ui/ash/login_screen_client.h
index 320e7f3..720e923 100644
--- a/chrome/browser/ui/ash/login_screen_client.h
+++ b/chrome/browser/ui/ash/login_screen_client.h
@@ -94,6 +94,12 @@
                          const std::string& bluetooth_name);
   void IsReadyForPassword(
       ash::mojom::LoginScreen::IsReadyForPasswordCallback callback);
+  void SetPublicSessionDisplayName(const AccountId& account_id,
+                                   const std::string& display_name);
+  void SetPublicSessionLocales(const AccountId& account_id,
+                               std::unique_ptr<base::ListValue> locales,
+                               const std::string& default_locale,
+                               bool show_advanced_view);
 
   void SetDelegate(Delegate* delegate);
 
diff --git a/chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.cc b/chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.cc
index 67005889..e7bd3cd0 100644
--- a/chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.cc
+++ b/chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "components/app_modal/javascript_dialog_manager.h"
 #include "components/navigation_metrics/navigation_metrics.h"
+#include "content/public/browser/devtools_agent_host.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "ui/gfx/text_elider.h"
@@ -207,7 +208,8 @@
   CloseDialog(DismissalCause::SUBSEQUENT_DIALOG_SHOWN, false, base::string16());
 
   bool make_pending = false;
-  if (!IsWebContentsForemost(parent_web_contents)) {
+  if (!IsWebContentsForemost(parent_web_contents) &&
+      !content::DevToolsAgentHost::IsDebuggerAttached(parent_web_contents)) {
     switch (dialog_type) {
       case content::JAVASCRIPT_DIALOG_TYPE_ALERT: {
         // When an alert fires in the background, make the callback so that the
@@ -433,8 +435,10 @@
 }
 
 void JavaScriptDialogTabHelper::HandleTabSwitchAway(DismissalCause cause) {
-  if (!dialog_)
+  if (!dialog_ || content::DevToolsAgentHost::IsDebuggerAttached(
+                      WebContentsObserver::web_contents())) {
     return;
+  }
 
   if (dialog_type_ == content::JAVASCRIPT_DIALOG_TYPE_ALERT) {
     // When the user switches tabs, make the callback so that the render process
diff --git a/chrome/browser/ui/views/arc_app_dialog_view.cc b/chrome/browser/ui/views/arc_app_dialog_view.cc
index 9378898..5587a93 100644
--- a/chrome/browser/ui/views/arc_app_dialog_view.cc
+++ b/chrome/browser/ui/views/arc_app_dialog_view.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
+#include "chrome/browser/ui/app_list/arc/arc_usb_host_permission_manager.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
 #include "chrome/grit/generated_resources.h"
@@ -38,8 +39,7 @@
 // Currenty ARC apps only support 48*48 native icon.
 const int kIconSourceSize = 48;
 
-using ArcAppConfirmCallback =
-    base::Callback<void(const std::string& app_id, Profile* profile)>;
+using ArcAppConfirmCallback = base::OnceCallback<void(bool accept)>;
 
 class ArcAppDialogView : public views::DialogDelegateView,
                          public AppIconLoaderDelegate {
@@ -67,6 +67,7 @@
   // views::DialogDelegate:
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool Accept() override;
+  bool Cancel() override;
 
   // AppIconLoaderDelegate:
   void OnAppImageUpdated(const std::string& app_id,
@@ -96,7 +97,7 @@
   DISALLOW_COPY_AND_ASSIGN(ArcAppDialogView);
 };
 
-// Browertest use only. Global pointer of ArcAppDialogView which is shown.
+// Browsertest use only. Global pointer of currently shown ArcAppDialogView.
 ArcAppDialogView* g_current_arc_app_dialog_view = nullptr;
 
 ArcAppDialogView::ArcAppDialogView(Profile* profile,
@@ -114,9 +115,7 @@
       window_title_(window_title),
       confirm_button_text_(confirm_button_text),
       cancel_button_text_(cancel_button_text),
-      confirm_callback_(confirm_callback) {
-  DCHECK(controller);
-
+      confirm_callback_(std::move(confirm_callback)) {
   ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
 
   SetLayoutManager(std::make_unique<views::BoxLayout>(
@@ -150,7 +149,8 @@
 }
 
 ArcAppDialogView::~ArcAppDialogView() {
-  g_current_arc_app_dialog_view = nullptr;
+  if (g_current_arc_app_dialog_view == this)
+    g_current_arc_app_dialog_view = nullptr;
 }
 
 void ArcAppDialogView::AddMultiLineLabel(views::View* parent,
@@ -192,7 +192,14 @@
 }
 
 bool ArcAppDialogView::Accept() {
-  confirm_callback_.Run(app_id_, profile_);
+  if (confirm_callback_)
+    std::move(confirm_callback_).Run(true);
+  return true;
+}
+
+bool ArcAppDialogView::Cancel() {
+  if (confirm_callback_)
+    std::move(confirm_callback_).Run(false);
   return true;
 }
 
@@ -213,7 +220,9 @@
   initial_setup_ = false;
 
   // The parent window was killed before the icon was loaded.
-  if (!AppListService::Get()->IsAppListVisible()) {
+  // TODO(lgcheng@) : Remove this since the dialog is not parented to applist
+  // anymore.
+  if (controller_ && !AppListService::Get()->IsAppListVisible()) {
     g_current_arc_app_dialog_view = nullptr;
     Cancel();
     DialogDelegateView::DeleteDelegate();
@@ -227,16 +236,26 @@
   constrained_window::CreateBrowserModalDialogViews(this, nullptr)->Show();
 }
 
+void HandleArcAppUninstall(base::OnceClosure closure, bool accept) {
+  if (accept)
+    std::move(closure).Run();
+}
+
+std::unique_ptr<ArcAppListPrefs::AppInfo> GetArcAppInfo(
+    Profile* profile,
+    const std::string& app_id) {
+  ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile);
+  DCHECK(arc_prefs);
+  return arc_prefs->GetApp(app_id);
+}
+
 }  // namespace
 
 void ShowArcAppUninstallDialog(Profile* profile,
                                AppListControllerDelegate* controller,
                                const std::string& app_id) {
-  ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile);
-  DCHECK(arc_prefs);
   std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
-      arc_prefs->GetApp(app_id);
-
+      GetArcAppInfo(profile, app_id);
   if (!app_info)
     return;
 
@@ -261,10 +280,66 @@
                   : IDS_EXTENSION_PROMPT_UNINSTALL_APP_BUTTON);
 
   base::string16 cancel_button_text = l10n_util::GetStringUTF16(IDS_CANCEL);
+  new ArcAppDialogView(
+      profile, controller, app_id, window_title, heading_text, subheading_text,
+      confirm_button_text, cancel_button_text,
+      base::BindOnce(HandleArcAppUninstall,
+                     base::BindOnce(UninstallArcApp, app_id, profile)));
+}
 
-  new ArcAppDialogView(profile, controller, app_id, window_title, heading_text,
-                       subheading_text, confirm_button_text, cancel_button_text,
-                       base::Bind(UninstallArcApp));
+void ShowUsbScanDeviceListPermissionDialog(Profile* profile,
+                                           const std::string& app_id,
+                                           ArcUsbConfirmCallback callback) {
+  std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
+      GetArcAppInfo(profile, app_id);
+  if (!app_info) {
+    std::move(callback).Run(false);
+    return;
+  }
+
+  base::string16 window_title =
+      l10n_util::GetStringUTF16(IDS_ARC_USB_PERMISSION_TITLE);
+
+  base::string16 heading_text = l10n_util::GetStringFUTF16(
+      IDS_ARC_USB_SCAN_DEVICE_LIST_PERMISSION_HEADING,
+      base::UTF8ToUTF16(app_info->name));
+
+  base::string16 confirm_button_text = l10n_util::GetStringUTF16(IDS_OK);
+
+  base::string16 cancel_button_text = l10n_util::GetStringUTF16(IDS_CANCEL);
+
+  new ArcAppDialogView(profile, nullptr /*controller*/, app_id, window_title,
+                       heading_text, base::string16() /*subheading_text*/,
+                       confirm_button_text, cancel_button_text,
+                       std::move(callback));
+}
+
+void ShowUsbAccessPermissionDialog(Profile* profile,
+                                   const std::string& app_id,
+                                   const base::string16& device_name,
+                                   ArcUsbConfirmCallback callback) {
+  std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
+      GetArcAppInfo(profile, app_id);
+  if (!app_info) {
+    std::move(callback).Run(false);
+    return;
+  }
+
+  base::string16 window_title =
+      l10n_util::GetStringUTF16(IDS_ARC_USB_PERMISSION_TITLE);
+
+  base::string16 heading_text = l10n_util::GetStringFUTF16(
+      IDS_ARC_USB_ACCESS_PERMISSION_HEADING, base::UTF8ToUTF16(app_info->name));
+
+  base::string16 subheading_text = device_name;
+
+  base::string16 confirm_button_text = l10n_util::GetStringUTF16(IDS_OK);
+
+  base::string16 cancel_button_text = l10n_util::GetStringUTF16(IDS_CANCEL);
+
+  new ArcAppDialogView(profile, nullptr /*controller*/, app_id, window_title,
+                       heading_text, subheading_text, confirm_button_text,
+                       cancel_button_text, std::move(callback));
 }
 
 bool IsArcAppDialogViewAliveForTest() {
diff --git a/chrome/browser/ui/views/arc_app_dialog_view_browsertest.cc b/chrome/browser/ui/views/arc_app_dialog_view_browsertest.cc
index 5df025a9..9ac732f 100644
--- a/chrome/browser/ui/views/arc_app_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/arc_app_dialog_view_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -15,6 +16,7 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs_factory.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
+#include "chrome/browser/ui/app_list/arc/arc_usb_host_permission_manager.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -30,8 +32,7 @@
  public:
   ArcAppUninstallDialogViewBrowserTest() {}
 
-  // InProcessBrowserTest:
-  ~ArcAppUninstallDialogViewBrowserTest() override {}
+  ~ArcAppUninstallDialogViewBrowserTest() override = default;
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     arc::SetArcAvailableCommandLineForTesting(command_line);
@@ -65,20 +66,20 @@
 
     // In this setup, we have one app and one shortcut which share one package.
     mojom::AppInfo app;
-    app.name = base::StringPrintf("Fake App %d", 0);
-    app.package_name = base::StringPrintf("fake.package.%d", 0);
-    app.activity = base::StringPrintf("fake.app.%d.activity", 0);
+    app.name = "Fake App 0";
+    app.package_name = "fake.package.0";
+    app.activity = "fake.app.0.activity";
     app.sticky = false;
     app_instance_->SendRefreshAppList(std::vector<mojom::AppInfo>(1, app));
 
     mojom::ShortcutInfo shortcut;
-    shortcut.name = base::StringPrintf("Fake Shortcut %d", 0);
-    shortcut.package_name = base::StringPrintf("fake.package.%d", 0);
-    shortcut.intent_uri = base::StringPrintf("Fake Shortcut uri %d", 0);
+    shortcut.name = "Fake Shortcut 0";
+    shortcut.package_name = "fake.package.0";
+    shortcut.intent_uri = "Fake Shortcut uri 0";
     app_instance_->SendInstallShortcut(shortcut);
 
     mojom::ArcPackageInfo package;
-    package.package_name = base::StringPrintf("fake.package.%d", 0);
+    package.package_name = "fake.package.0";
     package.package_version = 0;
     package.last_backup_android_id = 0;
     package.last_backup_time = 0;
@@ -101,6 +102,8 @@
 
   FakeAppInstance* instance() { return app_instance_.get(); }
 
+  Profile* profile() { return profile_; }
+
  private:
   ArcAppListPrefs* arc_app_list_pref_ = nullptr;
 
@@ -111,6 +114,297 @@
   DISALLOW_COPY_AND_ASSIGN(ArcAppUninstallDialogViewBrowserTest);
 };
 
+class ArcAppPermissionDialogViewBrowserTest
+    : public ArcAppUninstallDialogViewBrowserTest {
+ public:
+  ArcAppPermissionDialogViewBrowserTest() : weak_ptr_factory_(this) {}
+  // InProcessBrowserTest:
+  ~ArcAppPermissionDialogViewBrowserTest() override = default;
+
+  void InstallExtraPackage(int id) {
+    mojom::AppInfo app;
+    app.name = base::StringPrintf("Fake App %d", id);
+    app.package_name = base::StringPrintf("fake.package.%d", id);
+    app.activity = base::StringPrintf("fake.app.%d.activity", id);
+    app.sticky = false;
+    instance()->SendAppAdded(app);
+
+    mojom::ArcPackageInfo package;
+    package.package_name = base::StringPrintf("fake.package.%d", id);
+    package.package_version = id;
+    package.last_backup_android_id = id;
+    package.last_backup_time = 0;
+    package.sync = false;
+    instance()->SendPackageAdded(package);
+  }
+
+  void set_accepted(bool accepted) { accepted_ = accepted; }
+
+  bool accepted() const { return accepted_; }
+
+  base::WeakPtr<ArcAppPermissionDialogViewBrowserTest> weak_ptr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+  void RequestScanDeviceListPermission(
+      ArcUsbHostPermissionManager* arc_usb_permission_manager,
+      const std::string& package_name) {
+    arc_usb_permission_manager->RequestUsbScanDeviceListPermission(
+        package_name,
+        base::BindOnce(&ArcAppPermissionDialogViewBrowserTest::set_accepted,
+                       weak_ptr()));
+  }
+
+  void RequestAccessPermission(
+      ArcUsbHostPermissionManager* arc_usb_permission_manager,
+      const std::string& package_name) {
+    arc_usb_permission_manager->RequestUsbAccessPermission(
+        package_name, guid_, serial_number_, manufacturer_string_,
+        product_string_, vendor_id_, product_id_,
+        base::BindOnce(&ArcAppPermissionDialogViewBrowserTest::set_accepted,
+                       weak_ptr()));
+  }
+
+  const std::string& guid() const { return guid_; }
+  const base::string16& serial_number() const { return serial_number_; }
+  uint16_t vendor_id() const { return vendor_id_; }
+  uint16_t product_id() const { return product_id_; }
+
+ private:
+  // boolean used to verify dialog result if |set_accepted| is passed as
+  // callback. Used in USB basic permission flow test.
+  bool accepted_ = false;
+
+  // USB flow test related.
+  const std::string guid_ = "TestGuidXXXXXX";
+  const base::string16 serial_number_ = base::UTF8ToUTF16("TestSerialNumber");
+  const base::string16 manufacturer_string_ = base::UTF8ToUTF16("Factory");
+  const base::string16 product_string_ = base::UTF8ToUTF16("Product");
+  uint16_t vendor_id_ = 123;
+  uint16_t product_id_ = 456;
+
+  base::WeakPtrFactory<ArcAppPermissionDialogViewBrowserTest> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcAppPermissionDialogViewBrowserTest);
+};
+
+// Basic flow of requesting scan device list or access permission.
+IN_PROC_BROWSER_TEST_F(ArcAppPermissionDialogViewBrowserTest,
+                       ArcUsbPermissionBasicFlow) {
+  ArcUsbHostPermissionManager* arc_usb_permission_manager =
+      ArcUsbHostPermissionManager::GetForBrowserContext(profile());
+  DCHECK(arc_usb_permission_manager);
+
+  // Invalid package name. Requesut is automatically rejected.
+  const std::string invalid_package = "invalid_package";
+  RequestScanDeviceListPermission(arc_usb_permission_manager, invalid_package);
+  EXPECT_FALSE(IsArcAppDialogViewAliveForTest());
+  EXPECT_FALSE(accepted());
+
+  const std::string package_name = "fake.package.0";
+
+  // Package sends scan devicelist request.
+  RequestScanDeviceListPermission(arc_usb_permission_manager, package_name);
+
+  // Dialog is shown. Call runs with false.
+  EXPECT_TRUE(IsArcAppDialogViewAliveForTest());
+  // Accept the dialog.
+  EXPECT_TRUE(CloseAppDialogViewAndConfirmForTest(true));
+  content::RunAllPendingInMessageLoop();
+  EXPECT_FALSE(IsArcAppDialogViewAliveForTest());
+  // Result will apply when next time the package tries to request the
+  // permisson.
+  EXPECT_FALSE(accepted());
+  // Package tries to request scan device list permission again.
+  RequestScanDeviceListPermission(arc_usb_permission_manager, package_name);
+  EXPECT_FALSE(IsArcAppDialogViewAliveForTest());
+  EXPECT_TRUE(accepted());
+
+  set_accepted(false);
+
+  // Package sends device access request.
+  RequestAccessPermission(arc_usb_permission_manager, package_name);
+  // Dialog is shown.
+  EXPECT_TRUE(IsArcAppDialogViewAliveForTest());
+  // Accept the dialog.
+  EXPECT_TRUE(CloseAppDialogViewAndConfirmForTest(true));
+  content::RunAllPendingInMessageLoop();
+  EXPECT_FALSE(IsArcAppDialogViewAliveForTest());
+  // Permission applies.
+  EXPECT_TRUE(accepted());
+  // Package sends same device access request again.
+  RequestAccessPermission(arc_usb_permission_manager, package_name);
+  // Dialog is not shown. Permission still applies.
+  EXPECT_FALSE(IsArcAppDialogViewAliveForTest());
+  EXPECT_TRUE(accepted());
+}
+
+// Multiple requests are sent at same time and are processed in request order.
+// Previously accepted requests will be remembered.
+IN_PROC_BROWSER_TEST_F(ArcAppPermissionDialogViewBrowserTest,
+                       ArcUsbPermissionMultipleRequestFlow) {
+  ArcUsbHostPermissionManager* arc_usb_permission_manager =
+      ArcUsbHostPermissionManager::GetForBrowserContext(profile());
+  DCHECK(arc_usb_permission_manager);
+
+  InstallExtraPackage(1);
+  InstallExtraPackage(2);
+
+  const std::string package0 = "fake.package.0";
+  const std::string package1 = "fake.package.1";
+  const std::string package2 = "fake.package.2";
+
+  // Package0 sends device access request.
+  RequestAccessPermission(arc_usb_permission_manager, package0);
+
+  // Package1 sends device access request.
+  RequestAccessPermission(arc_usb_permission_manager, package1);
+
+  // Package0 sends device access request again.
+  RequestAccessPermission(arc_usb_permission_manager, package0);
+
+  // Package2 sends device access request.
+  RequestAccessPermission(arc_usb_permission_manager, package2);
+
+  // Dialog is shown.
+  EXPECT_TRUE(IsArcAppDialogViewAliveForTest());
+  const auto& pending_requests =
+      arc_usb_permission_manager->GetPendingRequestsForTesting();
+  EXPECT_EQ(3u, pending_requests.size());
+  EXPECT_EQ(package1, pending_requests[0].package_name());
+  EXPECT_EQ(package0, pending_requests[1].package_name());
+  EXPECT_EQ(package2, pending_requests[2].package_name());
+
+  // Accept the dialog.
+  EXPECT_TRUE(CloseAppDialogViewAndConfirmForTest(true));
+  content::RunAllPendingInMessageLoop();
+
+  // Dialog is shown for the next request.
+  EXPECT_TRUE(IsArcAppDialogViewAliveForTest());
+  EXPECT_EQ(2u, pending_requests.size());
+  EXPECT_EQ(package0, pending_requests[0].package_name());
+  EXPECT_EQ(package2, pending_requests[1].package_name());
+
+  // Accept the dialog.
+  EXPECT_TRUE(CloseAppDialogViewAndConfirmForTest(true));
+  content::RunAllPendingInMessageLoop();
+
+  // The 3rd request is the same to the first request so it's automatically
+  // confirmed. Dialog is shown for the final request.
+  EXPECT_TRUE(IsArcAppDialogViewAliveForTest());
+  EXPECT_EQ(0u, pending_requests.size());
+
+  // Reject the dialog.
+  EXPECT_TRUE(CloseAppDialogViewAndConfirmForTest(false));
+  content::RunAllPendingInMessageLoop();
+
+  // All requests are handled. No dialog is shown.
+  EXPECT_FALSE(IsArcAppDialogViewAliveForTest());
+
+  // Checks permissions.
+  EXPECT_TRUE(arc_usb_permission_manager->HasUsbAccessPermission(
+      package0, guid(), serial_number(), vendor_id(), product_id()));
+  EXPECT_TRUE(arc_usb_permission_manager->HasUsbAccessPermission(
+      package1, guid(), serial_number(), vendor_id(), product_id()));
+  EXPECT_FALSE(arc_usb_permission_manager->HasUsbAccessPermission(
+      package2, guid(), serial_number(), vendor_id(), product_id()));
+}
+
+// Package is removed when permission request is queued.
+IN_PROC_BROWSER_TEST_F(ArcAppPermissionDialogViewBrowserTest,
+                       ArcUsbPermissionPackageUninstallFlow) {
+  ArcUsbHostPermissionManager* arc_usb_permission_manager =
+      ArcUsbHostPermissionManager::GetForBrowserContext(profile());
+  DCHECK(arc_usb_permission_manager);
+
+  InstallExtraPackage(1);
+  InstallExtraPackage(2);
+
+  const std::string package0 = "fake.package.0";
+  const std::string package1 = "fake.package.1";
+  const std::string package2 = "fake.package.2";
+
+  // Package0 sends device access request.
+  RequestAccessPermission(arc_usb_permission_manager, package0);
+
+  // Package1 sends device access request.
+  RequestAccessPermission(arc_usb_permission_manager, package1);
+
+  // Package2 sends device access request.
+  RequestAccessPermission(arc_usb_permission_manager, package2);
+
+  // Dialog is shown.
+  EXPECT_TRUE(IsArcAppDialogViewAliveForTest());
+  const auto& pending_requests =
+      arc_usb_permission_manager->GetPendingRequestsForTesting();
+  EXPECT_EQ(2u, pending_requests.size());
+  EXPECT_EQ(package1, pending_requests[0].package_name());
+  EXPECT_EQ(package2, pending_requests[1].package_name());
+
+  // Uninstall package0 and package2.
+  UninstallPackage(package0);
+  UninstallPackage(package2);
+  EXPECT_EQ(1u, pending_requests.size());
+  EXPECT_EQ(package1, pending_requests[0].package_name());
+
+  // Accept the dialog. But callback is ignored as package0 is uninstalled.
+  EXPECT_TRUE(CloseAppDialogViewAndConfirmForTest(true));
+  content::RunAllPendingInMessageLoop();
+
+  // Permision dialog for next request is shown.
+  EXPECT_TRUE(IsArcAppDialogViewAliveForTest());
+  EXPECT_EQ(0u, pending_requests.size());
+
+  EXPECT_TRUE(CloseAppDialogViewAndConfirmForTest(true));
+  content::RunAllPendingInMessageLoop();
+
+  EXPECT_FALSE(arc_usb_permission_manager->HasUsbAccessPermission(
+      package0, guid(), serial_number(), vendor_id(), product_id()));
+  EXPECT_TRUE(arc_usb_permission_manager->HasUsbAccessPermission(
+      package1, guid(), serial_number(), vendor_id(), product_id()));
+  EXPECT_FALSE(arc_usb_permission_manager->HasUsbAccessPermission(
+      package2, guid(), serial_number(), vendor_id(), product_id()));
+}
+
+// Device is removed when permission request is queued.
+IN_PROC_BROWSER_TEST_F(ArcAppPermissionDialogViewBrowserTest,
+                       ArcUsbPermissionDeviceRemoveFlow) {
+  ArcUsbHostPermissionManager* arc_usb_permission_manager =
+      ArcUsbHostPermissionManager::GetForBrowserContext(profile());
+  DCHECK(arc_usb_permission_manager);
+
+  InstallExtraPackage(1);
+
+  const std::string package0 = "fake.package.0";
+  const std::string package1 = "fake.package.1";
+
+  // Package0 sends device access request.
+  RequestAccessPermission(arc_usb_permission_manager, package0);
+
+  // Package1 sends device access request.
+  RequestAccessPermission(arc_usb_permission_manager, package1);
+
+  // Dialog is shown.
+  EXPECT_TRUE(IsArcAppDialogViewAliveForTest());
+  const auto& pending_requests =
+      arc_usb_permission_manager->GetPendingRequestsForTesting();
+  EXPECT_EQ(1u, pending_requests.size());
+  EXPECT_EQ(package1, pending_requests[0].package_name());
+
+  // Device is removed.
+  arc_usb_permission_manager->DeviceRemoved(guid());
+  EXPECT_EQ(0u, pending_requests.size());
+
+  // Accept the dialog. But callback is ignored as device is removed.
+  EXPECT_TRUE(CloseAppDialogViewAndConfirmForTest(true));
+  content::RunAllPendingInMessageLoop();
+
+  EXPECT_FALSE(arc_usb_permission_manager->HasUsbAccessPermission(
+      package0, guid(), serial_number(), vendor_id(), product_id()));
+  EXPECT_FALSE(arc_usb_permission_manager->HasUsbAccessPermission(
+      package1, guid(), serial_number(), vendor_id(), product_id()));
+}
+
 // User confirms/cancels ARC app uninstall. Note that the shortcut is removed
 // when the app and the package are uninstalled since the shortcut and the app
 // share same package.
@@ -118,8 +412,8 @@
                        UserConfirmsUninstall) {
   std::vector<std::string> app_ids = arc_app_list_pref()->GetAppIds();
   EXPECT_EQ(app_ids.size(), 2u);
-  std::string package_name = base::StringPrintf("fake.package.%d", 0);
-  std::string app_activity = base::StringPrintf("fake.app.%d.activity", 0);
+  std::string package_name = "fake.package.0";
+  std::string app_activity = "fake.app.0.activity";
   std::string app_id =
       arc_app_list_pref()->GetAppId(package_name, app_activity);
 
@@ -175,8 +469,8 @@
                        UserConfirmsUninstallShortcut) {
   std::vector<std::string> app_ids = arc_app_list_pref()->GetAppIds();
   EXPECT_EQ(app_ids.size(), 2u);
-  std::string package_name = base::StringPrintf("fake.package.%d", 0);
-  std::string intent_uri = base::StringPrintf("Fake Shortcut uri %d", 0);
+  std::string package_name = "fake.package.0";
+  std::string intent_uri = "Fake Shortcut uri 0";
   std::string app_id = arc_app_list_pref()->GetAppId(package_name, intent_uri);
 
   AppListService* service = AppListService::Get();
diff --git a/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.cc b/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.cc
index 5cc0cef..29c1779 100644
--- a/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.cc
@@ -10,21 +10,29 @@
 
 namespace policy_indicator {
 
+struct LocalizedString {
+  const char* name;
+  int id;
+};
+
 void AddLocalizedStrings(content::WebUIDataSource* html_source) {
-  html_source->AddLocalizedString("controlledSettingPolicy",
-                                  IDS_CONTROLLED_SETTING_POLICY);
-  html_source->AddLocalizedString("controlledSettingRecommendedMatches",
-                                  IDS_CONTROLLED_SETTING_RECOMMENDED);
-  html_source->AddLocalizedString("controlledSettingRecommendedDiffers",
-                                  IDS_CONTROLLED_SETTING_HAS_RECOMMENDATION);
-  html_source->AddLocalizedString("controlledSettingExtension",
-                                  IDS_CONTROLLED_SETTING_EXTENSION);
+  LocalizedString localized_strings[] = {
+    {"controlledSettingPolicy", IDS_CONTROLLED_SETTING_POLICY},
+    {"controlledSettingRecommendedMatches", IDS_CONTROLLED_SETTING_RECOMMENDED},
+    {"controlledSettingRecommendedDiffers",
+     IDS_CONTROLLED_SETTING_HAS_RECOMMENDATION},
+    {"controlledSettingExtension", IDS_CONTROLLED_SETTING_EXTENSION},
+    {"controlledSettingExtensionWithoutName",
+     IDS_CONTROLLED_SETTING_EXTENSION_WITHOUT_NAME},
 #if defined(OS_CHROMEOS)
-  html_source->AddLocalizedString("controlledSettingShared",
-                                  IDS_CONTROLLED_SETTING_SHARED);
-  html_source->AddLocalizedString("controlledSettingOwner",
-                                  IDS_CONTROLLED_SETTING_OWNER);
+    {"controlledSettingShared", IDS_CONTROLLED_SETTING_SHARED},
+    {"controlledSettingOwner", IDS_CONTROLLED_SETTING_OWNER},
 #endif
+  };
+
+  for (size_t i = 0; i < arraysize(localized_strings); i++)
+    html_source->AddLocalizedString(localized_strings[i].name,
+                                    localized_strings[i].id);
 }
 
 }  // namespace policy_indicator
diff --git a/chrome/browser/usb/usb_blocklist.cc b/chrome/browser/usb/usb_blocklist.cc
index 22bc6ca7..954c87c 100644
--- a/chrome/browser/usb/usb_blocklist.cc
+++ b/chrome/browser/usb/usb_blocklist.cc
@@ -57,18 +57,50 @@
 
 // This list must be sorted according to CompareEntry.
 const UsbBlocklist::Entry kStaticEntries[] = {
+    {0x096e, 0x0850, kMaxVersion},  // KEY-ID
+    {0x096e, 0x0852, kMaxVersion},  // Feitian
+    {0x096e, 0x0853, kMaxVersion},  // Feitian
+    {0x096e, 0x0854, kMaxVersion},  // Feitian
+    {0x096e, 0x0856, kMaxVersion},  // Feitian
+    {0x096e, 0x0858, kMaxVersion},  // Feitian USB+NFC
+    {0x096e, 0x085a, kMaxVersion},  // Feitian
+    {0x096e, 0x085b, kMaxVersion},  // Feitian
+    {0x096e, 0x0880, kMaxVersion},  // HyperFIDO
+
     // Yubikey devices. https://crbug.com/818807
-    {0x1050, 0x0010, kMaxVersion}, {0x1050, 0x0018, kMaxVersion},
-    {0x1050, 0x0030, kMaxVersion}, {0x1050, 0x0110, kMaxVersion},
-    {0x1050, 0x0111, kMaxVersion}, {0x1050, 0x0112, kMaxVersion},
-    {0x1050, 0x0113, kMaxVersion}, {0x1050, 0x0114, kMaxVersion},
-    {0x1050, 0x0115, kMaxVersion}, {0x1050, 0x0116, kMaxVersion},
-    {0x1050, 0x0120, kMaxVersion}, {0x1050, 0x0200, kMaxVersion},
-    {0x1050, 0x0211, kMaxVersion}, {0x1050, 0x0401, kMaxVersion},
-    {0x1050, 0x0402, kMaxVersion}, {0x1050, 0x0403, kMaxVersion},
-    {0x1050, 0x0404, kMaxVersion}, {0x1050, 0x0405, kMaxVersion},
-    {0x1050, 0x0406, kMaxVersion}, {0x1050, 0x0407, kMaxVersion},
+    {0x1050, 0x0010, kMaxVersion},
+    {0x1050, 0x0018, kMaxVersion},
+    {0x1050, 0x0030, kMaxVersion},
+    {0x1050, 0x0110, kMaxVersion},
+    {0x1050, 0x0111, kMaxVersion},
+    {0x1050, 0x0112, kMaxVersion},
+    {0x1050, 0x0113, kMaxVersion},
+    {0x1050, 0x0114, kMaxVersion},
+    {0x1050, 0x0115, kMaxVersion},
+    {0x1050, 0x0116, kMaxVersion},
+    {0x1050, 0x0120, kMaxVersion},
+    {0x1050, 0x0200, kMaxVersion},
+    {0x1050, 0x0211, kMaxVersion},
+    {0x1050, 0x0401, kMaxVersion},
+    {0x1050, 0x0402, kMaxVersion},
+    {0x1050, 0x0403, kMaxVersion},
+    {0x1050, 0x0404, kMaxVersion},
+    {0x1050, 0x0405, kMaxVersion},
+    {0x1050, 0x0406, kMaxVersion},
+    {0x1050, 0x0407, kMaxVersion},
     {0x1050, 0x0410, kMaxVersion},
+
+    {0x10c4, 0x8acf, kMaxVersion},  // U2F Zero
+    {0x18d1, 0x5026, kMaxVersion},  // Titan
+    {0x1a44, 0x00bb, kMaxVersion},  // VASCO
+    {0x1e0d, 0xf1ae, kMaxVersion},  // Keydo AES
+    {0x1e0d, 0xf1d0, kMaxVersion},  // Neowave Keydo
+    {0x1ea8, 0xf025, kMaxVersion},  // Thetis
+    {0x20a0, 0x4287, kMaxVersion},  // Nitrokey
+    {0x24dc, 0x0101, kMaxVersion},  // JaCarta
+    {0x2581, 0xf1d0, kMaxVersion},  // Happlink
+    {0x2abe, 0x1002, kMaxVersion},  // Bluink
+    {0x2ccf, 0x0880, kMaxVersion},  // Feitian USB, HyperFIDO
 };
 
 }  // namespace
diff --git a/chrome/common/media_router/BUILD.gn b/chrome/common/media_router/BUILD.gn
index 6dae758..c9937c1 100644
--- a/chrome/common/media_router/BUILD.gn
+++ b/chrome/common/media_router/BUILD.gn
@@ -22,6 +22,7 @@
     "issue.h",
     "media_route.cc",
     "media_route.h",
+    "media_route_provider_helper.cc",
     "media_route_provider_helper.h",
     "media_sink.cc",
     "media_sink.h",
diff --git a/chrome/common/media_router/media_route_provider_helper.cc b/chrome/common/media_router/media_route_provider_helper.cc
new file mode 100644
index 0000000..fb1762d
--- /dev/null
+++ b/chrome/common/media_router/media_route_provider_helper.cc
@@ -0,0 +1,27 @@
+// 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/common/media_router/media_route_provider_helper.h"
+
+#include "base/logging.h"
+
+namespace media_router {
+
+const char* ProviderIdToString(MediaRouteProviderId provider_id) {
+  switch (provider_id) {
+    case EXTENSION:
+      return "EXTENSION";
+    case WIRED_DISPLAY:
+      return "WIRED_DISPLAY";
+    case CAST:
+      return "CAST";
+    case UNKNOWN:
+      return "UNKNOWN";
+  }
+
+  NOTREACHED() << "Unknown provider_id " << static_cast<int>(provider_id);
+  return "Unknown provider_id";
+}
+
+}  // namespace media_router
diff --git a/chrome/common/media_router/media_route_provider_helper.h b/chrome/common/media_router/media_route_provider_helper.h
index 04c89916..4fb55d2 100644
--- a/chrome/common/media_router/media_route_provider_helper.h
+++ b/chrome/common/media_router/media_route_provider_helper.h
@@ -5,17 +5,22 @@
 #ifndef CHROME_COMMON_MEDIA_ROUTER_MEDIA_ROUTE_PROVIDER_HELPER_H_
 #define CHROME_COMMON_MEDIA_ROUTER_MEDIA_ROUTE_PROVIDER_HELPER_H_
 
+#include <string>
+
 namespace media_router {
 
 // Each MediaRouteProvider is associated with a unique ID. This enum must be
-// kept in sync with mojom::MediaRouteProvider::Id, except for |UNKNWON|, which
+// kept in sync with mojom::MediaRouteProvider::Id, except for |UNKNOWN|, which
 // is not present in the Mojo enum.
 enum MediaRouteProviderId {
   EXTENSION,
   WIRED_DISPLAY,
+  CAST,
   UNKNOWN  // New values must be added above this value.
 };
 
+const char* ProviderIdToString(MediaRouteProviderId provider_id);
+
 }  // namespace media_router
 
 #endif  // CHROME_COMMON_MEDIA_ROUTER_MEDIA_ROUTE_PROVIDER_HELPER_H_
diff --git a/chrome/common/media_router/mojo/media_router.mojom b/chrome/common/media_router/mojo/media_router.mojom
index ccff31e..9bcb2db2 100644
--- a/chrome/common/media_router/mojo/media_router.mojom
+++ b/chrome/common/media_router/mojo/media_router.mojom
@@ -92,7 +92,9 @@
   kMirroring
 };
 
-// Should be kept in sync with media_route.h.
+// MediaRoute objects contain the status and metadata of a routing
+// operation.
+// This struct should be kept in sync with media_route.h.
 struct MediaRoute {
   // The ID of this media route, e.g. "r_PR1O_blkC9dsKp-tb1ti8qurOo".
   string media_route_id;
@@ -212,7 +214,7 @@
 };
 
 // Used to pass feature configuration data from the Browser to Media Route
-// Provider.
+// Provider. This object is used by the extension MRP only.
 struct MediaRouteProviderConfig {
   // If the MRP should enable DIAL discovery.
   bool enable_dial_discovery;
@@ -222,20 +224,30 @@
 
   // If the MRP should enable DIAL sink query.
   bool enable_dial_sink_query;
+
+  // If the MRP should enable Cast sink query.
+  bool enable_cast_sink_query;
 };
 
 // Modeled after the MediaRouter interface defined in
 // chrome/browser/media/router/media_router.h
 //
 // MediaRouteProvider is responsible for discovering MediaSinks, and managing
-// MediaRoutes associated with them. It notifies its observers (i.e.
-// MediaRouter) when there are changes related to sinks or routes.
+// MediaRoutes associated with them.
+// A MediaRouteProvider is associated with a MediaRouter. MediaRouter issues
+// commands to the MediaRouteProvider, such as observing MediaSinks or creating
+// a MediaRoute. In return, the MediaRouteProvider notifies the MediaRouter when
+// there are changes related to sinks or routes.
+// A MediaRouteProvider may live in the Media Router component extension, or
+// the browser (e.g. DIAL and Cast MediaRouteProviders will soon move to the
+// browser).
 interface MediaRouteProvider {
   // Each MediaRouteProvider is associated with a unique ID. This enum must be
   // kept in sync with MediaRouteProviderId in C++.
   enum Id {
     EXTENSION,
-    WIRED_DISPLAY
+    WIRED_DISPLAY,
+    CAST
   };
 
   // Creates a media route from |media_source| to the sink given by |sink_id|.
@@ -438,7 +450,8 @@
 };
 
 // Interface for a service which observes MediaRouteProviders for state changes
-// across media sources, sinks, and issues.
+// across media sources, sinks, and issues. The MediaRouter lives in the browser
+// process.
 interface MediaRouter {
 
   // Represents overall media sink availability states.
@@ -523,4 +536,7 @@
   // receive the updates/messages from MediaRemoter.
   OnMediaRemoterCreated(int32 tab_id, media.mojom.MirrorServiceRemoter remoter,
       media.mojom.MirrorServiceRemotingSource& remoting_source);
+
+  // Returns current status of media sink service in JSON format.
+  GetMediaSinkServiceStatus() => (string status);
 };
diff --git a/chrome/common/media_router/mojo/media_router_struct_traits.h b/chrome/common/media_router/mojo/media_router_struct_traits.h
index bde46e71..509060f9 100644
--- a/chrome/common/media_router/mojo/media_router_struct_traits.h
+++ b/chrome/common/media_router/mojo/media_router_struct_traits.h
@@ -626,6 +626,8 @@
         return media_router::mojom::MediaRouteProvider::Id::EXTENSION;
       case media_router::MediaRouteProviderId::WIRED_DISPLAY:
         return media_router::mojom::MediaRouteProvider::Id::WIRED_DISPLAY;
+      case media_router::MediaRouteProviderId::CAST:
+        return media_router::mojom::MediaRouteProvider::Id::CAST;
       case media_router::MediaRouteProviderId::UNKNOWN:
         break;
     }
@@ -643,6 +645,9 @@
       case media_router::mojom::MediaRouteProvider::Id::WIRED_DISPLAY:
         *provider_id = media_router::MediaRouteProviderId::WIRED_DISPLAY;
         return true;
+      case media_router::mojom::MediaRouteProvider::Id::CAST:
+        *provider_id = media_router::MediaRouteProviderId::CAST;
+        return true;
     }
     return false;
   }
diff --git a/chrome/common/media_router/providers/cast/cast_media_source.cc b/chrome/common/media_router/providers/cast/cast_media_source.cc
index aadd779..aeb49d5 100644
--- a/chrome/common/media_router/providers/cast/cast_media_source.cc
+++ b/chrome/common/media_router/providers/cast/cast_media_source.cc
@@ -234,4 +234,10 @@
   return false;
 }
 
+bool CastMediaSource::ContainsAnyAppFrom(
+    const std::vector<std::string>& app_ids) const {
+  return std::any_of(
+      app_ids.begin(), app_ids.end(),
+      [this](const std::string& app_id) { return ContainsApp(app_id); });
+}
 }  // namespace media_router
diff --git a/chrome/common/media_router/providers/cast/cast_media_source.h b/chrome/common/media_router/providers/cast/cast_media_source.h
index 6cafad16..5f52338 100644
--- a/chrome/common/media_router/providers/cast/cast_media_source.h
+++ b/chrome/common/media_router/providers/cast/cast_media_source.h
@@ -58,6 +58,7 @@
 
   // Returns |true| if |app_infos| contain |app_id|.
   bool ContainsApp(const std::string& app_id) const;
+  bool ContainsAnyAppFrom(const std::vector<std::string>& app_ids) const;
 
   const MediaSource::Id& source_id() const { return source_id_; }
   const std::vector<CastAppInfo>& app_infos() const { return app_infos_; }
diff --git a/chrome/renderer/resources/extensions/media_router_bindings.js b/chrome/renderer/resources/extensions/media_router_bindings.js
index 052f0a9..b270be5 100644
--- a/chrome/renderer/resources/extensions/media_router_bindings.js
+++ b/chrome/renderer/resources/extensions/media_router_bindings.js
@@ -785,6 +785,7 @@
                 'enable_dial_discovery': response.config.enableDialDiscovery,
                 'enable_cast_discovery': response.config.enableCastDiscovery,
                 'enable_dial_sink_query': response.config.enableDialSinkQuery,
+                'enable_cast_sink_query': response.config.enableCastSinkQuery,
               }
             };
           });
@@ -975,6 +976,14 @@
 }
 
 /**
+ * Returns current status of media sink service in JSON format.
+ * @return {!Promise<!{status: string}>}
+ */
+MediaRouter.prototype.getMediaSinkServiceStatus = function() {
+  return this.service_.getMediaSinkServiceStatus();
+}
+
+/**
  * Object containing callbacks set by the provider manager.
  *
  * @constructor
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 20af8e3b..deeb5fe 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3056,6 +3056,9 @@
       "../browser/media/router/mojo/media_router_desktop_unittest.cc",
       "../browser/media/router/mojo/media_router_mojo_impl_unittest.cc",
       "../browser/media/router/mojo/media_router_mojo_metrics_unittest.cc",
+      "../browser/media/router/mojo/media_sink_service_status_unittest.cc",
+      "../browser/media/router/providers/cast/cast_app_availability_tracker_unittest.cc",
+      "../browser/media/router/providers/cast/cast_app_discovery_service_unittest.cc",
       "../browser/media/router/providers/cast/dual_media_sink_service_unittest.cc",
       "../browser/media/router/providers/extension/extension_media_route_provider_proxy_unittest.cc",
       "../browser/media/router/providers/wired_display/wired_display_media_route_provider_unittest.cc",
diff --git a/chrome/test/chromedriver/chrome/version.cc b/chrome/test/chromedriver/chrome/version.cc
index 96bbe33d..4b71be1 100644
--- a/chrome/test/chromedriver/chrome/version.cc
+++ b/chrome/test/chromedriver/chrome/version.cc
@@ -9,7 +9,7 @@
 namespace {
 
 // This variable must be able to be found and parsed by the upload script.
-const int kMinimumSupportedChromeVersion[] = {63, 0, 3239, 0};
+const int kMinimumSupportedChromeVersion[] = {64, 0, 3282, 0};
 
 }  // namespace
 
diff --git a/chrome/test/chromedriver/test/run_all_tests.py b/chrome/test/chromedriver/test/run_all_tests.py
index 1ec83de2..93654b5 100755
--- a/chrome/test/chromedriver/test/run_all_tests.py
+++ b/chrome/test/chromedriver/test/run_all_tests.py
@@ -202,21 +202,21 @@
 
     # Linux64 build numbers
     elif util.IsLinux():
+      versions['66'] = '540276'
       versions['65'] = '530372'
       versions['64'] = '520842'
-      versions['63'] = '508578'
 
     # Mac build numbers
     elif util.IsMac():
+      versions['66'] = '540271'
       versions['65'] = '530368'
       versions['64'] = '520840'
-      versions['63'] = '508578'
 
     # Windows build numbers
     elif util.IsWindows():
+      versions['66'] = '540270'
       versions['65'] = '530366'
       versions['64'] = '520840'
-      versions['63'] = '508578'
 
     code = 0
     for version, revision in versions.iteritems():
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 9cf09891..10e0baaa 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -82,6 +82,8 @@
 _VERSION_SPECIFIC_FILTER = {}
 _VERSION_SPECIFIC_FILTER['HEAD'] = []
 
+_VERSION_SPECIFIC_FILTER['66'] = []
+
 _VERSION_SPECIFIC_FILTER['65'] = [
     # https://bugs.chromium.org/p/chromium/issues/detail?id=803678
     'ChromeDriverTest.testGoBackAndGoForward',
@@ -99,20 +101,6 @@
     'ChromeDriverSiteIsolation.testCanClickOOPIF',
 ]
 
-_VERSION_SPECIFIC_FILTER['63'] = [
-    # These tests are implemented to run on the latest versions of Chrome > 64
-    'HeadlessInvalidCertificateTest.*',
-    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2025
-    'ChromeDriverTest.testDoesntHangOnFragmentNavigation',
-    'ChromeDriverPageLoadTimeoutTest.testHistoryNavigationWithPageLoadTimeout',
-    'ChromeDriverPageLoadTimeoutTest.testRefreshWithPageLoadTimeout',
-    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1819
-    'ChromeExtensionsCapabilityTest.testIFrameWithExtensionsSource',
-    # https://bugs.chromium.org/p/chromium/issues/detail?id=746266
-    'ChromeDriverSiteIsolation.testCanClickOOPIF',
-]
-
-
 _OS_SPECIFIC_FILTER = {}
 _OS_SPECIFIC_FILTER['win'] = [
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=299
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations
index f863f3a5..387405c 100644
--- a/chrome/test/chromedriver/test/test_expectations
+++ b/chrome/test/chromedriver/test/test_expectations
@@ -118,16 +118,6 @@
     ]
 )
 
-_REVISION_NEGATIVE_FILTER['63'] = (
-    _REVISION_NEGATIVE_FILTER['HEAD'] + [
-        # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2025
-        'MiscTest.testStimulatesStrangeOnloadInteractionInFirefox',
-        'PageLoadingTest.testShouldNotHangIfDocumentOpenCallIsNeverFollowedByDocumentCloseCall',
-        # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2025
-        'PageLoadingTest.testShouldBeAbleToGetAFragmentOnTheCurrentPage',
-    ]
-)
-
 _OS_NEGATIVE_FILTER = {}
 _OS_NEGATIVE_FILTER['win'] = [
     # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=373
diff --git a/chrome/test/data/extensions/api_test/debugger/background.js b/chrome/test/data/extensions/api_test/debugger/background.js
index 6855dae6..0725cf4 100644
--- a/chrome/test/data/extensions/api_test/debugger/background.js
+++ b/chrome/test/data/extensions/api_test/debugger/background.js
@@ -134,7 +134,10 @@
           chrome.test.assertTrue(responded);
           chrome.test.assertEq(debuggee.tabId, from.tabId);
           chrome.test.assertEq("target_closed", reason);
-          chrome.test.succeed();
+          chrome.tabs.remove(tab.id, function() {
+            chrome.test.assertNoLastError();
+            chrome.test.succeed();
+          });
         }
 
         chrome.test.assertNoLastError();
@@ -159,7 +162,10 @@
         function onDetach() {
           chrome.debugger.onDetach.removeListener(onDetach);
           chrome.test.assertTrue(responded);
-          chrome.test.succeed();
+          chrome.tabs.remove(tab.id, function() {
+            chrome.test.assertNoLastError();
+            chrome.test.succeed();
+          });
         }
 
         chrome.test.assertNoLastError();
diff --git a/chrome/test/data/webui/cr_elements/cr_policy_indicator_behavior_tests.js b/chrome/test/data/webui/cr_elements/cr_policy_indicator_behavior_tests.js
index c887898b..cf23587c 100644
--- a/chrome/test/data/webui/cr_elements/cr_policy_indicator_behavior_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_policy_indicator_behavior_tests.js
@@ -55,6 +55,15 @@
     assertEquals('extension: Extension name', indicator.indicatorTooltip);
   });
 
+  test('extension indicator without extension name', function() {
+    indicator.indicatorType = CrPolicyIndicatorType.EXTENSION;
+    indicator.indicatorSourceName = '';
+
+    assertTrue(indicator.indicatorVisible);
+    assertEquals('cr:extension', indicator.indicatorIcon);
+    assertEquals('extension', indicator.indicatorTooltip);
+  });
+
   if (cr.isChromeOS) {
     test('primary-user controlled indicator', function() {
       indicator.indicatorType = CrPolicyIndicatorType.PRIMARY_USER;
diff --git a/chrome/test/data/webui/cr_elements/cr_policy_strings.js b/chrome/test/data/webui/cr_elements/cr_policy_strings.js
index 4d0f62d..a3b62793 100644
--- a/chrome/test/data/webui/cr_elements/cr_policy_strings.js
+++ b/chrome/test/data/webui/cr_elements/cr_policy_strings.js
@@ -11,4 +11,5 @@
   controlledSettingShared: 'shared: $1',
   controlledSettingOwner: 'owner: $1',
   controlledSettingExtension: 'extension: $1',
+  controlledSettingExtensionWithoutName: 'extension',
 };
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 780c9f9f..1f1d91f 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -316,8 +316,6 @@
   sources = [
     "test/cast_browser_test.cc",
     "test/cast_browser_test.h",
-    "test/fake_web_contents_observer.cc",
-    "test/fake_web_contents_observer.h",
   ]
 
   public_deps = [
@@ -328,7 +326,6 @@
   deps = [
     "//base",
     "//chromecast/base",
-    "//chromecast/common:interfaces",
     "//content/public/browser",
     "//content/public/common",
     "//testing/gtest",
diff --git a/chromecast/browser/cast_display_configurator.cc b/chromecast/browser/cast_display_configurator.cc
index affe29ed..df84a61 100644
--- a/chromecast/browser/cast_display_configurator.cc
+++ b/chromecast/browser/cast_display_configurator.cc
@@ -81,7 +81,7 @@
   if (delegate_) {
     delegate_->AddObserver(this);
     delegate_->Initialize();
-    OnConfigurationChanged();
+    ForceInitialConfigure();
   } else {
     ConfigureDisplayFromCommandLine();
   }
@@ -95,9 +95,9 @@
 // display::NativeDisplayObserver interface
 void CastDisplayConfigurator::OnConfigurationChanged() {
   DCHECK(delegate_);
-  delegate_->GetDisplays(
-      base::Bind(&CastDisplayConfigurator::OnDisplaysAcquired,
-                 weak_factory_.GetWeakPtr()));
+  delegate_->GetDisplays(base::Bind(
+      &CastDisplayConfigurator::OnDisplaysAcquired, weak_factory_.GetWeakPtr(),
+      false /* force_initial_configure */));
 }
 
 void CastDisplayConfigurator::ConfigureDisplayFromCommandLine() {
@@ -106,7 +106,14 @@
                GetRotationFromCommandLine());
 }
 
+void CastDisplayConfigurator::ForceInitialConfigure() {
+  delegate_->GetDisplays(base::Bind(
+      &CastDisplayConfigurator::OnDisplaysAcquired, weak_factory_.GetWeakPtr(),
+      true /* force_initial_configure */));
+}
+
 void CastDisplayConfigurator::OnDisplaysAcquired(
+    bool force_initial_configure,
     const std::vector<display::DisplaySnapshot*>& displays) {
   DCHECK(delegate_);
   if (displays.empty()) {
@@ -126,6 +133,17 @@
   }
 
   gfx::Point origin;
+  gfx::Size native_size(display->native_mode()->size());
+  if (force_initial_configure) {
+    // For initial configuration, pass the native geometry to gfx::Screen
+    // before calling Configure(), so that this information is available
+    // to chrome during startup. Otherwise we will not have a valid display
+    // during the first queries to display::Screen.
+    UpdateScreen(display->display_id(), gfx::Rect(origin, native_size),
+                 GetDeviceScaleFactor(native_size),
+                 GetRotationFromCommandLine());
+  }
+
   delegate_->Configure(
       *display, display->native_mode(), origin,
       base::BindRepeating(&CastDisplayConfigurator::OnDisplayConfigured,
diff --git a/chromecast/browser/cast_display_configurator.h b/chromecast/browser/cast_display_configurator.h
index 44ae29b7..0e3423e 100644
--- a/chromecast/browser/cast_display_configurator.h
+++ b/chromecast/browser/cast_display_configurator.h
@@ -47,7 +47,9 @@
   void ConfigureDisplayFromCommandLine();
 
  private:
+  void ForceInitialConfigure();
   void OnDisplaysAcquired(
+      bool force_initial_configure,
       const std::vector<display::DisplaySnapshot*>& displays);
   void OnDisplayConfigured(display::DisplaySnapshot* display,
                            const display::DisplayMode* mode,
diff --git a/chromecast/browser/cast_media_blocker_browsertest.cc b/chromecast/browser/cast_media_blocker_browsertest.cc
index 355bc8e..7483ddd 100644
--- a/chromecast/browser/cast_media_blocker_browsertest.cc
+++ b/chromecast/browser/cast_media_blocker_browsertest.cc
@@ -10,7 +10,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "chromecast/browser/cast_media_blocker.h"
 #include "chromecast/browser/test/cast_browser_test.h"
-#include "chromecast/browser/test/fake_web_contents_observer.h"
 #include "chromecast/chromecast_buildflags.h"
 #include "content/public/browser/media_session.h"
 #include "content/public/browser/web_contents.h"
@@ -43,10 +42,7 @@
     GURL gurl = content::GetFileUrlWithQuery(
         media::GetTestDataFilePath("player.html"), query);
 
-    web_contents_ = CreateWebView();
-    web_contents_observer_ =
-        std::make_unique<FakeWebContentsObserver>(web_contents_);
-    NavigateToURL(gurl);
+    web_contents_ = NavigateToURL(gurl);
     WaitForLoadStop(web_contents_);
 
     blocker_ = std::make_unique<CastMediaBlocker>(
@@ -85,7 +81,6 @@
 
  private:
   content::WebContents* web_contents_;
-  std::unique_ptr<FakeWebContentsObserver> web_contents_observer_;
   std::unique_ptr<CastMediaBlocker> blocker_;
 
   DISALLOW_COPY_AND_ASSIGN(CastMediaBlockerBrowserTest);
diff --git a/chromecast/browser/cast_media_blocker_unittest.cc b/chromecast/browser/cast_media_blocker_unittest.cc
index 63dd3f8..dc4eb73d 100644
--- a/chromecast/browser/cast_media_blocker_unittest.cc
+++ b/chromecast/browser/cast_media_blocker_unittest.cc
@@ -36,6 +36,7 @@
   MOCK_CONST_METHOD0(IsActuallyPaused, bool());
   MOCK_METHOD0(StartDucking, void());
   MOCK_METHOD0(StopDucking, void());
+  MOCK_METHOD1(SetDuckingVolumeMultiplier, void(double));
   MOCK_METHOD1(DidReceiveAction, void(blink::mojom::MediaSessionAction));
   MOCK_METHOD1(AddObserver, void(content::MediaSessionObserver*));
   MOCK_METHOD1(RemoveObserver, void(content::MediaSessionObserver*));
diff --git a/chromecast/browser/test/cast_navigation_browsertest.cc b/chromecast/browser/test/cast_navigation_browsertest.cc
index 9c50131f..64b397b7 100644
--- a/chromecast/browser/test/cast_navigation_browsertest.cc
+++ b/chromecast/browser/test/cast_navigation_browsertest.cc
@@ -6,7 +6,6 @@
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chromecast/browser/test/cast_browser_test.h"
-#include "chromecast/browser/test/fake_web_contents_observer.h"
 #include "chromecast/chromecast_buildflags.h"
 #include "content/public/test/browser_test_utils.h"
 #include "media/base/test_data_util.h"
@@ -26,9 +25,8 @@
   CastNavigationBrowserTest() {}
 
   void LoadAboutBlank() {
-    content::WebContents* web_contents = CreateWebView();
-    FakeWebContentsObserver web_contents_observer(web_contents);
-    NavigateToURL(GURL(url::kAboutBlankURL));
+    content::WebContents* web_contents =
+        NavigateToURL(GURL(url::kAboutBlankURL));
     content::TitleWatcher title_watcher(
         web_contents, base::ASCIIToUTF16(url::kAboutBlankURL));
     base::string16 result = title_watcher.WaitAndGetTitle();
@@ -60,9 +58,7 @@
   }
 
   std::string RunTest(const GURL& gurl, const std::string& expected_title) {
-    content::WebContents* web_contents = CreateWebView();
-    FakeWebContentsObserver web_contents_observer(web_contents);
-    NavigateToURL(gurl);
+    content::WebContents* web_contents = NavigateToURL(gurl);
     content::TitleWatcher title_watcher(web_contents,
                                         base::ASCIIToUTF16(expected_title));
     title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16(kEnded));
diff --git a/chromecast/browser/test/fake_web_contents_observer.cc b/chromecast/browser/test/fake_web_contents_observer.cc
deleted file mode 100644
index 5a05fd6..0000000
--- a/chromecast/browser/test/fake_web_contents_observer.cc
+++ /dev/null
@@ -1,42 +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 "chromecast/browser/test/fake_web_contents_observer.h"
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "chromecast/common/application_media_capabilities.mojom.h"
-
-namespace chromecast {
-namespace shell {
-
-FakeWebContentsObserver::FakeWebContentsObserver(
-    content::WebContents* web_contents)
-    : content::WebContentsObserver(web_contents) {
-  registry_.AddInterface<mojom::ApplicationMediaCapabilities>(
-      base::BindRepeating(&ApplicationMediaCapabilities::AddBinding,
-                          base::Unretained(&app_media_capabilities_)));
-}
-
-FakeWebContentsObserver::~FakeWebContentsObserver() = default;
-
-void FakeWebContentsObserver::DidFinishNavigation(
-    content::NavigationHandle* /* navigation_handle */) {}
-
-void FakeWebContentsObserver::DidFirstVisuallyNonEmptyPaint() {}
-
-void FakeWebContentsObserver::RenderViewCreated(
-    content::RenderViewHost* /* render_view_host */) {}
-
-void FakeWebContentsObserver::RenderViewReady() {}
-
-void FakeWebContentsObserver::OnInterfaceRequestFromFrame(
-    content::RenderFrameHost* /* render_frame_host */,
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle* interface_pipe) {
-  registry_.TryBindInterface(interface_name, interface_pipe);
-}
-
-}  // namespace shell
-}  // namespace chromecast
diff --git a/chromecast/browser/test/fake_web_contents_observer.h b/chromecast/browser/test/fake_web_contents_observer.h
deleted file mode 100644
index 6b88ca4..0000000
--- a/chromecast/browser/test/fake_web_contents_observer.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMECAST_BROWSER_TEST_FAKE_WEB_CONTENTS_OBSERVER_H_
-#define CHROMECAST_BROWSER_TEST_FAKE_WEB_CONTENTS_OBSERVER_H_
-
-#include <string>
-
-#include "chromecast/browser/application_media_capabilities.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
-
-namespace content {
-class NavigationHandle;
-class RenderFrameHost;
-class RenderViewHost;
-class WebContents;
-}  // namespace content
-
-namespace chromecast {
-namespace shell {
-
-class FakeWebContentsObserver : public content::WebContentsObserver {
- public:
-  explicit FakeWebContentsObserver(content::WebContents* web_contents);
-  ~FakeWebContentsObserver() override;
-
- private:
-  // content::WebContentsObserver implementation:
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
-  void DidFirstVisuallyNonEmptyPaint() override;
-  void RenderViewCreated(content::RenderViewHost* render_view_host) override;
-  void RenderViewReady() override;
-  void OnInterfaceRequestFromFrame(
-      content::RenderFrameHost* render_frame_host,
-      const std::string& interface_name,
-      mojo::ScopedMessagePipeHandle* interface_pipe) override;
-
-  service_manager::BinderRegistry registry_;
-  ApplicationMediaCapabilities app_media_capabilities_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeWebContentsObserver);
-};
-
-}  // namespace shell
-}  // namespace chromecast
-
-#endif  // CHROMECAST_BROWSER_TEST_FAKE_WEB_CONTENTS_OBSERVER_H_
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn
index 8587942..3776da7c 100644
--- a/chromeos/services/assistant/BUILD.gn
+++ b/chromeos/services/assistant/BUILD.gn
@@ -13,6 +13,8 @@
 source_set("lib") {
   sources = [
     "assistant_manager_service.h",
+    "fake_assistant_manager_service_impl.cc",
+    "fake_assistant_manager_service_impl.h",
     "service.cc",
     "service.h",
   ]
@@ -61,11 +63,6 @@
       "//libassistant/contrib",
       "//libassistant/shared",
     ]
-  } else {
-    sources += [
-      "fake_assistant_manager_service_impl.cc",
-      "fake_assistant_manager_service_impl.h",
-    ]
   }
 }
 
@@ -81,12 +78,15 @@
     "//base",
     "//base/test:test_support",
     "//mojo/public/cpp/bindings:bindings",
+    "//services/identity/public/mojom",
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/cpp:service_test_support",
     "//testing/gmock",
     "//testing/gtest",
   ]
-  sources = []
+  sources = [
+    "service_unittest.cc",
+  ]
 
   if (enable_cros_libassistant) {
     sources += [ "platform/system_provider_impl_unittest.cc" ]
diff --git a/chromeos/services/assistant/service.cc b/chromeos/services/assistant/service.cc
index 2a4992d..c293090 100644
--- a/chromeos/services/assistant/service.cc
+++ b/chromeos/services/assistant/service.cc
@@ -9,6 +9,7 @@
 #include "ash/public/interfaces/constants.mojom.h"
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/timer/timer.h"
 #include "build/buildflag.h"
 #include "chromeos/assistant/buildflags.h"
 #include "chromeos/services/assistant/assistant_manager_service.h"
@@ -35,10 +36,27 @@
 
 }  // namespace
 
-Service::Service() : session_observer_binding_(this), weak_factory_(this) {}
+Service::Service()
+    : session_observer_binding_(this),
+      token_refresh_timer_(std::make_unique<base::OneShotTimer>()),
+      weak_factory_(this) {}
 
 Service::~Service() = default;
 
+void Service::SetIdentityManagerForTesting(
+    identity::mojom::IdentityManagerPtr identity_manager) {
+  identity_manager_ = std::move(identity_manager);
+}
+
+void Service::SetAssistantManagerForTesting(
+    std::unique_ptr<AssistantManagerService> assistant_manager_service) {
+  assistant_manager_service_ = std::move(assistant_manager_service);
+}
+
+void Service::SetTimerForTesting(std::unique_ptr<base::OneShotTimer> timer) {
+  token_refresh_timer_ = std::move(timer);
+}
+
 void Service::OnStart() {
   RequestAccessToken();
 }
@@ -84,7 +102,6 @@
       base::BindOnce(&Service::GetAccessTokenCallback, base::Unretained(this)));
 }
 
-// TODO: Handle |expiration_time| and token refreshing.
 void Service::GetAccessTokenCallback(const base::Optional<std::string>& token,
                                      base::Time expiration_time,
                                      const GoogleServiceAuthError& error) {
@@ -106,6 +123,9 @@
   } else {
     assistant_manager_service_->SetAccessToken(token.value());
   }
+
+  token_refresh_timer_->Start(FROM_HERE, expiration_time - base::Time::Now(),
+                              this, &Service::RequestAccessToken);
 }
 
 void Service::AddAshSessionObserver() {
diff --git a/chromeos/services/assistant/service.h b/chromeos/services/assistant/service.h
index 25ee2d7..5dbd7ad 100644
--- a/chromeos/services/assistant/service.h
+++ b/chromeos/services/assistant/service.h
@@ -21,6 +21,10 @@
 
 class GoogleServiceAuthError;
 
+namespace base {
+class OneShotTimer;
+}
+
 namespace chromeos {
 namespace assistant {
 
@@ -32,7 +36,16 @@
   Service();
   ~Service() override;
 
+  void SetIdentityManagerForTesting(
+      identity::mojom::IdentityManagerPtr identity_manager);
+
+  void SetAssistantManagerForTesting(
+      std::unique_ptr<AssistantManagerService> assistant_manager_service);
+
+  void SetTimerForTesting(std::unique_ptr<base::OneShotTimer> timer);
+
  private:
+  friend class ServiceTest;
   // service_manager::Service overrides
   void OnStart() override;
   void OnBindInterface(const service_manager::BindSourceInfo& source_info,
@@ -67,6 +80,8 @@
 
   std::unique_ptr<AssistantManagerService> assistant_manager_service_;
 
+  std::unique_ptr<base::OneShotTimer> token_refresh_timer_;
+
   base::WeakPtrFactory<Service> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(Service);
diff --git a/chromeos/services/assistant/service_unittest.cc b/chromeos/services/assistant/service_unittest.cc
new file mode 100644
index 0000000..f435220
--- /dev/null
+++ b/chromeos/services/assistant/service_unittest.cc
@@ -0,0 +1,163 @@
+// 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 "chromeos/services/assistant/service.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "chromeos/services/assistant/fake_assistant_manager_service_impl.h"
+#include "services/identity/public/mojom/identity_manager.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace assistant {
+
+namespace {
+constexpr base::TimeDelta kDefaultTokenExpirationDelay =
+    base::TimeDelta::FromMilliseconds(1000);
+}
+
+class FakeIdentityManager : identity::mojom::IdentityManager {
+ public:
+  FakeIdentityManager()
+      : binding_(this),
+        access_token_expriation_delay_(kDefaultTokenExpirationDelay) {}
+
+  identity::mojom::IdentityManagerPtr CreateInterfacePtrAndBind() {
+    identity::mojom::IdentityManagerPtr ptr;
+    binding_.Bind(mojo::MakeRequest(&ptr));
+    return ptr;
+  }
+
+  void SetAccessTokenExpirationDelay(base::TimeDelta delay) {
+    access_token_expriation_delay_ = delay;
+  }
+
+  int get_access_token_count() const { return get_access_token_count_; }
+
+ private:
+  // identity::mojom::IdentityManager:
+  void GetPrimaryAccountInfo(GetPrimaryAccountInfoCallback callback) override {
+    AccountInfo account_info;
+    account_info.account_id = "account_id";
+    account_info.gaia = "fakegaiaid";
+    account_info.email = "fake@email";
+    account_info.full_name = "full name";
+    account_info.given_name = "given name";
+    account_info.hosted_domain = "hosted_domain";
+    account_info.locale = "en";
+    account_info.picture_url = "http://fakepicture";
+
+    identity::AccountState account_state;
+    account_state.has_refresh_token = true;
+    account_state.is_primary_account = true;
+
+    std::move(callback).Run(account_info, account_state);
+  }
+
+  void GetPrimaryAccountWhenAvailable(
+      GetPrimaryAccountWhenAvailableCallback callback) override {}
+  void GetAccountInfoFromGaiaId(
+      const std::string& gaia_id,
+      GetAccountInfoFromGaiaIdCallback callback) override {}
+  void GetAccounts(GetAccountsCallback callback) override {}
+  void GetAccessToken(const std::string& account_id,
+                      const ::identity::ScopeSet& scopes,
+                      const std::string& consumer_id,
+                      GetAccessTokenCallback callback) override {
+    GoogleServiceAuthError auth_error(GoogleServiceAuthError::NONE);
+    std::move(callback).Run("fake access token",
+                            base::Time::Now() + access_token_expriation_delay_,
+                            auth_error);
+    ++get_access_token_count_;
+  }
+
+  mojo::Binding<identity::mojom::IdentityManager> binding_;
+
+  base::TimeDelta access_token_expriation_delay_;
+
+  int get_access_token_count_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeIdentityManager);
+};
+
+class ServiceTest : public testing::Test {
+ public:
+  ServiceTest()
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
+
+  void SetUp() override {
+    Test::SetUp();
+    service_ = std::make_unique<Service>();
+    fake_identity_manager_ = std::make_unique<FakeIdentityManager>();
+    fake_assistant_manager_ptr_ = new FakeAssistantManagerServiceImpl();
+
+    service_->SetIdentityManagerForTesting(
+        fake_identity_manager_->CreateInterfacePtrAndBind());
+    service_->SetAssistantManagerForTesting(
+        base::WrapUnique(fake_assistant_manager_ptr_));
+
+    service_->RequestAccessToken();
+  }
+
+  void TearDown() override {
+    Test::TearDown();
+    scoped_task_environment_.RunUntilIdle();
+  }
+
+  Service* service() { return service_.get(); }
+
+  FakeIdentityManager* identity_manager() {
+    return fake_identity_manager_.get();
+  }
+
+  FakeAssistantManagerServiceImpl* assistant_manager_service() {
+    return fake_assistant_manager_ptr_;
+  }
+
+ private:
+  std::unique_ptr<Service> service_;
+
+  std::unique_ptr<FakeIdentityManager> fake_identity_manager_;
+
+  FakeAssistantManagerServiceImpl* fake_assistant_manager_ptr_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServiceTest);
+};
+
+TEST_F(ServiceTest, RefreshTokenAfterExpire) {
+  // Set up a timer for testing.
+  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
+      base::Time::Now(), base::TimeTicks::Now());
+  auto tick_clock = task_runner->GetMockTickClock();
+  auto timer = std::make_unique<base::OneShotTimer>(tick_clock.get());
+  timer->SetTaskRunner(task_runner);
+  service()->SetTimerForTesting(std::move(timer));
+
+  base::RunLoop().RunUntilIdle();
+
+  auto current_count = identity_manager()->get_access_token_count();
+  task_runner->FastForwardBy(kDefaultTokenExpirationDelay / 2);
+  base::RunLoop().RunUntilIdle();
+
+  // Before token expire, should not request new token.
+  EXPECT_EQ(identity_manager()->get_access_token_count(), current_count);
+
+  task_runner->FastForwardBy(kDefaultTokenExpirationDelay);
+  base::RunLoop().RunUntilIdle();
+
+  // After token expire, should request once.
+  EXPECT_EQ(identity_manager()->get_access_token_count(), current_count + 1);
+}
+
+}  // namespace assistant
+}  // namespace chromeos
diff --git a/components/cast_channel/cast_message_handler.cc b/components/cast_channel/cast_message_handler.cc
index 0d73efb..86a154b 100644
--- a/components/cast_channel/cast_message_handler.cc
+++ b/components/cast_channel/cast_message_handler.cc
@@ -84,7 +84,11 @@
       socket_service_(socket_service),
       clock_(base::DefaultTickClock::GetInstance()),
       weak_ptr_factory_(this) {
-  socket_service_->AddObserver(this);
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+  socket_service_->task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&CastSocketService::AddObserver,
+                                base::Unretained(socket_service_),
+                                base::Unretained(this)));
 }
 
 CastMessageHandler::~CastMessageHandler() {
diff --git a/components/cast_channel/cast_message_handler.h b/components/cast_channel/cast_message_handler.h
index a61cba2..8c1cc334 100644
--- a/components/cast_channel/cast_message_handler.h
+++ b/components/cast_channel/cast_message_handler.h
@@ -74,7 +74,8 @@
 // message is sent. This makes the concept of VC transparent to the client.
 // This class currently provides only supports requesting app availability from
 // a device, but will be expanded to support additional types of messages.
-// This class must be run on the same sequence that CastSocketService runs on.
+// This class may be created on any sequence, but other methods (including
+// destructor) must be run on the same sequence that CastSocketService runs on.
 class CastMessageHandler : public CastSocket::Observer {
  public:
   explicit CastMessageHandler(CastSocketService* socket_service,
@@ -86,9 +87,9 @@
   // |callback| is always invoked asynchronously, and will be invoked when a
   // response is received, or if the request timed out. No-ops if there is
   // already a pending request with the same socket and app ID.
-  void RequestAppAvailability(CastSocket* socket,
-                              const std::string& app_id,
-                              GetAppAvailabilityCallback callback);
+  virtual void RequestAppAvailability(CastSocket* socket,
+                                      const std::string& app_id,
+                                      GetAppAvailabilityCallback callback);
 
   const std::string& sender_id() const { return sender_id_; }
 
diff --git a/components/cast_channel/cast_test_util.cc b/components/cast_channel/cast_test_util.cc
index cabff1b..f90d13d 100644
--- a/components/cast_channel/cast_test_util.cc
+++ b/components/cast_channel/cast_test_util.cc
@@ -48,4 +48,10 @@
   return net::IPEndPoint(net::IPAddress(192, 168, 1, 1), 8009);
 }
 
+MockCastMessageHandler::MockCastMessageHandler(
+    MockCastSocketService* socket_service)
+    : CastMessageHandler(socket_service, "userAgent", "1.2.3.4") {}
+
+MockCastMessageHandler::~MockCastMessageHandler() = default;
+
 }  // namespace cast_channel
diff --git a/components/cast_channel/cast_test_util.h b/components/cast_channel/cast_test_util.h
index 4fff1ad8..8781325 100644
--- a/components/cast_channel/cast_test_util.h
+++ b/components/cast_channel/cast_test_util.h
@@ -10,6 +10,7 @@
 
 #include "base/macros.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "components/cast_channel/cast_message_handler.h"
 #include "components/cast_channel/cast_socket.h"
 #include "components/cast_channel/cast_socket_service.h"
 #include "components/cast_channel/cast_transport.h"
@@ -143,6 +144,26 @@
   DISALLOW_COPY_AND_ASSIGN(MockCastSocket);
 };
 
+class MockCastMessageHandler : public CastMessageHandler {
+ public:
+  explicit MockCastMessageHandler(MockCastSocketService* socket_service);
+  ~MockCastMessageHandler() override;
+
+  void RequestAppAvailability(CastSocket* socket,
+                              const std::string& app_id,
+                              GetAppAvailabilityCallback callback) override {
+    DoRequestAppAvailability(socket, app_id, callback);
+  }
+
+  MOCK_METHOD3(DoRequestAppAvailability,
+               void(CastSocket*,
+                    const std::string&,
+                    GetAppAvailabilityCallback&));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCastMessageHandler);
+};
+
 // Creates the IPEndpoint 192.168.1.1.
 net::IPEndPoint CreateIPEndPointForTest();
 
diff --git a/components/consent_auditor/consent_auditor.cc b/components/consent_auditor/consent_auditor.cc
index 38a97fd..8ae79a4a 100644
--- a/components/consent_auditor/consent_auditor.cc
+++ b/components/consent_auditor/consent_auditor.cc
@@ -108,12 +108,19 @@
   // For real usage, UserEventSyncBridge should always be ready to receive
   // events when a consent gets recorded.
   // FakeUserEventService doesn't have a sync bridge.
-  // TODO(crbug.com/709094, crbug.com/761485): Remove this check when the store
-  // initializes synchronously and is instantly ready to receive data.
-  DCHECK(!user_event_service_->GetSyncBridge() ||
-         user_event_service_->GetSyncBridge()
-             ->change_processor()
-             ->IsTrackingMetadata());
+  // TODO(crbug.com/709094, crbug.com/761485): Remove this check and histogram
+  // when the store initializes synchronously and is instantly ready to receive
+  // data.
+  bool event_service_ready = !user_event_service_->GetSyncBridge() ||
+                             user_event_service_->GetSyncBridge()
+                                 ->change_processor()
+                                 ->IsTrackingMetadata();
+  // Although there is a DCHECK and we believe it should always be true,
+  // also record a histogram to verify this assumption.
+  UMA_HISTOGRAM_BOOLEAN("Privacy.ConsentAuditor.UserEventServiceReady",
+                        event_service_ready);
+  DCHECK(event_service_ready);
+
   user_event_service_->RecordUserEvent(std::move(specifics));
 }
 
diff --git a/components/download/downloader/in_progress/download_entry.cc b/components/download/downloader/in_progress/download_entry.cc
index 4924d46..38b0dd5e 100644
--- a/components/download/downloader/in_progress/download_entry.cc
+++ b/components/download/downloader/in_progress/download_entry.cc
@@ -31,7 +31,8 @@
 bool DownloadEntry::operator==(const DownloadEntry& other) const {
   return guid == other.guid && request_origin == other.request_origin &&
          download_source == other.download_source &&
-         ukm_download_id == other.ukm_download_id;
+         ukm_download_id == other.ukm_download_id &&
+         bytes_wasted == other.bytes_wasted;
 }
 
 bool DownloadEntry::operator!=(const DownloadEntry& other) const {
diff --git a/components/download/downloader/in_progress/download_entry.h b/components/download/downloader/in_progress/download_entry.h
index 449d29ef..554b1381 100644
--- a/components/download/downloader/in_progress/download_entry.h
+++ b/components/download/downloader/in_progress/download_entry.h
@@ -42,6 +42,9 @@
   // Unique ID that tracks the download UKM entry, where 0 means the
   // download_id is not yet initialized.
   uint64_t ukm_download_id = 0;
+
+  // Count for how many (extra) bytes were used (including resumption).
+  int64_t bytes_wasted = 0;
 };
 
 }  // namespace download
diff --git a/components/download/downloader/in_progress/in_progress_conversions.cc b/components/download/downloader/in_progress/in_progress_conversions.cc
index d58dc51..aa97cde 100644
--- a/components/download/downloader/in_progress/in_progress_conversions.cc
+++ b/components/download/downloader/in_progress/in_progress_conversions.cc
@@ -16,6 +16,7 @@
   entry.request_origin = proto.request_origin();
   entry.download_source = DownloadSourceFromProto(proto.download_source());
   entry.ukm_download_id = proto.ukm_download_id();
+  entry.bytes_wasted = proto.bytes_wasted();
   return entry;
 }
 
@@ -26,6 +27,7 @@
   proto.set_request_origin(entry.request_origin);
   proto.set_download_source(DownloadSourceToProto(entry.download_source));
   proto.set_ukm_download_id(entry.ukm_download_id);
+  proto.set_bytes_wasted(entry.bytes_wasted);
   return proto;
 }
 
diff --git a/components/download/downloader/in_progress/in_progress_conversions_unittest.cc b/components/download/downloader/in_progress/in_progress_conversions_unittest.cc
index 31c8cb6..6456b65 100644
--- a/components/download/downloader/in_progress/in_progress_conversions_unittest.cc
+++ b/components/download/downloader/in_progress/in_progress_conversions_unittest.cc
@@ -23,6 +23,7 @@
   entry.request_origin = "request origin";
   entry.download_source = DownloadSource::DRAG_AND_DROP;
   entry.ukm_download_id = 123;
+  entry.bytes_wasted = 1234;
   EXPECT_EQ(entry, DownloadEntryFromProto(DownloadEntryToProto(entry)));
 }
 
diff --git a/components/download/downloader/in_progress/proto/download_entry.proto b/components/download/downloader/in_progress/proto/download_entry.proto
index 8b010dd0..8d86791 100644
--- a/components/download/downloader/in_progress/proto/download_entry.proto
+++ b/components/download/downloader/in_progress/proto/download_entry.proto
@@ -16,6 +16,7 @@
   optional string request_origin = 2;
   optional DownloadSource download_source = 3;
   optional int64 ukm_download_id = 4;
+  optional int64 bytes_wasted = 5;
 }
 
 // Contains a list of entries.
diff --git a/components/download/internal/common/base_file.cc b/components/download/internal/common/base_file.cc
index 7464d35..fce4f34 100644
--- a/components/download/internal/common/base_file.cc
+++ b/components/download/internal/common/base_file.cc
@@ -82,7 +82,8 @@
     int64_t bytes_so_far,
     const std::string& hash_so_far,
     std::unique_ptr<crypto::SecureHash> hash_state,
-    bool is_sparse_file) {
+    bool is_sparse_file,
+    int64_t* const bytes_wasted) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!detached_);
 
@@ -107,7 +108,7 @@
     secure_hash_.reset();
   file_ = std::move(file);
 
-  return Open(hash_so_far);
+  return Open(hash_so_far, bytes_wasted);
 }
 
 DownloadInterruptReason BaseFile::AppendDataToFile(const char* data,
@@ -194,8 +195,10 @@
   // Re-open the file if we were still using it regardless of the interrupt
   // reason.
   DownloadInterruptReason open_result = DOWNLOAD_INTERRUPT_REASON_NONE;
-  if (was_in_progress)
-    open_result = Open(std::string());
+  if (was_in_progress) {
+    int64_t bytes_wasted;  // Do not need to use bytes_wasted.
+    open_result = Open(std::string(), &bytes_wasted);
+  }
 
   return rename_result == DOWNLOAD_INTERRUPT_REASON_NONE ? open_result
                                                          : rename_result;
@@ -309,7 +312,8 @@
   return DOWNLOAD_INTERRUPT_REASON_NONE;
 }
 
-DownloadInterruptReason BaseFile::Open(const std::string& hash_so_far) {
+DownloadInterruptReason BaseFile::Open(const std::string& hash_so_far,
+                                       int64_t* const bytes_wasted) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!detached_);
   DCHECK(!full_path_.empty());
@@ -332,6 +336,7 @@
   // For sparse file, skip hash validation.
   if (is_sparse_file_) {
     if (file_.GetLength() < bytes_so_far_) {
+      *bytes_wasted = bytes_so_far_;
       ClearFile();
       return LogInterruptReason("File has fewer written bytes than expected", 0,
                                 DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT);
@@ -342,6 +347,7 @@
   if (!secure_hash_) {
     DownloadInterruptReason reason = CalculatePartialHash(hash_so_far);
     if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
+      *bytes_wasted = file_.GetLength();
       ClearFile();
       return reason;
     }
@@ -356,14 +362,17 @@
     // The file is larger than we expected.
     // This is OK, as long as we don't use the extra.
     // Truncate the file.
+    *bytes_wasted = file_size - bytes_so_far_;
     if (!file_.SetLength(bytes_so_far_) ||
         file_.Seek(base::File::FROM_BEGIN, bytes_so_far_) != bytes_so_far_) {
       logging::SystemErrorCode error = logging::GetLastSystemErrorCode();
+      *bytes_wasted = file_size;
       ClearFile();
       return LogSystemError("Truncating to last known offset", error);
     }
   } else if (file_size < bytes_so_far_) {
     // The file is shorter than we expected.  Our hashes won't be valid.
+    *bytes_wasted = bytes_so_far_;
     ClearFile();
     return LogInterruptReason("Unable to seek to last written point", 0,
                               DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT);
diff --git a/components/download/internal/common/base_file_unittest.cc b/components/download/internal/common/base_file_unittest.cc
index 9dbbb2c..ea64590 100644
--- a/components/download/internal/common/base_file_unittest.cc
+++ b/components/download/internal/common/base_file_unittest.cc
@@ -33,6 +33,7 @@
 const int kTestDataLength2 = arraysize(kTestData2) - 1;
 const int kTestDataLength3 = arraysize(kTestData3) - 1;
 const int kTestDataLength4 = arraysize(kTestData4) - 1;
+int64_t kTestDataBytesWasted = 0;
 
 // SHA-256 hash of kTestData1 (excluding terminating NUL).
 const uint8_t kHashOfTestData1[] = {
@@ -87,7 +88,7 @@
   bool InitializeFile() {
     DownloadInterruptReason result = base_file_->Initialize(
         base::FilePath(), temp_dir_.GetPath(), base::File(), 0, std::string(),
-        std::unique_ptr<crypto::SecureHash>(), false);
+        std::unique_ptr<crypto::SecureHash>(), false, &kTestDataBytesWasted);
     EXPECT_EQ(expected_error_, result);
     return result == DOWNLOAD_INTERRUPT_REASON_NONE;
   }
@@ -118,10 +119,11 @@
     base::FilePath file_name;
     BaseFile file(DownloadItem::kInvalidId);
 
-    EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
-              file.Initialize(base::FilePath(), temp_dir_.GetPath(),
-                              base::File(), 0, std::string(),
-                              std::unique_ptr<crypto::SecureHash>(), false));
+    EXPECT_EQ(
+        DOWNLOAD_INTERRUPT_REASON_NONE,
+        file.Initialize(base::FilePath(), temp_dir_.GetPath(), base::File(), 0,
+                        std::string(), std::unique_ptr<crypto::SecureHash>(),
+                        false, &kTestDataBytesWasted));
     file_name = file.full_path();
     EXPECT_NE(base::FilePath::StringType(), file_name.value());
 
@@ -137,11 +139,12 @@
   // Create a file with the specified file name.
   void CreateFileWithName(const base::FilePath& file_name) {
     EXPECT_NE(base::FilePath::StringType(), file_name.value());
-    BaseFile duplicate_file(DownloadItem::kInvalidId);
+    BaseFile duplicate_file(download::DownloadItem::kInvalidId);
     EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
-              duplicate_file.Initialize(
-                  file_name, temp_dir_.GetPath(), base::File(), 0,
-                  std::string(), std::unique_ptr<crypto::SecureHash>(), false));
+              duplicate_file.Initialize(file_name, temp_dir_.GetPath(),
+                                        base::File(), 0, std::string(),
+                                        std::unique_ptr<crypto::SecureHash>(),
+                                        false, &kTestDataBytesWasted));
     // Write something into it.
     duplicate_file.AppendDataToFile(kTestData4, kTestDataLength4);
     // Detach the file so it isn't deleted on destruction of |duplicate_file|.
@@ -286,12 +289,12 @@
   ASSERT_TRUE(base::CopyFile(base_file_->full_path(), new_file_path));
 
   // Create another file
-  BaseFile second_file(DownloadItem::kInvalidId);
-  ASSERT_EQ(
-      DOWNLOAD_INTERRUPT_REASON_NONE,
-      second_file.Initialize(new_file_path, base::FilePath(), base::File(),
-                             base_file_->bytes_so_far(), std::string(),
-                             std::move(hash_state), false));
+  BaseFile second_file(download::DownloadItem::kInvalidId);
+  ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
+            second_file.Initialize(new_file_path, base::FilePath(),
+                                   base::File(), base_file_->bytes_so_far(),
+                                   std::string(), std::move(hash_state), false,
+                                   &kTestDataBytesWasted));
   std::string data(kTestData3);
   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
             second_file.AppendDataToFile(data.data(), data.size()));
@@ -408,11 +411,12 @@
   // Pass a file handle which was opened without the WRITE flag.
   // This should result in an error when writing.
   base::File file(path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ);
-  base_file_.reset(new BaseFile(DownloadItem::kInvalidId));
+  base_file_.reset(new BaseFile(download::DownloadItem::kInvalidId));
   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
-            base_file_->Initialize(
-                path, base::FilePath(), std::move(file), 0, std::string(),
-                std::unique_ptr<crypto::SecureHash>(), false));
+            base_file_->Initialize(path, base::FilePath(), std::move(file), 0,
+                                   std::string(),
+                                   std::unique_ptr<crypto::SecureHash>(), false,
+                                   &kTestDataBytesWasted));
 #if defined(OS_WIN)
   set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED);
 #elif defined(OS_POSIX)
@@ -449,12 +453,13 @@
   set_expected_data(kTestData4);
 
   // Use the file we've just created.
-  base_file_.reset(new BaseFile(DownloadItem::kInvalidId));
+  base_file_.reset(new BaseFile(download::DownloadItem::kInvalidId));
   ASSERT_EQ(
       DOWNLOAD_INTERRUPT_REASON_NONE,
       base_file_->Initialize(existing_file_name, base::FilePath(), base::File(),
                              kTestDataLength4, std::string(),
-                             std::unique_ptr<crypto::SecureHash>(), false));
+                             std::unique_ptr<crypto::SecureHash>(), false,
+                             &kTestDataBytesWasted));
 
   const base::FilePath file_name = base_file_->full_path();
   EXPECT_NE(base::FilePath::StringType(), file_name.value());
@@ -479,11 +484,12 @@
   EXPECT_TRUE(base::MakeFileUnwritable(readonly_file_name));
 
   // Try to overwrite it.
-  base_file_.reset(new BaseFile(DownloadItem::kInvalidId));
+  base_file_.reset(new BaseFile(download::DownloadItem::kInvalidId));
   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED,
-            base_file_->Initialize(
-                readonly_file_name, base::FilePath(), base::File(), 0,
-                std::string(), std::unique_ptr<crypto::SecureHash>(), false));
+            base_file_->Initialize(readonly_file_name, base::FilePath(),
+                                   base::File(), 0, std::string(),
+                                   std::unique_ptr<crypto::SecureHash>(), false,
+                                   &kTestDataBytesWasted));
 
   expect_in_progress_ = false;
 
@@ -509,9 +515,10 @@
   std::string hash_so_far(std::begin(kHashOfTestData1),
                           std::end(kHashOfTestData1));
   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
-            base_file_->Initialize(
-                file_path, base::FilePath(), base::File(), kTestDataLength1,
-                hash_so_far, std::unique_ptr<crypto::SecureHash>(), false));
+            base_file_->Initialize(file_path, base::FilePath(), base::File(),
+                                   kTestDataLength1, hash_so_far,
+                                   std::unique_ptr<crypto::SecureHash>(), false,
+                                   &kTestDataBytesWasted));
   set_expected_data(kTestData1);
   ASSERT_TRUE(AppendDataToFile(kTestData2));
   ASSERT_TRUE(AppendDataToFile(kTestData3));
@@ -526,9 +533,10 @@
             base::WriteFile(file_path, kTestData1, kTestDataLength1));
 
   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
-            base_file_->Initialize(
-                file_path, base::FilePath(), base::File(), kTestDataLength1,
-                std::string(), std::unique_ptr<crypto::SecureHash>(), false));
+            base_file_->Initialize(file_path, base::FilePath(), base::File(),
+                                   kTestDataLength1, std::string(),
+                                   std::unique_ptr<crypto::SecureHash>(), false,
+                                   &kTestDataBytesWasted));
   set_expected_data(kTestData1);
   ASSERT_TRUE(AppendDataToFile(kTestData2));
   ASSERT_TRUE(AppendDataToFile(kTestData3));
@@ -544,10 +552,11 @@
   std::string hash_so_far(std::begin(kHashOfTestData1),
                           std::end(kHashOfTestData1));
   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH,
-            base_file_->Initialize(
-                file_path, base::FilePath(), base::File(), kTestDataLength2,
-                hash_so_far, std::unique_ptr<crypto::SecureHash>(), false));
-  set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH);
+            base_file_->Initialize(file_path, base::FilePath(), base::File(),
+                                   kTestDataLength2, hash_so_far,
+                                   std::unique_ptr<crypto::SecureHash>(), false,
+                                   &kTestDataBytesWasted));
+  set_expected_error(download::DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH);
 }
 
 // Open a large existing file with a known hash and continue writing to it.
@@ -570,11 +579,12 @@
       0x41, 0x7c, 0xb3, 0x38, 0xd3, 0xf4, 0xe0, 0x78, 0x89, 0x46};
 
   ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
-            base_file_->Initialize(
-                file_path, base::FilePath(), base::File(), big_buffer.size(),
-                std::string(std::begin(kExpectedPartialHash),
-                            std::end(kExpectedPartialHash)),
-                std::unique_ptr<crypto::SecureHash>(), false));
+            base_file_->Initialize(file_path, base::FilePath(), base::File(),
+                                   big_buffer.size(),
+                                   std::string(std::begin(kExpectedPartialHash),
+                                               std::end(kExpectedPartialHash)),
+                                   std::unique_ptr<crypto::SecureHash>(), false,
+                                   &kTestDataBytesWasted));
   set_expected_data(big_buffer);  // Contents of the file on Open.
   ASSERT_TRUE(AppendDataToFile(big_buffer));
   ExpectHashValue(kExpectedFullHash, base_file_->Finish());
@@ -594,12 +604,13 @@
       0x02, 0x12, 0xa4, 0x1e, 0x54, 0xb5, 0xe7, 0xc2, 0x8a, 0xe5};
 
   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH,
-            base_file_->Initialize(
-                file_path, base::FilePath(), base::File(), big_buffer.size(),
-                std::string(std::begin(kExpectedPartialHash),
-                            std::end(kExpectedPartialHash)),
-                std::unique_ptr<crypto::SecureHash>(), false));
-  set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH);
+            base_file_->Initialize(file_path, base::FilePath(), base::File(),
+                                   big_buffer.size(),
+                                   std::string(std::begin(kExpectedPartialHash),
+                                               std::end(kExpectedPartialHash)),
+                                   std::unique_ptr<crypto::SecureHash>(), false,
+                                   &kTestDataBytesWasted));
+  set_expected_error(download::DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH);
 }
 
 // Open an existing file. The size of the file is too short.
@@ -609,10 +620,11 @@
             base::WriteFile(file_path, kTestData1, kTestDataLength1));
 
   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT,
-            base_file_->Initialize(
-                file_path, base::FilePath(), base::File(), kTestDataLength1 + 1,
-                std::string(), std::unique_ptr<crypto::SecureHash>(), false));
-  set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT);
+            base_file_->Initialize(file_path, base::FilePath(), base::File(),
+                                   kTestDataLength1 + 1, std::string(),
+                                   std::unique_ptr<crypto::SecureHash>(), false,
+                                   &kTestDataBytesWasted));
+  set_expected_error(download::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT);
 }
 
 // Open an existing file. The size is larger than expected.
@@ -627,9 +639,10 @@
   std::string hash_so_far(std::begin(kHashOfTestData1),
                           std::end(kHashOfTestData1));
   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
-            base_file_->Initialize(
-                file_path, base::FilePath(), base::File(), kTestDataLength1,
-                hash_so_far, std::unique_ptr<crypto::SecureHash>(), false));
+            base_file_->Initialize(file_path, base::FilePath(), base::File(),
+                                   kTestDataLength1, hash_so_far,
+                                   std::unique_ptr<crypto::SecureHash>(), false,
+                                   &kTestDataBytesWasted));
   set_expected_data(kTestData1);  // Our starting position.
   ASSERT_TRUE(AppendDataToFile(kTestData2));
   ASSERT_TRUE(AppendDataToFile(kTestData3));
@@ -647,9 +660,10 @@
             base::WriteFile(file_path, contents.data(), contents.size()));
 
   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
-            base_file_->Initialize(
-                file_path, base::FilePath(), base::File(), kTestDataLength1,
-                std::string(), std::unique_ptr<crypto::SecureHash>(), false));
+            base_file_->Initialize(file_path, base::FilePath(), base::File(),
+                                   kTestDataLength1, std::string(),
+                                   std::unique_ptr<crypto::SecureHash>(), false,
+                                   &kTestDataBytesWasted));
   set_expected_data(kTestData1);
   ASSERT_TRUE(AppendDataToFile(kTestData2));
   ASSERT_TRUE(AppendDataToFile(kTestData3));
@@ -670,9 +684,10 @@
             base::WriteFile(file_path, contents.data(), contents.size()));
 
   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
-            base_file_->Initialize(
-                file_path, base::FilePath(), base::File(), kIntermediateSize,
-                std::string(), std::unique_ptr<crypto::SecureHash>(), false));
+            base_file_->Initialize(file_path, base::FilePath(), base::File(),
+                                   kIntermediateSize, std::string(),
+                                   std::unique_ptr<crypto::SecureHash>(), false,
+                                   &kTestDataBytesWasted));
   // The extra bytes should be stripped during Initialize().
   contents.resize(kIntermediateSize, 'a');
   set_expected_data(contents);
@@ -731,7 +746,8 @@
 
   base_file_->Initialize(file_path, base::FilePath(), base::File(),
                          kTestDataLength1, std::string(),
-                         std::unique_ptr<crypto::SecureHash>(), true);
+                         std::unique_ptr<crypto::SecureHash>(), true,
+                         &kTestDataBytesWasted);
   // This will create a hole in the file.
   base_file_->WriteDataToFile(kTestDataLength1 + kTestDataLength2, kTestData3,
                               kTestDataLength3);
diff --git a/components/download/internal/common/base_file_win_unittest.cc b/components/download/internal/common/base_file_win_unittest.cc
index 3a9095b5..bd415ff1 100644
--- a/components/download/internal/common/base_file_win_unittest.cc
+++ b/components/download/internal/common/base_file_win_unittest.cc
@@ -67,11 +67,13 @@
     SCOPED_TRACE(::testing::Message() << "Source URL: " << url.spec()
                                       << " Referrer: " << test_case.referrer);
 
-    BaseFile base_file(DownloadItem::kInvalidId);
+    BaseFile base_file(download::DownloadItem::kInvalidId);
+    int64_t bytes_wasted = 0;  // unused
     ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
-              base_file.Initialize(
-                  base::FilePath(), target_directory.GetPath(), base::File(), 0,
-                  std::string(), std::unique_ptr<crypto::SecureHash>(), false));
+              base_file.Initialize(base::FilePath(), target_directory.GetPath(),
+                                   base::File(), 0, std::string(),
+                                   std::unique_ptr<crypto::SecureHash>(), false,
+                                   &bytes_wasted));
     ASSERT_FALSE(base_file.full_path().empty());
     ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
               base_file.Rename(
diff --git a/components/download/internal/common/download_ukm_helper.cc b/components/download/internal/common/download_ukm_helper.cc
index ec99fc75..8f071e66c 100644
--- a/components/download/internal/common/download_ukm_helper.cc
+++ b/components/download/internal/common/download_ukm_helper.cc
@@ -20,6 +20,10 @@
   return static_cast<int>(floor(log10(value + 1) / CalcBucketIncrement()));
 }
 
+int DownloadUkmHelper::CalcNearestKB(int num_bytes) {
+  return num_bytes / 1024;
+}
+
 void DownloadUkmHelper::RecordDownloadStarted(int download_id,
                                               ukm::SourceId source_id,
                                               DownloadContent file_type,
@@ -36,14 +40,16 @@
     base::Optional<int> change_in_file_size,
     DownloadInterruptReason reason,
     int resulting_file_size,
-    const base::TimeDelta& time_since_start) {
+    const base::TimeDelta& time_since_start,
+    int64_t bytes_wasted) {
   ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID();
   ukm::builders::Download_Interrupted builder(source_id);
   builder.SetDownloadId(download_id)
       .SetReason(static_cast<int>(reason))
       .SetResultingFileSize(
           DownloadUkmHelper::CalcExponentialBucket(resulting_file_size))
-      .SetTimeSinceStart(time_since_start.InMilliseconds());
+      .SetTimeSinceStart(time_since_start.InMilliseconds())
+      .SetBytesWasted(DownloadUkmHelper::CalcNearestKB(bytes_wasted));
   if (change_in_file_size.has_value()) {
     builder.SetChangeInFileSize(
         DownloadUkmHelper::CalcExponentialBucket(change_in_file_size.value()));
@@ -66,13 +72,15 @@
 void DownloadUkmHelper::RecordDownloadCompleted(
     int download_id,
     int resulting_file_size,
-    const base::TimeDelta& time_since_start) {
+    const base::TimeDelta& time_since_start,
+    int64_t bytes_wasted) {
   ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID();
   ukm::builders::Download_Completed(source_id)
       .SetDownloadId(download_id)
       .SetResultingFileSize(
           DownloadUkmHelper::CalcExponentialBucket(resulting_file_size))
       .SetTimeSinceStart(time_since_start.InMilliseconds())
+      .SetBytesWasted(DownloadUkmHelper::CalcNearestKB(bytes_wasted))
       .Record(ukm::UkmRecorder::Get());
 }
 
diff --git a/components/download/internal/common/download_ukm_helper_unittest.cc b/components/download/internal/common/download_ukm_helper_unittest.cc
index 4cc969a..48ef050 100644
--- a/components/download/internal/common/download_ukm_helper_unittest.cc
+++ b/components/download/internal/common/download_ukm_helper_unittest.cc
@@ -71,9 +71,10 @@
   DownloadInterruptReason reason = DOWNLOAD_INTERRUPT_REASON_NONE;
   int resulting_file_size = 2000;
   int time_since_start = 250;
+  int bytes_wasted = 1234;
   DownloadUkmHelper::RecordDownloadInterrupted(
       download_id_, change_in_file_size, reason, resulting_file_size,
-      base::TimeDelta::FromMilliseconds(time_since_start));
+      base::TimeDelta::FromMilliseconds(time_since_start), bytes_wasted);
 
   ExpectUkmMetrics(
       UkmDownloadInterrupted::kEntryName,
@@ -81,12 +82,13 @@
        UkmDownloadInterrupted::kChangeInFileSizeName,
        UkmDownloadInterrupted::kReasonName,
        UkmDownloadInterrupted::kResultingFileSizeName,
-       UkmDownloadInterrupted::kTimeSinceStartName},
+       UkmDownloadInterrupted::kTimeSinceStartName,
+       UkmDownloadInterrupted::kBytesWastedName},
       {download_id_,
        DownloadUkmHelper::CalcExponentialBucket(change_in_file_size),
        static_cast<int>(reason),
        DownloadUkmHelper::CalcExponentialBucket(resulting_file_size),
-       time_since_start});
+       time_since_start, DownloadUkmHelper::CalcNearestKB(bytes_wasted)});
 
   // RecordDownloadResumed.
   ResumeMode mode = ResumeMode::IMMEDIATE_RESTART;
@@ -104,18 +106,22 @@
   // RecordDownloadCompleted.
   int resulting_file_size_completed = 3000;
   int time_since_start_completed = 400;
+  int bytes_wasted_completed = 2345;
   DownloadUkmHelper::RecordDownloadCompleted(
       download_id_, resulting_file_size_completed,
-      base::TimeDelta::FromMilliseconds(time_since_start_completed));
+      base::TimeDelta::FromMilliseconds(time_since_start_completed),
+      bytes_wasted_completed);
 
   ExpectUkmMetrics(
       UkmDownloadCompleted::kEntryName,
       {UkmDownloadCompleted::kDownloadIdName,
        UkmDownloadCompleted::kResultingFileSizeName,
-       UkmDownloadCompleted::kTimeSinceStartName},
+       UkmDownloadCompleted::kTimeSinceStartName,
+       UkmDownloadCompleted::kBytesWastedName},
       {download_id_,
        DownloadUkmHelper::CalcExponentialBucket(resulting_file_size_completed),
-       time_since_start_completed});
+       time_since_start_completed,
+       DownloadUkmHelper::CalcNearestKB(bytes_wasted_completed)});
 }
 
 }  // namespace download
diff --git a/components/download/public/common/base_file.h b/components/download/public/common/base_file.h
index 1f45015..524ef53 100644
--- a/components/download/public/common/base_file.h
+++ b/components/download/public/common/base_file.h
@@ -11,11 +11,14 @@
 #include <memory>
 #include <string>
 
+#include "base/callback.h"
+#include "base/callback_forward.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/gtest_prod_util.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
 #include "components/download/public/common/download_export.h"
@@ -86,6 +89,9 @@
   // |is_sparse_file|: Specifies whether the file is a sparse file. If so, it is
   //     possible that a write can happen at an offset that is larger than the
   //     file size, thus creating holes in it.
+  //
+  // |bytes_wasted|: Address of an integer that will handle bytes_wasted if
+  //     there is a significant amount (ie. greater than 0).
   DownloadInterruptReason Initialize(
       const base::FilePath& full_path,
       const base::FilePath& default_directory,
@@ -93,7 +99,8 @@
       int64_t bytes_so_far,
       const std::string& hash_so_far,
       std::unique_ptr<crypto::SecureHash> hash_state,
-      bool is_sparse_file);
+      bool is_sparse_file,
+      int64_t* const bytes_wasted);
 
   // Write a new chunk of data to the file. Returns a DownloadInterruptReason
   // indicating the result of the operation. Works only if |is_sparse_file| is
@@ -181,7 +188,8 @@
   // relevant interrupt reason. Unless Open() return
   // DOWNLOAD_INTERRUPT_REASON_NONE, it should be assumed that |file_| is not
   // valid.
-  DownloadInterruptReason Open(const std::string& hash_so_far);
+  DownloadInterruptReason Open(const std::string& hash_so_far,
+                               int64_t* const bytes_wasted);
 
   // Closes and resets file_.
   void Close();
diff --git a/components/download/public/common/download_item.h b/components/download/public/common/download_item.h
index d5face7..b439cf4 100644
--- a/components/download/public/common/download_item.h
+++ b/components/download/public/common/download_item.h
@@ -222,6 +222,9 @@
   // can't be resumed.
   virtual bool IsDone() const = 0;
 
+  // Returns the calculated number of bytes wasted (if any).
+  virtual int64_t GetBytesWasted() const = 0;
+
   //    Origin State accessors -------------------------------------------------
 
   // Final URL. The primary resource being downloaded is from this URL. This is
diff --git a/components/download/public/common/download_ukm_helper.h b/components/download/public/common/download_ukm_helper.h
index 8920790..18de2a0c 100644
--- a/components/download/public/common/download_ukm_helper.h
+++ b/components/download/public/common/download_ukm_helper.h
@@ -23,6 +23,9 @@
   // that could trace back the user's exact actions.
   static int CalcExponentialBucket(int value);
 
+  // Calculate the number of bytes to the nearest kilobyte to maintain privacy.
+  static int CalcNearestKB(int num_bytes);
+
   // Record when the download has started.
   static void RecordDownloadStarted(int download_id,
                                     ukm::SourceId source_id,
@@ -35,7 +38,8 @@
       base::Optional<int> change_in_file_size,
       download::DownloadInterruptReason reason,
       int resulting_file_size,
-      const base::TimeDelta& time_since_start);
+      const base::TimeDelta& time_since_start,
+      int64_t bytes_wasted);
 
   // Record when the download is resumed.
   static void RecordDownloadResumed(int download_id,
@@ -45,7 +49,8 @@
   // Record when the download is completed.
   static void RecordDownloadCompleted(int download_id,
                                       int resulting_file_size,
-                                      const base::TimeDelta& time_since_start);
+                                      const base::TimeDelta& time_since_start,
+                                      int64_t bytes_wasted);
 
   // Friended Helper for recording main frame URLs to UKM.
   static void UpdateSourceURL(ukm::UkmRecorder* ukm_recorder,
diff --git a/components/offline_pages/core/request_header/offline_page_header.cc b/components/offline_pages/core/request_header/offline_page_header.cc
index 7cb311f..b1c8826 100644
--- a/components/offline_pages/core/request_header/offline_page_header.cc
+++ b/components/offline_pages/core/request_header/offline_page_header.cc
@@ -41,26 +41,27 @@
     if (pos == std::string::npos)
       return false;
     std::string key = base::ToLowerASCII(pair.substr(0, pos));
-    std::string value = base::ToLowerASCII(pair.substr(pos + 1));
+    std::string value = pair.substr(pos + 1);
+    std::string lower_value = base::ToLowerASCII(value);
     if (key == kOfflinePageHeaderPersistKey) {
-      if (value == "1")
+      if (lower_value == "1")
         *need_to_persist = true;
-      else if (value == "0")
+      else if (lower_value == "0")
         *need_to_persist = false;
       else
         return false;
     } else if (key == kOfflinePageHeaderReasonKey) {
-      if (value == kOfflinePageHeaderReasonValueDueToNetError)
+      if (lower_value == kOfflinePageHeaderReasonValueDueToNetError)
         *reason = OfflinePageHeader::Reason::NET_ERROR;
-      else if (value == kOfflinePageHeaderReasonValueFromDownload)
+      else if (lower_value == kOfflinePageHeaderReasonValueFromDownload)
         *reason = OfflinePageHeader::Reason::DOWNLOAD;
-      else if (value == kOfflinePageHeaderReasonValueReload)
+      else if (lower_value == kOfflinePageHeaderReasonValueReload)
         *reason = OfflinePageHeader::Reason::RELOAD;
-      else if (value == kOfflinePageHeaderReasonValueFromNotification)
+      else if (lower_value == kOfflinePageHeaderReasonValueFromNotification)
         *reason = OfflinePageHeader::Reason::NOTIFICATION;
-      else if (value == kOfflinePageHeaderReasonFileUrlIntent)
+      else if (lower_value == kOfflinePageHeaderReasonFileUrlIntent)
         *reason = OfflinePageHeader::Reason::FILE_URL_INTENT;
-      else if (value == kOfflinePageHeaderReasonContentUrlIntent)
+      else if (lower_value == kOfflinePageHeaderReasonContentUrlIntent)
         *reason = OfflinePageHeader::Reason::CONTENT_URL_INTENT;
       else
         return false;
diff --git a/components/offline_pages/core/request_header/offline_page_header_unittest.cc b/components/offline_pages/core/request_header/offline_page_header_unittest.cc
index 99ac697..f275705f 100644
--- a/components/offline_pages/core/request_header/offline_page_header_unittest.cc
+++ b/components/offline_pages/core/request_header/offline_page_header_unittest.cc
@@ -124,22 +124,22 @@
   EXPECT_FALSE(ParseFromHeaderValue("intent_url=://foo/bar", &need_to_persist,
                                     &reason, &id, &intent_url));
 
-  EXPECT_TRUE(ParseFromHeaderValue("intent_url=file://foo/bar",
+  EXPECT_TRUE(ParseFromHeaderValue("intent_url=file://foo/Bar",
                                    &need_to_persist, &reason, &id,
                                    &intent_url));
   EXPECT_FALSE(need_to_persist);
   EXPECT_EQ(OfflinePageHeader::Reason::NONE, reason);
   EXPECT_EQ("", id);
-  EXPECT_EQ(GURL("file://foo/bar"), intent_url);
+  EXPECT_EQ(GURL("file://foo/Bar"), intent_url);
 
   // Parse multiple fields.
   EXPECT_TRUE(ParseFromHeaderValue(
-      "persist=1 reason=download id=a1b2 intent_url=file://foo/bar",
+      "persist=1 reason=download id=a1b2 intent_url=file://foo/Bar",
       &need_to_persist, &reason, &id, &intent_url));
   EXPECT_TRUE(need_to_persist);
   EXPECT_EQ(OfflinePageHeader::Reason::DOWNLOAD, reason);
   EXPECT_EQ("a1b2", id);
-  EXPECT_EQ(GURL("file://foo/bar"), intent_url);
+  EXPECT_EQ(GURL("file://foo/Bar"), intent_url);
 }
 
 TEST_F(OfflinePageHeaderTest, ToString) {
@@ -147,10 +147,10 @@
   header.need_to_persist = true;
   header.reason = OfflinePageHeader::Reason::DOWNLOAD;
   header.id = "a1b2";
-  header.intent_url = GURL("file://foo/bar");
+  header.intent_url = GURL("file://foo/Bar");
   EXPECT_EQ(
       "X-Chrome-offline: persist=1 reason=download id=a1b2 "
-      "intent_url=file://foo/bar",
+      "intent_url=file://foo/Bar",
       header.GetCompleteHeaderString());
 }
 
diff --git a/components/subresource_filter/content/browser/BUILD.gn b/components/subresource_filter/content/browser/BUILD.gn
index a6d8980..cb859b7 100644
--- a/components/subresource_filter/content/browser/BUILD.gn
+++ b/components/subresource_filter/content/browser/BUILD.gn
@@ -90,6 +90,7 @@
     ":browser",
     ":test_support",
     "//base/test:test_support",
+    "//components/prefs:test_support",
     "//components/safe_browsing/db:util",
     "//components/subresource_filter/content/common",
     "//components/subresource_filter/core/browser",
diff --git a/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc b/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc
index ae420171..6742720 100644
--- a/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc
@@ -80,8 +80,8 @@
     // tasks while waiting for throttle checks to finish.
     dealer_handle_ = std::make_unique<VerifiedRulesetDealer::Handle>(
         base::MessageLoop::current()->task_runner());
-    dealer_handle_->SetRulesetFile(
-        testing::TestRuleset::Open(test_ruleset_pair_.indexed));
+    dealer_handle_->TryOpenAndSetRulesetFile(test_ruleset_pair_.indexed.path,
+                                             base::DoNothing());
     ruleset_handle_ =
         std::make_unique<VerifiedRuleset::Handle>(dealer_handle_.get());
   }
diff --git a/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc b/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
index 097e7b8..d3e3779e 100644
--- a/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
+++ b/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
@@ -136,7 +136,7 @@
 }  // namespace
 
 TEST_F(AsyncDocumentSubresourceFilterTest, ActivationStateIsReported) {
-  dealer_handle()->SetRulesetFile(testing::TestRuleset::Open(ruleset()));
+  dealer_handle()->TryOpenAndSetRulesetFile(ruleset().path, base::DoNothing());
   auto ruleset_handle = CreateRulesetHandle();
 
   AsyncDocumentSubresourceFilter::InitializationParams params(
@@ -152,7 +152,7 @@
 }
 
 TEST_F(AsyncDocumentSubresourceFilterTest, ActivationStateIsComputedCorrectly) {
-  dealer_handle()->SetRulesetFile(testing::TestRuleset::Open(ruleset()));
+  dealer_handle()->TryOpenAndSetRulesetFile(ruleset().path, base::DoNothing());
   auto ruleset_handle = CreateRulesetHandle();
 
   AsyncDocumentSubresourceFilter::InitializationParams params(
@@ -173,7 +173,7 @@
 
 TEST_F(AsyncDocumentSubresourceFilterTest, DisabledForCorruptRuleset) {
   testing::TestRuleset::CorruptByFilling(ruleset(), 0, 100, 0xFF);
-  dealer_handle()->SetRulesetFile(testing::TestRuleset::Open(ruleset()));
+  dealer_handle()->TryOpenAndSetRulesetFile(ruleset().path, base::DoNothing());
 
   auto ruleset_handle = CreateRulesetHandle();
 
@@ -190,7 +190,7 @@
 }
 
 TEST_F(AsyncDocumentSubresourceFilterTest, GetLoadPolicyForSubdocument) {
-  dealer_handle()->SetRulesetFile(testing::TestRuleset::Open(ruleset()));
+  dealer_handle()->TryOpenAndSetRulesetFile(ruleset().path, base::DoNothing());
   auto ruleset_handle = CreateRulesetHandle();
 
   AsyncDocumentSubresourceFilter::InitializationParams params(
@@ -213,7 +213,7 @@
 }
 
 TEST_F(AsyncDocumentSubresourceFilterTest, FirstDisallowedLoadIsReported) {
-  dealer_handle()->SetRulesetFile(testing::TestRuleset::Open(ruleset()));
+  dealer_handle()->TryOpenAndSetRulesetFile(ruleset().path, base::DoNothing());
   auto ruleset_handle = CreateRulesetHandle();
 
   TestCallbackReceiver first_disallowed_load_receiver;
diff --git a/components/subresource_filter/content/browser/content_ruleset_service.cc b/components/subresource_filter/content/browser/content_ruleset_service.cc
index bad103d..dcd08ecf 100644
--- a/components/subresource_filter/content/browser/content_ruleset_service.cc
+++ b/components/subresource_filter/content/browser/content_ruleset_service.cc
@@ -75,14 +75,16 @@
       task);
 }
 
+void ContentRulesetService::TryOpenAndSetRulesetFile(
+    const base::FilePath& file_path,
+    base::OnceCallback<void(base::File)> callback) {
+  ruleset_dealer_->TryOpenAndSetRulesetFile(file_path, std::move(callback));
+}
+
 void ContentRulesetService::PublishNewRulesetVersion(base::File ruleset_data) {
   DCHECK(ruleset_data.IsValid());
   CloseFileOnFileThread(&ruleset_data_);
 
-  // Will not perform verification until the ruleset is retrieved the first
-  // time.
-  ruleset_dealer_->SetRulesetFile(ruleset_data.Duplicate());
-
   ruleset_data_ = std::move(ruleset_data);
   for (auto it = content::RenderProcessHost::AllHostsIterator(); !it.IsAtEnd();
        it.Advance()) {
diff --git a/components/subresource_filter/content/browser/content_ruleset_service.h b/components/subresource_filter/content/browser/content_ruleset_service.h
index e3cc43f1..51dbb602 100644
--- a/components/subresource_filter/content/browser/content_ruleset_service.h
+++ b/components/subresource_filter/content/browser/content_ruleset_service.h
@@ -57,7 +57,7 @@
 class ContentRulesetService : public RulesetServiceDelegate,
                               content::NotificationObserver {
  public:
-  ContentRulesetService(
+  explicit ContentRulesetService(
       scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
   ~ContentRulesetService() override;
 
@@ -65,6 +65,10 @@
 
   // RulesetServiceDelegate:
   void PostAfterStartupTask(base::Closure task) override;
+  void TryOpenAndSetRulesetFile(
+      const base::FilePath& file_path,
+      base::OnceCallback<void(base::File)> callback) override;
+
   void PublishNewRulesetVersion(base::File ruleset_data) override;
 
   void set_ruleset_service(std::unique_ptr<RulesetService> ruleset_service);
diff --git a/components/subresource_filter/content/browser/content_ruleset_service_unittest.cc b/components/subresource_filter/content/browser/content_ruleset_service_unittest.cc
index 9cbabb66..e6fd599 100644
--- a/components/subresource_filter/content/browser/content_ruleset_service_unittest.cc
+++ b/components/subresource_filter/content/browser/content_ruleset_service_unittest.cc
@@ -19,8 +19,12 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/run_loop.h"
 #include "base/task_runner.h"
+#include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "components/prefs/testing_pref_service.h"
 #include "components/subresource_filter/content/common/subresource_filter_messages.h"
+#include "components/subresource_filter/core/browser/ruleset_service.h"
+#include "components/subresource_filter/core/common/test_ruleset_creator.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/notification_service.h"
@@ -191,4 +195,79 @@
   browser_client()->RunAfterStartupTask();
 }
 
+TEST_F(SubresourceFilterContentRulesetServiceTest,
+       PublishesRulesetInOnePostTask) {
+  // Regression test for crbug.com/817308. Test verifies that ruleset is
+  // published on browser startup via exactly one PostTask.
+
+  // Create a temporary directory for the indexed ruleset data.
+  base::ScopedTempDir scoped_temp_dir;
+  ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
+  const base::FilePath base_dir =
+      scoped_temp_dir.GetPath().AppendASCII("Rules").AppendASCII("Indexed");
+
+  // Create a testing ruleset.
+  testing::TestRulesetPair ruleset;
+  ASSERT_NO_FATAL_FAILURE(
+      testing::TestRulesetCreator().CreateRulesetToDisallowURLsWithPathSuffix(
+          "foo", &ruleset));
+
+  // Create local state and save the ruleset version to emulate invariant that
+  // the version of the last indexed ruleset is stored in the local state.
+  TestingPrefServiceSimple prefs;
+  IndexedRulesetVersion::RegisterPrefs(prefs.registry());
+  IndexedRulesetVersion current_version(
+      "1.2.3.4", IndexedRulesetVersion::CurrentFormatVersion());
+  current_version.SaveToPrefs(&prefs);
+
+  // Create ruleset data on a disk.
+  const base::FilePath version_dir_path =
+      IndexedRulesetLocator::GetSubdirectoryPathForVersion(base_dir,
+                                                           current_version);
+  ASSERT_EQ(RulesetService::IndexAndWriteRulesetResult::SUCCESS,
+            RulesetService::WriteRuleset(version_dir_path,
+                                         /* license_path =*/base::FilePath(),
+                                         ruleset.indexed.contents.data(),
+                                         ruleset.indexed.contents.size()));
+
+  // Create a ruleset service and its harness.
+  scoped_refptr<base::TestSimpleTaskRunner> blocking_task_runner =
+      base::MakeRefCounted<base::TestSimpleTaskRunner>();
+  scoped_refptr<base::TestSimpleTaskRunner> background_task_runner =
+      base::MakeRefCounted<base::TestSimpleTaskRunner>();
+  NotifyingMockRenderProcessHost renderer_host(browser_context());
+  auto service = std::make_unique<ContentRulesetService>(blocking_task_runner);
+  base::RunLoop callback_waiter;
+  service->SetRulesetPublishedCallbackForTesting(callback_waiter.QuitClosure());
+
+  // |RulesetService| constructor should read the last indexed ruleset version
+  // and post ruleset setup on |blocking_task_runner|. (Yes, exactly
+  // |blocking_task_runner| via |ContentRulesetService| as its delegate).
+  ASSERT_EQ(0u, blocking_task_runner->NumPendingTasks());
+  service->set_ruleset_service(std::make_unique<RulesetService>(
+      &prefs, background_task_runner, service.get(), base_dir));
+
+  // The key test assertion is that ruleset data is published via exactly one
+  // post task on |blocking_task_runner|. It is important to run pending tasks
+  // only once here.
+  ASSERT_EQ(1u, blocking_task_runner->NumPendingTasks());
+  blocking_task_runner->RunPendingTasks();
+  callback_waiter.Run();
+
+  // Check that the ruleset data is delivered to the renderer.
+  EXPECT_EQ(1u, renderer_host.sink().message_count());
+  const std::string expected_data(ruleset.indexed.contents.begin(),
+                                  ruleset.indexed.contents.end());
+  ASSERT_NO_FATAL_FAILURE(AssertSetRulesetForProcessMessageWithContent(
+      renderer_host.sink().GetMessageAt(0), expected_data));
+
+  // |ContentRulesetService| destruction requires additional tricks. Its member
+  // |VerifiedRulesetDealer::Handle| posts task upon destruction on
+  // |blocking_task_runner|.
+  service.reset();
+  // Need to wait for |VerifiedRulesetDealer| destruction on the
+  // |blocking_task_runner|.
+  blocking_task_runner->RunPendingTasks();
+}
+
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
index f8800bf..58380426 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
+++ b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
@@ -138,8 +138,8 @@
     // tasks while waiting for throttle checks to finish.
     dealer_handle_ = std::make_unique<VerifiedRulesetDealer::Handle>(
         base::MessageLoop::current()->task_runner());
-    dealer_handle_->SetRulesetFile(
-        testing::TestRuleset::Open(test_ruleset_pair_.indexed));
+    dealer_handle_->TryOpenAndSetRulesetFile(test_ruleset_pair_.indexed.path,
+                                             base::DoNothing());
 
     throttle_manager_ =
         std::make_unique<ContentSubresourceFilterThrottleManager>(
diff --git a/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc b/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc
index 32b5677b..538fc8a 100644
--- a/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc
@@ -72,8 +72,8 @@
     // tasks while waiting for throttle checks to finish.
     dealer_handle_ = std::make_unique<VerifiedRulesetDealer::Handle>(
         base::MessageLoop::current()->task_runner());
-    dealer_handle_->SetRulesetFile(
-        testing::TestRuleset::Open(test_ruleset_pair_.indexed));
+    dealer_handle_->TryOpenAndSetRulesetFile(test_ruleset_pair_.indexed.path,
+                                             base::DoNothing());
     ruleset_handle_ =
         std::make_unique<VerifiedRuleset::Handle>(dealer_handle_.get());
 
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
index 5d9754e..f10a102 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
@@ -184,8 +184,8 @@
         rules, &test_ruleset_pair_));
     auto ruleset_dealer = std::make_unique<VerifiedRulesetDealer::Handle>(
         base::MessageLoop::current()->task_runner());
-    ruleset_dealer->SetRulesetFile(
-        testing::TestRuleset::Open(test_ruleset_pair_.indexed));
+    ruleset_dealer->TryOpenAndSetRulesetFile(test_ruleset_pair_.indexed.path,
+                                             base::DoNothing());
     client_ =
         std::make_unique<::testing::NiceMock<MockSubresourceFilterClient>>();
     client_->set_ruleset_dealer(std::move(ruleset_dealer));
diff --git a/components/subresource_filter/content/browser/verified_ruleset_dealer.cc b/components/subresource_filter/content/browser/verified_ruleset_dealer.cc
index 33ce31c..e0dc2d5 100644
--- a/components/subresource_filter/content/browser/verified_ruleset_dealer.cc
+++ b/components/subresource_filter/content/browser/verified_ruleset_dealer.cc
@@ -11,6 +11,7 @@
 #include "base/files/file.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/task_runner_util.h"
 #include "base/trace_event/trace_event.h"
 #include "components/subresource_filter/core/common/indexed_ruleset.h"
 #include "components/subresource_filter/core/common/memory_mapped_ruleset.h"
@@ -22,6 +23,18 @@
 VerifiedRulesetDealer::VerifiedRulesetDealer() = default;
 VerifiedRulesetDealer::~VerifiedRulesetDealer() = default;
 
+base::File VerifiedRulesetDealer::OpenAndSetRulesetFile(
+    const base::FilePath& file_path) {
+  DCHECK(CalledOnValidSequence());
+  // On Windows, open the file with FLAG_SHARE_DELETE to allow deletion while
+  // there are handles to it still open.
+  base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
+                                 base::File::FLAG_SHARE_DELETE);
+  if (file.IsValid())
+    SetRulesetFile(file.Duplicate());
+  return file;
+}
+
 void VerifiedRulesetDealer::SetRulesetFile(base::File ruleset_file) {
   RulesetDealer::SetRulesetFile(std::move(ruleset_file));
   status_ = RulesetVerificationStatus::NOT_VERIFIED;
@@ -77,12 +90,18 @@
                          base::Bind(std::move(callback), dealer_.get()));
 }
 
-void VerifiedRulesetDealer::Handle::SetRulesetFile(base::File file) {
+void VerifiedRulesetDealer::Handle::TryOpenAndSetRulesetFile(
+    const base::FilePath& path,
+    base::OnceCallback<void(base::File)> callback) {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&VerifiedRulesetDealer::SetRulesetFile,
-                     base::Unretained(dealer_.get()), std::move(file)));
+  // |base::Unretained| is safe here because the |OpenAndSetRulesetFile| task
+  // will be posted before a task to delete the pointer upon destruction of
+  // |this| Handler.
+  base::PostTaskAndReplyWithResult(
+      task_runner_, FROM_HERE,
+      base::BindOnce(&VerifiedRulesetDealer::OpenAndSetRulesetFile,
+                     base::Unretained(dealer_.get()), path),
+      std::move(callback));
 }
 
 // VerifiedRuleset and its Handle. ---------------------------------------------
diff --git a/components/subresource_filter/content/browser/verified_ruleset_dealer.h b/components/subresource_filter/content/browser/verified_ruleset_dealer.h
index 3e1915d4..bdde4822 100644
--- a/components/subresource_filter/content/browser/verified_ruleset_dealer.h
+++ b/components/subresource_filter/content/browser/verified_ruleset_dealer.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/callback_forward.h"
+#include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/sequence_checker.h"
@@ -49,6 +50,12 @@
   void SetRulesetFile(base::File ruleset_file) override;
   scoped_refptr<const MemoryMappedRuleset> GetRuleset() override;
 
+  // Opens file and use it as ruleset file on success. Returns valid
+  // |base::File| in the case of file opened and set. Returns invalid
+  // |base::File| in the case of file open error. In the case of error
+  // ruleset dealer continues to use the previous file (if any).
+  base::File OpenAndSetRulesetFile(const base::FilePath& file_path);
+
   // For tests only.
   RulesetVerificationStatus status() const { return status_; }
 
@@ -77,9 +84,12 @@
   // at least until the callback returns.
   void GetDealerAsync(base::Callback<void(VerifiedRulesetDealer*)> callback);
 
-  // Sets the ruleset |file| that the VerifiedRulesetDealer should distribute
-  // from now on.
-  void SetRulesetFile(base::File file);
+  // Schedules file open to use as a new ruleset file. In the case of success,
+  // the new and valid |base::File| is passed to |callback|. In the case of
+  // error an invalid |base::File| is passed to |callback| and dealer continues
+  // to use previous ruleset file (if any).
+  void TryOpenAndSetRulesetFile(const base::FilePath& path,
+                                base::OnceCallback<void(base::File)> callback);
 
  private:
   // Note: Raw pointer, |dealer_| already holds a reference to |task_runner_|.
diff --git a/components/subresource_filter/content/browser/verified_ruleset_dealer_unittest.cc b/components/subresource_filter/content/browser/verified_ruleset_dealer_unittest.cc
index 5977117..91cbfd9 100644
--- a/components/subresource_filter/content/browser/verified_ruleset_dealer_unittest.cc
+++ b/components/subresource_filter/content/browser/verified_ruleset_dealer_unittest.cc
@@ -8,10 +8,12 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/files/file.h"
 #include "base/test/test_simple_task_runner.h"
 #include "components/subresource_filter/core/common/memory_mapped_ruleset.h"
 #include "components/subresource_filter/core/common/test_ruleset_creator.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace subresource_filter {
@@ -73,6 +75,22 @@
                               ruleset->data() + ruleset->length());
 }
 
+std::vector<uint8_t> ReadFileContent(base::File* file) {
+  DCHECK(file);
+  DCHECK(file->IsValid());
+
+  const int64_t file_length = file->GetLength();
+  DCHECK_LE(0, file_length);
+
+  std::vector<uint8_t> file_content(static_cast<size_t>(file_length), 0);
+  const int read_res =
+      file->Read(0, reinterpret_cast<char*>(&(file_content[0])),
+                 static_cast<int>(file_length));
+  DCHECK_EQ(read_res, file_length);
+
+  return file_content;
+}
+
 }  // namespace
 
 // Tests for VerifiedRulesetDealer. --------------------------------------------
@@ -208,6 +226,31 @@
   EXPECT_EQ(RulesetVerificationStatus::INTACT, ruleset_dealer()->status());
 }
 
+TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
+       OpenAndSetRulesetFileReturnsCorrectFileOnSuccess) {
+  base::File file =
+      ruleset_dealer()->OpenAndSetRulesetFile(rulesets().indexed_1().path);
+
+  // Check the required file is opened.
+  ASSERT_TRUE(file.IsValid());
+  EXPECT_EQ(rulesets().indexed_1().contents, ReadFileContent(&file));
+
+  // Check |OpenAndSetRulesetFile| forwards call to |SetRulesetFile| on success.
+  EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
+  EXPECT_FALSE(has_cached_ruleset());
+  EXPECT_EQ(RulesetVerificationStatus::NOT_VERIFIED,
+            ruleset_dealer()->status());
+}
+
+TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
+       OpenAndSetRulesetFileReturnsNullFileOnFailure) {
+  base::File file = ruleset_dealer()->OpenAndSetRulesetFile(
+      base::FilePath::FromUTF8Unsafe("non_existent_file"));
+
+  EXPECT_FALSE(file.IsValid());
+  EXPECT_FALSE(ruleset_dealer()->IsRulesetFileAvailable());
+}
+
 // Tests for VerifiedRulesetDealer::Handle. ------------------------------------
 
 namespace {
@@ -231,7 +274,7 @@
     EXPECT_EQ(expected_status, status_);
   }
 
-  void ExpectRulesetContents(const std::vector<uint8_t> expected_contents,
+  void ExpectRulesetContents(const std::vector<uint8_t>& expected_contents,
                              bool expected_cached = false) const {
     ExpectRulesetState(true, RulesetVerificationStatus::INTACT,
                        expected_cached);
@@ -285,6 +328,7 @@
  private:
   TestRulesets rulesets_;
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  content::TestBrowserThreadBundle thread_bundle_;
 
   DISALLOW_COPY_AND_ASSIGN(SubresourceFilterVerifiedRulesetDealerHandleTest);
 };
@@ -298,8 +342,8 @@
   std::unique_ptr<VerifiedRulesetDealer::Handle> dealer_handle(
       new VerifiedRulesetDealer::Handle(task_runner()));
   dealer_handle->GetDealerAsync(before_set_ruleset.GetCallback());
-  dealer_handle->SetRulesetFile(
-      testing::TestRuleset::Open(rulesets().indexed_1()));
+  dealer_handle->TryOpenAndSetRulesetFile(rulesets().indexed_1().path,
+                                          base::DoNothing());
   dealer_handle->GetDealerAsync(after_set_ruleset.GetCallback());
   dealer_handle->GetDealerAsync(after_warm_up.GetCallback());
   dealer_handle.reset(nullptr);
@@ -319,13 +363,13 @@
   std::unique_ptr<VerifiedRulesetDealer::Handle> dealer_handle(
       new VerifiedRulesetDealer::Handle(task_runner()));
 
-  dealer_handle->SetRulesetFile(
-      testing::TestRuleset::Open(rulesets().indexed_1()));
+  dealer_handle->TryOpenAndSetRulesetFile(rulesets().indexed_1().path,
+                                          base::DoNothing());
   dealer_handle->GetDealerAsync(after_set_ruleset_1.GetCallback());
   dealer_handle->GetDealerAsync(read_ruleset_1.GetCallback());
 
-  dealer_handle->SetRulesetFile(
-      testing::TestRuleset::Open(rulesets().indexed_2()));
+  dealer_handle->TryOpenAndSetRulesetFile(rulesets().indexed_2().path,
+                                          base::DoNothing());
   dealer_handle->GetDealerAsync(after_set_ruleset_2.GetCallback());
   dealer_handle->GetDealerAsync(read_ruleset_2.GetCallback());
 
@@ -338,6 +382,36 @@
   read_ruleset_2.ExpectRulesetContents(rulesets().indexed_2().contents);
 }
 
+TEST_F(SubresourceFilterVerifiedRulesetDealerHandleTest,
+       InvalidFileDoesNotReplaceTheValidOne) {
+  TestVerifiedRulesetDealerClient after_set_ruleset_1;
+  TestVerifiedRulesetDealerClient read_ruleset_1;
+  TestVerifiedRulesetDealerClient after_set_ruleset_2;
+  TestVerifiedRulesetDealerClient read_ruleset_2;
+
+  auto dealer_handle =
+      std::make_unique<VerifiedRulesetDealer::Handle>(task_runner());
+
+  dealer_handle->TryOpenAndSetRulesetFile(rulesets().indexed_1().path,
+                                          base::DoNothing());
+  dealer_handle->GetDealerAsync(after_set_ruleset_1.GetCallback());
+  dealer_handle->GetDealerAsync(read_ruleset_1.GetCallback());
+
+  dealer_handle->TryOpenAndSetRulesetFile(
+      base::FilePath::FromUTF8Unsafe("non_existent_file"),
+      base::BindOnce([](base::File file) { EXPECT_FALSE(file.IsValid()); }));
+  dealer_handle->GetDealerAsync(after_set_ruleset_2.GetCallback());
+  dealer_handle->GetDealerAsync(read_ruleset_2.GetCallback());
+  dealer_handle.reset(nullptr);
+  task_runner()->RunUntilIdle();
+
+  after_set_ruleset_1.ExpectRulesetState(true);
+  read_ruleset_1.ExpectRulesetContents(rulesets().indexed_1().contents);
+  after_set_ruleset_2.ExpectRulesetState(true,
+                                         RulesetVerificationStatus::INTACT);
+  read_ruleset_2.ExpectRulesetContents(rulesets().indexed_1().contents);
+}
+
 // Tests for VerifiedRuleset::Handle. ------------------------------------------
 
 namespace {
@@ -413,6 +487,7 @@
   TestRulesets rulesets_;
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   std::unique_ptr<VerifiedRulesetDealer::Handle> dealer_handle_;
+  content::TestBrowserThreadBundle thread_bundle_;
 
   DISALLOW_COPY_AND_ASSIGN(SubresourceFilterVerifiedRulesetHandleTest);
 };
@@ -423,8 +498,9 @@
   TestVerifiedRulesetClient read_ruleset;
   TestVerifiedRulesetDealerClient deleted_handle;
 
-  dealer_handle()->SetRulesetFile(
-      testing::TestRuleset::Open(rulesets().indexed_1()));
+  dealer_handle()->TryOpenAndSetRulesetFile(
+      rulesets().indexed_1().path,
+      base::BindOnce([](base::File file) { EXPECT_TRUE(file.IsValid()); }));
 
   auto ruleset_handle = CreateRulesetHandle();
   dealer_handle()->GetDealerAsync(created_handle.GetCallback());
@@ -447,8 +523,9 @@
   TestVerifiedRulesetClient read_ruleset_again_from_handle_2;
   TestVerifiedRulesetDealerClient deleted_both_handles;
 
-  dealer_handle()->SetRulesetFile(
-      testing::TestRuleset::Open(rulesets().indexed_1()));
+  dealer_handle()->TryOpenAndSetRulesetFile(
+      rulesets().indexed_1().path,
+      base::BindOnce([](base::File file) { EXPECT_TRUE(file.IsValid()); }));
 
   auto ruleset_handle_1 = CreateRulesetHandle();
   auto ruleset_handle_2 = CreateRulesetHandle();
@@ -489,15 +566,16 @@
   TestVerifiedRulesetClient read_from_handle_2_after_update;
   TestVerifiedRulesetDealerClient deleted_all_handles;
 
-  dealer_handle()->SetRulesetFile(
-      testing::TestRuleset::Open(rulesets().indexed_1()));
+  dealer_handle()->TryOpenAndSetRulesetFile(
+      rulesets().indexed_1().path,
+      base::BindOnce([](base::File file) { EXPECT_TRUE(file.IsValid()); }));
 
   auto ruleset_handle_1 = CreateRulesetHandle();
   dealer_handle()->GetDealerAsync(created_handle_1.GetCallback());
   ruleset_handle_1->GetRulesetAsync(read_from_handle_1.GetCallback());
 
-  dealer_handle()->SetRulesetFile(
-      testing::TestRuleset::Open(rulesets().indexed_2()));
+  dealer_handle()->TryOpenAndSetRulesetFile(rulesets().indexed_2().path,
+                                            base::DoNothing());
   auto ruleset_handle_2 = CreateRulesetHandle();
   dealer_handle()->GetDealerAsync(created_handle_2_after_update.GetCallback());
   ruleset_handle_2->GetRulesetAsync(read_from_handle_2.GetCallback());
@@ -537,8 +615,9 @@
   TestVerifiedRulesetDealerClient deleted_handle;
 
   testing::TestRuleset::CorruptByTruncating(rulesets().indexed_1(), 4096);
-  dealer_handle()->SetRulesetFile(
-      testing::TestRuleset::Open(rulesets().indexed_1()));
+  dealer_handle()->TryOpenAndSetRulesetFile(
+      rulesets().indexed_1().path,
+      base::BindOnce([](base::File file) { EXPECT_TRUE(file.IsValid()); }));
 
   auto ruleset_handle = CreateRulesetHandle();
   dealer_handle()->GetDealerAsync(created_handle.GetCallback());
diff --git a/components/subresource_filter/core/browser/BUILD.gn b/components/subresource_filter/core/browser/BUILD.gn
index a8536dc..9a64db1 100644
--- a/components/subresource_filter/core/browser/BUILD.gn
+++ b/components/subresource_filter/core/browser/BUILD.gn
@@ -16,7 +16,6 @@
     "//base",
     "//components/prefs",
     "//components/subresource_filter/core/common",
-    "//components/variations",
     "//third_party/protobuf:protobuf_lite",
   ]
 }
diff --git a/components/subresource_filter/core/browser/ruleset_service.cc b/components/subresource_filter/core/browser/ruleset_service.cc
index f7b22ac..b0a521d 100644
--- a/components/subresource_filter/core/browser/ruleset_service.cc
+++ b/components/subresource_filter/core/browser/ruleset_service.cc
@@ -9,7 +9,6 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/files/file_enumerator.h"
-#include "base/files/file_proxy.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/location.h"
@@ -214,12 +213,10 @@
 
 RulesetService::RulesetService(
     PrefService* local_state,
-    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
     scoped_refptr<base::SequencedTaskRunner> background_task_runner,
     RulesetServiceDelegate* delegate,
     const base::FilePath& indexed_ruleset_base_dir)
     : local_state_(local_state),
-      blocking_task_runner_(std::move(blocking_task_runner)),
       background_task_runner_(std::move(background_task_runner)),
       delegate_(delegate),
       is_after_startup_(false),
@@ -473,38 +470,26 @@
 
 void RulesetService::OpenAndPublishRuleset(
     const IndexedRulesetVersion& version) {
-  ruleset_data_.reset(new base::FileProxy(blocking_task_runner_.get()));
-  // On Windows, open the file with FLAG_SHARE_DELETE to allow deletion while
-  // there are handles to it still open. Note that creating a new file at the
-  // same path would still be impossible until after the last handle is closed.
-  ruleset_data_->CreateOrOpen(
+  const base::FilePath file_path =
       IndexedRulesetLocator::GetRulesetDataFilePath(
           IndexedRulesetLocator::GetSubdirectoryPathForVersion(
-              indexed_ruleset_base_dir_, version)),
-      base::File::FLAG_OPEN | base::File::FLAG_READ |
-          base::File::FLAG_SHARE_DELETE,
-      base::Bind(&RulesetService::OnOpenedRuleset, AsWeakPtr()));
+              indexed_ruleset_base_dir_, version));
+
+  delegate_->TryOpenAndSetRulesetFile(
+      file_path, base::BindOnce(&RulesetService::OnRulesetSet, AsWeakPtr()));
 }
 
-void RulesetService::OnOpenedRuleset(base::File::Error error) {
+void RulesetService::OnRulesetSet(base::File file) {
   // The file has just been successfully written, so a failure here is unlikely
   // unless |indexed_ruleset_base_dir_| has been tampered with or there are disk
   // errors. Still, restore the invariant that a valid version in preferences
   // always points to an existing version of disk by invalidating the prefs.
-  if (error != base::File::FILE_OK) {
+  if (!file.IsValid()) {
     IndexedRulesetVersion().SaveToPrefs(local_state_);
     return;
   }
 
-  // A second call to OpenAndPublishRuleset() may have reset |ruleset_data_| to
-  // a new instance that is in the process of calling CreateOrOpen() on a newer
-  // version. Bail out.
-  DCHECK(ruleset_data_);
-  if (!ruleset_data_->IsValid())
-    return;
-
-  DCHECK_EQ(error, base::File::Error::FILE_OK);
-  delegate_->PublishNewRulesetVersion(ruleset_data_->DuplicateFile());
+  delegate_->PublishNewRulesetVersion(std::move(file));
 }
 
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/core/browser/ruleset_service.h b/components/subresource_filter/core/browser/ruleset_service.h
index 99becdd1..6f2b76ea 100644
--- a/components/subresource_filter/core/browser/ruleset_service.h
+++ b/components/subresource_filter/core/browser/ruleset_service.h
@@ -25,7 +25,6 @@
 class PrefRegistrySimple;
 
 namespace base {
-class FileProxy;
 class SequencedTaskRunner;
 }  // namespace base
 
@@ -143,11 +142,8 @@
 // version pointed to by preferences, if valid, will exist on disk at any point
 // in time.
 //
-// Ruleset file opening is a critical for user experience operation. It is
-// posted to |blocking_task_runner|. Obsolete files deletion and rulesets
-// indexing are not critical for user experience. These tasks are posted to
-// |background_task_runner|. Since the two task runners are distinct you cannot
-// make guarantees about task ordering between them.
+// Obsolete files deletion and rulesets indexing are posted to
+// |background_task_runner|.
 class RulesetService : public base::SupportsWeakPtr<RulesetService> {
  public:
   // Enumerates the possible outcomes of indexing a ruleset and writing it to
@@ -176,7 +172,6 @@
   // See class comments for details of arguments.
   RulesetService(
       PrefService* local_state,
-      scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
       scoped_refptr<base::SequencedTaskRunner> background_task_runner,
       RulesetServiceDelegate* delegate,
       const base::FilePath& indexed_ruleset_base_dir);
@@ -199,6 +194,8 @@
 
  private:
   friend class SubresourceFilteringRulesetServiceTest;
+  FRIEND_TEST_ALL_PREFIXES(SubresourceFilterContentRulesetServiceTest,
+                           PublishesRulesetInOnePostTask);
   FRIEND_TEST_ALL_PREFIXES(SubresourceFilteringRulesetServiceTest,
                            NewRuleset_WriteFailure);
   FRIEND_TEST_ALL_PREFIXES(SubresourceFilteringRulesetServiceDeathTest,
@@ -255,17 +252,11 @@
                         const IndexedRulesetVersion& version);
 
   void OpenAndPublishRuleset(const IndexedRulesetVersion& version);
-  void OnOpenedRuleset(base::File::Error error);
+  void OnRulesetSet(base::File file);
 
   PrefService* const local_state_;
 
-  // Task runner for tasks critical for user experience. The current ruleset
-  // file opening should be done on this task runner so as it throttles the
-  // first page load.
-  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
-
-  // Task runner for tasks that don't influence user experience. Obsolete files
-  // deletion and ruleset indexing should be done on this task runner.
+  // Obsolete files deletion and indexing should be done on this runner.
   scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
 
   // Must outlive |this| object.
@@ -275,7 +266,6 @@
   bool is_after_startup_;
 
   const base::FilePath indexed_ruleset_base_dir_;
-  std::unique_ptr<base::FileProxy> ruleset_data_;
 
   DISALLOW_COPY_AND_ASSIGN(RulesetService);
 };
diff --git a/components/subresource_filter/core/browser/ruleset_service_delegate.h b/components/subresource_filter/core/browser/ruleset_service_delegate.h
index e6aa823..9b46edd 100644
--- a/components/subresource_filter/core/browser/ruleset_service_delegate.h
+++ b/components/subresource_filter/core/browser/ruleset_service_delegate.h
@@ -5,8 +5,10 @@
 #ifndef COMPONENTS_SUBRESOURCE_FILTER_CORE_BROWSER_RULESET_SERVICE_DELEGATE_H_
 #define COMPONENTS_SUBRESOURCE_FILTER_CORE_BROWSER_RULESET_SERVICE_DELEGATE_H_
 
+#include "base/callback.h"
 #include "base/callback_forward.h"
 #include "base/files/file.h"
+#include "base/files/file_path.h"
 
 namespace subresource_filter {
 
@@ -19,6 +21,14 @@
   // Posts |task| to be executed on the UI thread after browser start-up.
   virtual void PostAfterStartupTask(base::Closure task) = 0;
 
+  // Schedules file open and use it as ruleset file. In the case of success,
+  // the new and valid |base::File| is passed to |callback|. In the case of
+  // error an invalid |base::File| is passed to |callback|. The previous
+  // ruleset file will be used (if any).
+  virtual void TryOpenAndSetRulesetFile(
+      const base::FilePath& file_path,
+      base::OnceCallback<void(base::File)> callback) = 0;
+
   // Redistributes the new version of the |ruleset| to all existing consumers,
   // and sets up |ruleset| to be distributed to all future consumers.
   virtual void PublishNewRulesetVersion(base::File ruleset_data) = 0;
diff --git a/components/subresource_filter/core/browser/ruleset_service_unittest.cc b/components/subresource_filter/core/browser/ruleset_service_unittest.cc
index 4cb09b82..a9ba71f 100644
--- a/components/subresource_filter/core/browser/ruleset_service_unittest.cc
+++ b/components/subresource_filter/core/browser/ruleset_service_unittest.cc
@@ -13,6 +13,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/bind.h"
 #include "base/environment.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -21,6 +22,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/task_runner_util.h"
 #include "base/test/histogram_tester.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/test/test_simple_task_runner.h"
@@ -86,7 +88,9 @@
 
 class MockRulesetServiceDelegate : public RulesetServiceDelegate {
  public:
-  MockRulesetServiceDelegate() = default;
+  explicit MockRulesetServiceDelegate(
+      scoped_refptr<base::TestSimpleTaskRunner> blocking_task_runner)
+      : blocking_task_runner_(std::move(blocking_task_runner)) {}
   ~MockRulesetServiceDelegate() override = default;
 
   void SimulateStartupCompleted() {
@@ -103,6 +107,18 @@
       after_startup_tasks_.push_back(task);
   }
 
+  void TryOpenAndSetRulesetFile(
+      const base::FilePath& path,
+      base::OnceCallback<void(base::File)> callback) override {
+    // Emulate |VerifiedRulesetDealer::Handle| behaviour:
+    //   1. Open file on task runner.
+    //   2. Reply with result on current thread runner.
+    base::PostTaskAndReplyWithResult(
+        blocking_task_runner_.get(), FROM_HERE,
+        base::BindOnce(&MockRulesetServiceDelegate::OpenRulesetFile, path),
+        std::move(callback));
+  }
+
   void PublishNewRulesetVersion(base::File ruleset_data) override {
     published_rulesets_.push_back(std::move(ruleset_data));
   }
@@ -110,9 +126,15 @@
   std::vector<base::File>& published_rulesets() { return published_rulesets_; }
 
  private:
+  static base::File OpenRulesetFile(base::FilePath file_path) {
+    return base::File(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
+                                     base::File::FLAG_SHARE_DELETE);
+  }
+
   bool is_after_startup_ = false;
   std::vector<base::Closure> after_startup_tasks_;
   std::vector<base::File> published_rulesets_;
+  scoped_refptr<base::TestSimpleTaskRunner> blocking_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(MockRulesetServiceDelegate);
 };
@@ -174,10 +196,11 @@
   }
 
   void ResetRulesetService() {
-    mock_delegate_ = std::make_unique<MockRulesetServiceDelegate>();
+    mock_delegate_ =
+        std::make_unique<MockRulesetServiceDelegate>(blocking_task_runner_);
     service_ = std::make_unique<RulesetService>(
-        &pref_service_, blocking_task_runner_, background_task_runner_,
-        mock_delegate_.get(), base_dir());
+        &pref_service_, background_task_runner_, mock_delegate_.get(),
+        base_dir());
   }
 
   void ClearRulesetService() {
@@ -1028,8 +1051,7 @@
       AssertReadonlyRulesetFile(&mock_delegate()->published_rulesets()[0]));
 }
 
-TEST_F(SubresourceFilteringRulesetServiceTest,
-       ParallelOpenOfTwoFilesPublishesOnlyLastOne) {
+TEST_F(SubresourceFilteringRulesetServiceTest, ParallelOpenOfTwoFiles) {
   // Test emulates bail out situation when ruleset file opening is cheduled
   // during another file opening.
   SimulateStartupCompletedAndWaitForTasks();
@@ -1052,11 +1074,12 @@
 
   // Process both respones.
   base::RunLoop().RunUntilIdle();
-
-  // Only the last one should fire callback.
-  ASSERT_EQ(1u, mock_delegate()->published_rulesets().size());
+  ASSERT_EQ(2u, mock_delegate()->published_rulesets().size());
   ASSERT_NO_FATAL_FAILURE(AssertValidRulesetFileWithContents(
       &mock_delegate()->published_rulesets()[0],
+      test_ruleset_1().indexed.contents));
+  ASSERT_NO_FATAL_FAILURE(AssertValidRulesetFileWithContents(
+      &mock_delegate()->published_rulesets()[1],
       test_ruleset_2().indexed.contents));
 }
 
diff --git a/components/subresource_filter/core/browser/subresource_filter_features.cc b/components/subresource_filter/core/browser/subresource_filter_features.cc
index 407eb2eb..3eea9a6 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features.cc
+++ b/components/subresource_filter/core/browser/subresource_filter_features.cc
@@ -18,7 +18,6 @@
 #include "base/strings/string_util.h"
 #include "base/synchronization/lock.h"
 #include "base/trace_event/trace_event_argument.h"
-#include "components/variations/variations_associated_data.h"
 
 namespace subresource_filter {
 
diff --git a/components/viz/client/client_shared_bitmap_manager.cc b/components/viz/client/client_shared_bitmap_manager.cc
index e2cae71..3982a9e 100644
--- a/components/viz/client/client_shared_bitmap_manager.cc
+++ b/components/viz/client/client_shared_bitmap_manager.cc
@@ -32,7 +32,8 @@
                      id,
                      sequence_number),
         shared_bitmap_allocation_notifier_(
-            std::move(shared_bitmap_allocation_notifier)) {}
+            std::move(shared_bitmap_allocation_notifier)),
+        tracing_id_(shared_memory->mapped_id()) {}
 
   ClientSharedBitmap(
       scoped_refptr<mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
@@ -51,17 +52,16 @@
     (*shared_bitmap_allocation_notifier_)->DidDeleteSharedBitmap(id());
   }
 
-  // SharedBitmap:
-  base::SharedMemoryHandle GetSharedMemoryHandle() const override {
-    if (!shared_memory_holder_)
-      return base::SharedMemoryHandle();
-    return shared_memory_holder_->handle();
+  // SharedBitmap implementation.
+  base::UnguessableToken GetCrossProcessGUID() const override {
+    return tracing_id_;
   }
 
  private:
   scoped_refptr<mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
       shared_bitmap_allocation_notifier_;
   std::unique_ptr<base::SharedMemory> shared_memory_holder_;
+  base::UnguessableToken tracing_id_;
 };
 
 // Collect extra information for debugging bitmap creation failures.
diff --git a/components/viz/common/hit_test/aggregated_hit_test_region.h b/components/viz/common/hit_test/aggregated_hit_test_region.h
index 2f93daae0..9fb48ff 100644
--- a/components/viz/common/hit_test/aggregated_hit_test_region.h
+++ b/components/viz/common/hit_test/aggregated_hit_test_region.h
@@ -32,8 +32,8 @@
       : frame_sink_id(frame_sink_id),
         flags(flags),
         rect(rect),
-        transform(transform),
-        child_count(child_count) {}
+        child_count(child_count),
+        transform_(transform) {}
 
   // The FrameSinkId corresponding to this region.  Events that match
   // are routed to this surface.
@@ -46,13 +46,30 @@
   // The rectangle that defines the region in parent region's coordinate space.
   gfx::Rect rect;
 
-  // The transform applied to the rect in parent region's coordinate space.
-  gfx::Transform transform;
-
   // The number of children including their children below this entry.
   // If this element is not matched then child_count elements can be skipped
   // to move to the next entry.
   int32_t child_count;
+
+  // gfx::Transform is backed by SkMatrix44. SkMatrix44 has a mutable attribute
+  // which can be changed even during a const function call (e.g.
+  // SkMatrix44::getType()). This means that when HitTestQuery reads the
+  // transform in the read-only shared memory segment created (and populated) by
+  // HitTestAggregator, if it attempts to perform any operation on the
+  // transform (e.g. use Transform::IsIdentity()), skia will attempt to write to
+  // the read-only shared memory segment, causing exception in HitTestQuery.
+  // For this reason, it is necessary for the HitTestQuery to make a copy of the
+  // transform before using it. To enforce this, the |transform_| attribute is
+  // made private here, and exposed through an accessor which always makes a
+  // copy.
+  gfx::Transform transform() const { return transform_; }
+  void set_transform(const gfx::Transform& transform) {
+    transform_ = transform;
+  }
+
+ private:
+  // The transform applied to the rect in parent region's coordinate space.
+  gfx::Transform transform_;
 };
 
 }  // namespace viz
diff --git a/components/viz/common/quads/shared_bitmap.h b/components/viz/common/quads/shared_bitmap.h
index 14683ce..7a41ac8 100644
--- a/components/viz/common/quads/shared_bitmap.h
+++ b/components/viz/common/quads/shared_bitmap.h
@@ -16,7 +16,7 @@
 #include "ui/gfx/geometry/size.h"
 
 namespace base {
-class SharedMemoryHandle;
+class UnguessableToken;
 }
 
 namespace viz {
@@ -40,16 +40,15 @@
   virtual ~SharedBitmap();
 
   uint8_t* pixels() { return pixels_; }
-
   const SharedBitmapId& id() { return id_; }
 
   // The sequence number that ClientSharedBitmapManager assigned to this
   // SharedBitmap.
   uint32_t sequence_number() const { return sequence_number_; }
 
-  // Returns the shared memory's handle when the back end is base::SharedMemory.
-  // Otherwise, this returns an invalid handle.
-  virtual base::SharedMemoryHandle GetSharedMemoryHandle() const = 0;
+  // Returns the GUID for tracing when the SharedBitmap supports cross-process
+  // use via shared memory. Otherwise, this returns empty.
+  virtual base::UnguessableToken GetCrossProcessGUID() const = 0;
 
   // Returns true if the size is valid and false otherwise.
   static bool SizeInBytes(const gfx::Size& size, size_t* size_in_bytes);
diff --git a/components/viz/host/hit_test/hit_test_query.cc b/components/viz/host/hit_test/hit_test_query.cc
index 092505a..ec8e9d5 100644
--- a/components/viz/host/hit_test/hit_test_query.cc
+++ b/components/viz/host/hit_test/hit_test_query.cc
@@ -95,7 +95,7 @@
     AggregatedHitTestRegion* region,
     Target* target) const {
   gfx::PointF location_transformed(location_in_parent);
-  region->transform.TransformPoint(&location_transformed);
+  region->transform().TransformPoint(&location_transformed);
   if (!gfx::RectF(region->rect).Contains(location_transformed))
     return false;
 
@@ -152,7 +152,7 @@
     return false;
   }
 
-  region->transform.TransformPoint(location_in_target);
+  region->transform().TransformPoint(location_in_target);
   location_in_target->Offset(-region->rect.x(), -region->rect.y());
   if (!target_ancestor)
     return true;
diff --git a/components/viz/host/hit_test/hit_test_query_unittest.cc b/components/viz/host/hit_test/hit_test_query_unittest.cc
index 85555fe..df8a25f1 100644
--- a/components/viz/host/hit_test/hit_test_query_unittest.cc
+++ b/components/viz/host/hit_test/hit_test_query_unittest.cc
@@ -34,7 +34,9 @@
         mojo::SharedBufferHandle::Create(num_bytes);
     active_buffer_ = active_handle->Map(num_bytes);
     hit_test_query_.OnAggregatedHitTestRegionListUpdated(
-        std::move(active_handle), handle_size, std::move(idle_handle),
+        active_handle->Clone(mojo::SharedBufferHandle::AccessMode::READ_ONLY),
+        handle_size,
+        idle_handle->Clone(mojo::SharedBufferHandle::AccessMode::READ_ONLY),
         handle_size);
   }
   void TearDown() override {}
diff --git a/components/viz/host/host_frame_sink_manager.cc b/components/viz/host/host_frame_sink_manager.cc
index 79a38c26..8a9377f 100644
--- a/components/viz/host/host_frame_sink_manager.cc
+++ b/components/viz/host/host_frame_sink_manager.cc
@@ -248,6 +248,12 @@
   frame_sink_manager_->EvictSurfaces(surface_ids);
 }
 
+void HostFrameSinkManager::RequestCopyOfOutput(
+    const FrameSinkId& frame_sink_id,
+    std::unique_ptr<CopyOutputRequest> request) {
+  frame_sink_manager_->RequestCopyOfOutput(frame_sink_id, std::move(request));
+}
+
 std::unique_ptr<CompositorFrameSinkSupport>
 HostFrameSinkManager::CreateCompositorFrameSinkSupport(
     mojom::CompositorFrameSinkClient* client,
diff --git a/components/viz/host/host_frame_sink_manager.h b/components/viz/host/host_frame_sink_manager.h
index b86b9e993..9fe3d13 100644
--- a/components/viz/host/host_frame_sink_manager.h
+++ b/components/viz/host/host_frame_sink_manager.h
@@ -141,6 +141,12 @@
   // Marks the given SurfaceIds for destruction.
   void EvictSurfaces(const std::vector<SurfaceId>& surface_ids);
 
+  // Takes a snapshot of |frame_sink_id|. Next time a display frame is
+  // generated, the snapshot will be taken from the Surface belonging to
+  // |frame_sink_id| that is reachable from the root Surface.
+  void RequestCopyOfOutput(const FrameSinkId& frame_sink_id,
+                           std::unique_ptr<CopyOutputRequest> request);
+
   // CompositorFrameSinkSupportManager:
   std::unique_ptr<CompositorFrameSinkSupport> CreateCompositorFrameSinkSupport(
       mojom::CompositorFrameSinkClient* client,
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index aec23b7..f8b55e5 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -816,7 +816,7 @@
   auto it = render_pass_backings_.find(render_pass_id);
   DCHECK(it != render_pass_backings_.end());
   SkSurface* texture = it->second.render_pass_surface.get();
-  return gfx::Size(texture->height(), texture->width());
+  return gfx::Size(texture->width(), texture->height());
 }
 
 }  // namespace viz
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index 4f8b8f7e..dabf723 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -1054,7 +1054,7 @@
                         device_scale_factor);
   auto copy_request = CopyOutputRequest::CreateStubForTesting();
   auto* copy_request_ptr = copy_request.get();
-  embedded_support->RequestCopyOfSurface(std::move(copy_request));
+  embedded_support->RequestCopyOfOutput(std::move(copy_request));
 
   Quad root_quads[] = {
       Quad::SolidColorQuad(SK_ColorWHITE, gfx::Rect(5, 5)),
@@ -1198,7 +1198,7 @@
                         device_scale_factor);
   auto copy_request(CopyOutputRequest::CreateStubForTesting());
   auto* copy_request_ptr = copy_request.get();
-  embedded_support->RequestCopyOfSurface(std::move(copy_request));
+  embedded_support->RequestCopyOfOutput(std::move(copy_request));
 
   LocalSurfaceId parent_local_surface_id = allocator_.GenerateId();
   SurfaceId parent_surface_id(parent_support->frame_sink_id(),
diff --git a/components/viz/service/display_embedder/server_shared_bitmap_manager.cc b/components/viz/service/display_embedder/server_shared_bitmap_manager.cc
index 9477a5a..c0411d88 100644
--- a/components/viz/service/display_embedder/server_shared_bitmap_manager.cc
+++ b/components/viz/service/display_embedder/server_shared_bitmap_manager.cc
@@ -22,7 +22,9 @@
 class BitmapData : public base::RefCountedThreadSafe<BitmapData> {
  public:
   explicit BitmapData(size_t buffer_size) : buffer_size(buffer_size) {}
+  // For shm allocated and shared from a client out-of-process.
   std::unique_ptr<base::SharedMemory> memory;
+  // For memory allocated by the ServerSharedBitmapManager for in-process.
   std::unique_ptr<uint8_t[]> pixels;
   size_t buffer_size;
 
@@ -49,11 +51,13 @@
       manager_->FreeSharedMemoryFromMap(id());
   }
 
-  // SharedBitmap:
-  base::SharedMemoryHandle GetSharedMemoryHandle() const override {
-    if (!bitmap_data_->memory)
-      return base::SharedMemoryHandle();
-    return bitmap_data_->memory->handle();
+  // SharedBitmap implementation.
+  base::UnguessableToken GetCrossProcessGUID() const override {
+    if (!bitmap_data_->memory) {
+      // Locally allocated for in-process use.
+      return {};
+    }
+    return bitmap_data_->memory->mapped_id();
   }
 
  private:
@@ -151,7 +155,8 @@
     const SharedBitmapId& id) {
   auto data = base::MakeRefCounted<BitmapData>(buffer_size);
   data->memory = std::make_unique<base::SharedMemory>(memory_handle, false);
-  data->memory->Map(data->buffer_size);
+  if (!data->memory->Map(data->buffer_size))
+    return false;
   data->memory->Close();
 
   base::AutoLock lock(lock_);
@@ -172,30 +177,32 @@
     base::trace_event::ProcessMemoryDump* pmd) {
   base::AutoLock lock(lock_);
 
-  for (const auto& bitmap : handle_map_) {
+  for (const auto& pair : handle_map_) {
+    const SharedBitmapId& id = pair.first;
+    BitmapData* data = pair.second.get();
+
+    std::string dump_str = base::StringPrintf(
+        "sharedbitmap/%s", base::HexEncode(id.name, sizeof(id.name)).c_str());
     base::trace_event::MemoryAllocatorDump* dump =
-        pmd->CreateAllocatorDump(base::StringPrintf(
-            "sharedbitmap/%s",
-            base::HexEncode(bitmap.first.name, sizeof(bitmap.first.name))
-                .c_str()));
+        pmd->CreateAllocatorDump(dump_str);
     if (!dump)
       return false;
 
     dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
                     base::trace_event::MemoryAllocatorDump::kUnitsBytes,
-                    bitmap.second->buffer_size);
+                    data->buffer_size);
 
-    if (bitmap.second->memory) {
-      base::UnguessableToken shared_memory_guid =
-          bitmap.second->memory->mapped_id();
-      if (!shared_memory_guid.is_empty()) {
-        pmd->CreateSharedMemoryOwnershipEdge(dump->guid(), shared_memory_guid,
-                                             0 /* importance*/);
-      }
+    if (data->memory) {
+      // Resources from a client have shared memory, and we use the guid from
+      // that.
+      base::UnguessableToken shared_memory_guid = data->memory->mapped_id();
+      DCHECK(!shared_memory_guid.is_empty());
+      pmd->CreateSharedMemoryOwnershipEdge(dump->guid(), shared_memory_guid,
+                                           0 /* importance*/);
     } else {
-      // Generate a global GUID used to share this allocation with renderer
-      // processes.
-      auto guid = GetSharedBitmapGUIDForTracing(bitmap.first);
+      // Otherwise, resources were allocated locally for in-process use, and
+      // there is no shared memory. Instead make up a GUID for them.
+      auto guid = GetSharedBitmapGUIDForTracing(id);
       pmd->CreateSharedGlobalAllocatorDump(guid);
       pmd->AddOwnershipEdge(dump->guid(), guid);
     }
diff --git a/components/viz/service/display_embedder/server_shared_bitmap_manager_unittest.cc b/components/viz/service/display_embedder/server_shared_bitmap_manager_unittest.cc
index 662e020..5ac3bdb 100644
--- a/components/viz/service/display_embedder/server_shared_bitmap_manager_unittest.cc
+++ b/components/viz/service/display_embedder/server_shared_bitmap_manager_unittest.cc
@@ -160,13 +160,14 @@
   size_t size_in_bytes;
   EXPECT_TRUE(SharedBitmap::SizeInBytes(bitmap_size, &size_in_bytes));
   std::unique_ptr<base::SharedMemory> bitmap(new base::SharedMemory());
-  auto shared_memory_guid = bitmap->handle().GetGUID();
   bitmap->CreateAndMapAnonymous(size_in_bytes);
   memset(bitmap->memory(), 0xff, size_in_bytes);
-  SharedBitmapId id = SharedBitmap::GenerateId();
+  base::UnguessableToken shared_memory_guid = bitmap->handle().GetGUID();
+  EXPECT_FALSE(shared_memory_guid.is_empty());
 
   SharedBitmapAllocationNotifierImpl notifier(manager());
 
+  SharedBitmapId id = SharedBitmap::GenerateId();
   base::SharedMemoryHandle handle = bitmap->handle().Duplicate();
   mojo::ScopedSharedBufferHandle buffer_handle = mojo::WrapSharedMemoryHandle(
       handle, size_in_bytes,
@@ -175,8 +176,7 @@
 
   std::unique_ptr<SharedBitmap> shared_bitmap;
   shared_bitmap = manager()->GetSharedBitmapFromId(gfx::Size(1, 1), id);
-  EXPECT_EQ(shared_bitmap->GetSharedMemoryHandle().GetGUID(),
-            shared_memory_guid);
+  EXPECT_EQ(shared_bitmap->GetCrossProcessGUID(), shared_memory_guid);
 
   notifier.DidDeleteSharedBitmap(id);
 }
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
index 13948ef..9d14d10 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -488,7 +488,7 @@
   return gfx::Size();
 }
 
-void CompositorFrameSinkSupport::RequestCopyOfSurface(
+void CompositorFrameSinkSupport::RequestCopyOfOutput(
     std::unique_ptr<CopyOutputRequest> copy_request) {
   if (!last_activated_surface_id_.is_valid())
     return;
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
index aae2a88..d2ba916 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.h
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -127,8 +127,7 @@
   void AttachCaptureClient(CapturableFrameSink::Client* client) override;
   void DetachCaptureClient(CapturableFrameSink::Client* client) override;
   gfx::Size GetActiveFrameSize() override;
-  void RequestCopyOfSurface(
-      std::unique_ptr<CopyOutputRequest> request) override;
+  void RequestCopyOfOutput(std::unique_ptr<CopyOutputRequest> request) override;
 
   HitTestAggregator* GetHitTestAggregator();
 
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
index 19997ffe..fe0d9edc 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -639,7 +639,7 @@
       base::BindOnce(&CopyRequestTestCallback, &called1));
   request->set_source(kArbitrarySourceId1);
 
-  support_->RequestCopyOfSurface(std::move(request));
+  support_->RequestCopyOfOutput(std::move(request));
   GetSurfaceForId(surface_id)->OnWillBeDrawn();
   EXPECT_FALSE(called1);
 
@@ -649,7 +649,7 @@
       base::BindOnce(&CopyRequestTestCallback, &called2));
   request->set_source(kArbitrarySourceId2);
 
-  support_->RequestCopyOfSurface(std::move(request));
+  support_->RequestCopyOfOutput(std::move(request));
   GetSurfaceForId(surface_id)->OnWillBeDrawn();
   // Callbacks have different sources so neither should be called.
   EXPECT_FALSE(called1);
@@ -661,7 +661,7 @@
       base::BindOnce(&CopyRequestTestCallback, &called3));
   request->set_source(kArbitrarySourceId1);
 
-  support_->RequestCopyOfSurface(std::move(request));
+  support_->RequestCopyOfOutput(std::move(request));
   GetSurfaceForId(surface_id)->OnWillBeDrawn();
   // Two callbacks are from source1, so the first should be called.
   EXPECT_TRUE(called1);
@@ -831,7 +831,7 @@
   auto request = std::make_unique<CopyOutputRequest>(
       CopyOutputRequest::ResultFormat::RGBA_BITMAP,
       base::BindOnce(StubResultCallback));
-  support_->RequestCopyOfSurface(std::move(request));
+  support_->RequestCopyOfOutput(std::move(request));
 
   // Both surfaces should report that they have a CopyOutputRequest.
   EXPECT_TRUE(GetSurfaceForId(id1)->HasCopyOutputRequests());
@@ -870,7 +870,7 @@
   auto request = std::make_unique<CopyOutputRequest>(
       CopyOutputRequest::ResultFormat::RGBA_BITMAP,
       base::BindOnce(StubResultCallback));
-  support_->RequestCopyOfSurface(std::move(request));
+  support_->RequestCopyOfOutput(std::move(request));
 
   // Create the second surface.
   support_->SubmitCompositorFrame(local_surface_id2,
@@ -915,7 +915,7 @@
   auto request = std::make_unique<CopyOutputRequest>(
       CopyOutputRequest::ResultFormat::RGBA_BITMAP,
       base::BindOnce(StubResultCallback));
-  support_->RequestCopyOfSurface(std::move(request));
+  support_->RequestCopyOfOutput(std::move(request));
 
   // Both surfaces should report that they have a CopyOutputRequest.
   EXPECT_TRUE(GetSurfaceForId(id1)->HasCopyOutputRequests());
@@ -957,7 +957,7 @@
   auto request = std::make_unique<CopyOutputRequest>(
       CopyOutputRequest::ResultFormat::RGBA_BITMAP,
       base::BindOnce(StubResultCallback));
-  support_->RequestCopyOfSurface(std::move(request));
+  support_->RequestCopyOfOutput(std::move(request));
 
   // Create the second surface.
   support_->SubmitCompositorFrame(local_surface_id2,
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
index 6e1df891..7ce276f 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -267,6 +267,17 @@
   }
 }
 
+void FrameSinkManagerImpl::RequestCopyOfOutput(
+    const FrameSinkId& frame_sink_id,
+    std::unique_ptr<CopyOutputRequest> request) {
+  auto it = support_map_.find(frame_sink_id);
+  if (it == support_map_.end()) {
+    // |request| will send an empty result when it goes out of scope.
+    return;
+  }
+  it->second->RequestCopyOfOutput(std::move(request));
+}
+
 void FrameSinkManagerImpl::OnSurfaceCreated(const SurfaceId& surface_id) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.h b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
index 1e73527..768a9b4a 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.h
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
@@ -93,6 +93,8 @@
   void CreateVideoCapturer(
       mojom::FrameSinkVideoCapturerRequest request) override;
   void EvictSurfaces(const std::vector<SurfaceId>& surface_ids) override;
+  void RequestCopyOfOutput(const FrameSinkId& frame_sink_id,
+                           std::unique_ptr<CopyOutputRequest> request) override;
 
   // SurfaceObserver implementation.
   void OnSurfaceCreated(const SurfaceId& surface_id) override;
diff --git a/components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h b/components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h
index 200201d..986efed 100644
--- a/components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h
+++ b/components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h
@@ -54,7 +54,7 @@
   virtual gfx::Size GetActiveFrameSize() = 0;
 
   // Issues a request for a copy of the next composited frame.
-  virtual void RequestCopyOfSurface(
+  virtual void RequestCopyOfOutput(
       std::unique_ptr<CopyOutputRequest> request) = 0;
 };
 
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
index 9d44508b9..5f4fc4ca 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
@@ -531,7 +531,7 @@
   // damage over all the frames that weren't captured.
   request->set_result_selection(gfx::Rect(content_rect.size()));
   dirty_rect_ = gfx::Rect();
-  resolved_target_->RequestCopyOfSurface(std::move(request));
+  resolved_target_->RequestCopyOfOutput(std::move(request));
 }
 
 void FrameSinkVideoCapturerImpl::DidCopyFrame(
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
index c3443125..20680135 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
@@ -198,7 +198,7 @@
 
   gfx::Size GetActiveFrameSize() override { return kSourceSize; }
 
-  void RequestCopyOfSurface(
+  void RequestCopyOfOutput(
       std::unique_ptr<CopyOutputRequest> request) override {
     EXPECT_EQ(CopyOutputResult::Format::I420_PLANES, request->result_format());
     EXPECT_NE(base::UnguessableToken(), request->source());
diff --git a/components/viz/service/hit_test/hit_test_aggregator.cc b/components/viz/service/hit_test/hit_test_aggregator.cc
index aaab034..544b4a9 100644
--- a/components/viz/service/hit_test/hit_test_aggregator.cc
+++ b/components/viz/service/hit_test/hit_test_aggregator.cc
@@ -18,17 +18,6 @@
 constexpr uint32_t kIncrementalSize = 1024;
 constexpr uint32_t kMaxSize = 100 * 1024;
 
-void PrepareTransformForReadOnlySharedMemory(gfx::Transform* transform) {
-  // |transform| is going to be shared in read-only memory to HitTestQuery.
-  // However, if HitTestQuery tries to operate on it, then it is possible that
-  // it will attempt to perform write on the underlying SkMatrix44 [1], causing
-  // invalid memory write in read-only memory.
-  // [1]
-  // https://cs.chromium.org/chromium/src/third_party/skia/include/core/SkMatrix44.h?l=133
-  // Explicitly calling getType() to compute the type-mask in SkMatrix44.
-  transform->matrix().getType();
-}
-
 }  // namespace
 
 HitTestAggregator::HitTestAggregator(const HitTestManager* hit_test_manager,
@@ -190,10 +179,8 @@
   element->frame_sink_id = frame_sink_id;
   element->flags = flags;
   element->rect = rect;
-  element->transform = transform;
   element->child_count = child_count;
-
-  PrepareTransformForReadOnlySharedMemory(&element->transform);
+  element->set_transform(transform);
 }
 
 void HitTestAggregator::MarkEndAt(size_t index) {
diff --git a/components/viz/service/hit_test/hit_test_aggregator_unittest.cc b/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
index af7fc3b..cdb74501 100644
--- a/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
+++ b/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
@@ -718,7 +718,7 @@
   EXPECT_EQ(region->child_count, 2);
 
   gfx::Point point(300, 300);
-  gfx::Transform transform(region->transform);
+  gfx::Transform transform(region->transform());
   transform.TransformPointReverse(&point);
   EXPECT_TRUE(point == gfx::Point(100, 200));
 
diff --git a/components/viz/service/surfaces/surface_unittest.cc b/components/viz/service/surfaces/surface_unittest.cc
index ea3d8b5..deecdf2 100644
--- a/components/viz/service/surfaces/surface_unittest.cc
+++ b/components/viz/service/surfaces/surface_unittest.cc
@@ -91,7 +91,7 @@
   ASSERT_TRUE(!!surface);
 
   bool copy_called = false;
-  support->RequestCopyOfSurface(std::make_unique<CopyOutputRequest>(
+  support->RequestCopyOfOutput(std::make_unique<CopyOutputRequest>(
       CopyOutputRequest::ResultFormat::RGBA_BITMAP,
       base::BindOnce(&TestCopyResultCallback, &copy_called)));
   surface->OnWillBeDrawn();
diff --git a/components/viz/test/test_frame_sink_manager.h b/components/viz/test/test_frame_sink_manager.h
index 3f6afa5..2c646eb5 100644
--- a/components/viz/test/test_frame_sink_manager.h
+++ b/components/viz/test/test_frame_sink_manager.h
@@ -51,6 +51,9 @@
   void CreateVideoCapturer(
       mojom::FrameSinkVideoCapturerRequest request) override {}
   void EvictSurfaces(const std::vector<SurfaceId>& surface_ids) override {}
+  void RequestCopyOfOutput(
+      const FrameSinkId& frame_sink_id,
+      std::unique_ptr<CopyOutputRequest> request) override {}
 
   mojo::Binding<mojom::FrameSinkManager> binding_;
   mojom::FrameSinkManagerClientPtr client_;
diff --git a/components/viz/test/test_layer_tree_frame_sink.cc b/components/viz/test/test_layer_tree_frame_sink.cc
index ea346d2d..c3d769f 100644
--- a/components/viz/test/test_layer_tree_frame_sink.cc
+++ b/components/viz/test/test_layer_tree_frame_sink.cc
@@ -174,7 +174,7 @@
   support_->SubmitCompositorFrame(local_surface_id_, std::move(frame));
 
   for (auto& copy_request : copy_requests_)
-    support_->RequestCopyOfSurface(std::move(copy_request));
+    support_->RequestCopyOfOutput(std::move(copy_request));
   copy_requests_.clear();
 
   if (!display_->has_scheduler()) {
diff --git a/components/viz/test/test_shared_bitmap_manager.cc b/components/viz/test/test_shared_bitmap_manager.cc
index 2948900..3afe26c8 100644
--- a/components/viz/test/test_shared_bitmap_manager.cc
+++ b/components/viz/test/test_shared_bitmap_manager.cc
@@ -27,8 +27,8 @@
   ~OwnedSharedBitmap() override = default;
 
   // SharedBitmap:
-  base::SharedMemoryHandle GetSharedMemoryHandle() const override {
-    return shared_memory_->handle();
+  base::UnguessableToken GetCrossProcessGUID() const override {
+    return shared_memory_->mapped_id();
   }
 
  private:
@@ -37,13 +37,19 @@
 
 class UnownedSharedBitmap : public SharedBitmap {
  public:
-  UnownedSharedBitmap(uint8_t* pixels, const SharedBitmapId& id)
-      : SharedBitmap(pixels, id, g_next_sequence_number++) {}
+  UnownedSharedBitmap(uint8_t* pixels,
+                      const SharedBitmapId& id,
+                      const base::UnguessableToken& tracing_id)
+      : SharedBitmap(pixels, id, g_next_sequence_number++),
+        tracing_id_(tracing_id) {}
 
   // SharedBitmap:
-  base::SharedMemoryHandle GetSharedMemoryHandle() const override {
-    return base::SharedMemoryHandle();
+  base::UnguessableToken GetCrossProcessGUID() const override {
+    return tracing_id_;
   }
+
+ private:
+  base::UnguessableToken tracing_id_;
 };
 
 }  // namespace
@@ -69,7 +75,8 @@
   if (bitmap_map_.find(id) == bitmap_map_.end())
     return nullptr;
   uint8_t* pixels = static_cast<uint8_t*>(bitmap_map_[id]->memory());
-  return std::make_unique<UnownedSharedBitmap>(pixels, id);
+  const base::UnguessableToken& tracing_id = bitmap_map_[id]->mapped_id();
+  return std::make_unique<UnownedSharedBitmap>(pixels, id, tracing_id);
 }
 
 bool TestSharedBitmapManager::ChildAllocatedSharedBitmap(
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index ad93eb9..b16afc8e 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -2428,7 +2428,7 @@
     active_files_--;
   }
 
-  void Initialize(const InitializeCallback& callback,
+  void Initialize(InitializeCallback callback,
                   const CancelRequestCallback& cancel_request_callback,
                   const download::DownloadItem::ReceivedSlices& received_slices,
                   bool is_parallelizable) override {
diff --git a/content/browser/devtools/protocol/security_handler.cc b/content/browser/devtools/protocol/security_handler.cc
index bd4a401..db4c56ec 100644
--- a/content/browser/devtools/protocol/security_handler.cc
+++ b/content/browser/devtools/protocol/security_handler.cc
@@ -141,6 +141,8 @@
 
 void SecurityHandler::DidChangeVisibleSecurityState() {
   DCHECK(enabled_);
+  if (!web_contents()->GetDelegate())
+    return;
 
   SecurityStyleExplanations security_style_explanations;
   blink::WebSecurityStyle security_style =
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index 11373b6..72b3820 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -343,7 +343,7 @@
     active_files_--;
   }
 
-  void Initialize(const InitializeCallback& callback,
+  void Initialize(InitializeCallback callback,
                   const CancelRequestCallback& cancel_request_callback,
                   const download::DownloadItem::ReceivedSlices& received_slices,
                   bool is_parallelizable) override {
@@ -1633,6 +1633,7 @@
   download->Resume();
   WaitForCompletion(download);
 
+  ASSERT_EQ(interruption_offset, download->GetBytesWasted());
   ASSERT_EQ(parameters.size, download->GetReceivedBytes());
   ASSERT_EQ(parameters.size, download->GetTotalBytes());
   ASSERT_NO_FATAL_FAILURE(
@@ -1724,6 +1725,7 @@
   TestDownloadHttpResponse::Parameters parameters =
       TestDownloadHttpResponse::Parameters::WithSingleInterruption(
           inject_error_callback());
+  int64_t interruption_offset = parameters.injected_errors.front();
 
   TestDownloadHttpResponse::StartServing(parameters, server_url);
   download::DownloadItem* download =
@@ -1743,6 +1745,7 @@
   download->Resume();
   WaitForCompletion(download);
 
+  ASSERT_EQ(interruption_offset, download->GetBytesWasted());
   ASSERT_EQ(parameters.size, download->GetReceivedBytes());
   ASSERT_EQ(parameters.size, download->GetTotalBytes());
   ASSERT_NO_FATAL_FAILURE(ReadAndVerifyFileContents(
@@ -2325,6 +2328,7 @@
   download->Resume();
   WaitForCompletion(download);
 
+  EXPECT_EQ(kIntermediateSize, download->GetBytesWasted());
   EXPECT_FALSE(PathExists(intermediate_file_path));
   ReadAndVerifyFileContents(parameters.pattern_generator_seed,
                             parameters.size,
@@ -2598,6 +2602,8 @@
   download->Resume();
   WaitForCompletion(download);
 
+  // The amount "extra" that was added to the file.
+  EXPECT_EQ(100, download->GetBytesWasted());
   EXPECT_FALSE(PathExists(intermediate_file_path));
   ReadAndVerifyFileContents(parameters.pattern_generator_seed,
                             parameters.size,
diff --git a/content/browser/download/download_file.h b/content/browser/download/download_file.h
index f531890..af9a174 100644
--- a/content/browser/download/download_file.h
+++ b/content/browser/download/download_file.h
@@ -12,6 +12,7 @@
 
 #include "base/callback_forward.h"
 #include "base/files/file_path.h"
+#include "components/download/public/common/base_file.h"
 #include "components/download/public/common/download_interrupt_reasons.h"
 #include "components/download/public/common/download_item.h"
 #include "content/common/content_export.h"
@@ -28,11 +29,20 @@
 // cancelled, the DownloadFile is destroyed.
 class CONTENT_EXPORT DownloadFile {
  public:
-  // Callback used with Initialize.  On a successful initialize, |reason| will
-  // be DOWNLOAD_INTERRUPT_REASON_NONE; on a failed initialize, it will be
-  // set to the reason for the failure.
-  typedef base::Callback<void(download::DownloadInterruptReason reason)>
-      InitializeCallback;
+  // Callback used with Initialize.
+  //
+  // On a successful initialize, |reason| = DOWNLOAD_INTERRUPT_REASON_NONE;
+  // on a failed initialize, it will be set to the reason for the failure.
+  //
+  // In the case that the originally downloaded file had to be deleted,
+  // |bytes_wasted| would be set to > 0.
+  //
+  // TODO(b/73967242): Change this to a OnceCallback. This is currently a
+  // repeating callback because gMock does not support all built in actions for
+  // move-only arguments (specifically SaveArg from download_item_impl_unittest.
+  using InitializeCallback =
+      base::RepeatingCallback<void(download::DownloadInterruptReason reason,
+                                   int64_t bytes_wasted)>;
 
   // Callback used with Rename*().  On a successful rename |reason| will be
   // DOWNLOAD_INTERRUPT_REASON_NONE and |path| the path the rename
@@ -52,7 +62,7 @@
   // thread as per the comment above, passing DOWNLOAD_INTERRUPT_REASON_NONE
   // on success, or a network download interrupt reason on failure.
   virtual void Initialize(
-      const InitializeCallback& initialize_callback,
+      InitializeCallback initialize_callback,
       const CancelRequestCallback& cancel_request_callback,
       const download::DownloadItem::ReceivedSlices& received_slices,
       bool is_parallelizable) = 0;
diff --git a/content/browser/download/download_file_impl.cc b/content/browser/download/download_file_impl.cc
index cbdcf01..ffecdf4 100644
--- a/content/browser/download/download_file_impl.cc
+++ b/content/browser/download/download_file_impl.cc
@@ -247,7 +247,7 @@
 }
 
 void DownloadFileImpl::Initialize(
-    const InitializeCallback& initialize_callback,
+    InitializeCallback initialize_callback,
     const CancelRequestCallback& cancel_request_callback,
     const download::DownloadItem::ReceivedSlices& received_slices,
     bool is_parallelizable) {
@@ -271,14 +271,16 @@
   } else {
     bytes_so_far = save_info_->offset;
   }
-  download::DownloadInterruptReason result =
-      file_.Initialize(save_info_->file_path, default_download_directory_,
-                       std::move(save_info_->file), bytes_so_far,
-                       save_info_->hash_of_partial_file,
-                       std::move(save_info_->hash_state), IsSparseFile());
-  if (result != download::DOWNLOAD_INTERRUPT_REASON_NONE) {
-    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                            base::BindOnce(initialize_callback, result));
+  int64_t bytes_wasted = 0;
+  download::DownloadInterruptReason reason = file_.Initialize(
+      save_info_->file_path, default_download_directory_,
+      std::move(save_info_->file), bytes_so_far,
+      save_info_->hash_of_partial_file, std::move(save_info_->hash_state),
+      IsSparseFile(), &bytes_wasted);
+  if (reason != download::DOWNLOAD_INTERRUPT_REASON_NONE) {
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::BindOnce(std::move(initialize_callback), reason, bytes_wasted));
     return;
   }
   download_start_ = base::TimeTicks::Now();
@@ -290,8 +292,8 @@
 
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::BindOnce(initialize_callback,
-                     download::DOWNLOAD_INTERRUPT_REASON_NONE));
+      base::BindOnce(std::move(initialize_callback),
+                     download::DOWNLOAD_INTERRUPT_REASON_NONE, bytes_wasted));
 
   // Initial pull from the straw from all source streams.
   for (auto& source_stream : source_streams_)
diff --git a/content/browser/download/download_file_impl.h b/content/browser/download/download_file_impl.h
index e140db1..2af556c 100644
--- a/content/browser/download/download_file_impl.h
+++ b/content/browser/download/download_file_impl.h
@@ -54,7 +54,7 @@
   ~DownloadFileImpl() override;
 
   // DownloadFile functions.
-  void Initialize(const InitializeCallback& initialize_callback,
+  void Initialize(InitializeCallback initialize_callback,
                   const CancelRequestCallback& cancel_request_callback,
                   const download::DownloadItem::ReceivedSlices& received_slices,
                   bool is_parallelizable) override;
diff --git a/content/browser/download/download_file_unittest.cc b/content/browser/download/download_file_unittest.cc
index 93d17338..b74328d 100644
--- a/content/browser/download/download_file_unittest.cc
+++ b/content/browser/download/download_file_unittest.cc
@@ -206,7 +206,8 @@
 
   void SetInterruptReasonCallback(const base::Closure& closure,
                                   download::DownloadInterruptReason* reason_p,
-                                  download::DownloadInterruptReason reason) {
+                                  download::DownloadInterruptReason reason,
+                                  int64_t bytes_wasted) {
     *reason_p = reason;
     closure.Run();
   }
@@ -252,9 +253,9 @@
         download::DOWNLOAD_INTERRUPT_REASON_NONE;
     base::RunLoop loop_runner;
     download_file_->Initialize(
-        base::Bind(&DownloadFileTest::SetInterruptReasonCallback,
-                   weak_ptr_factory.GetWeakPtr(), loop_runner.QuitClosure(),
-                   &result),
+        base::BindRepeating(&DownloadFileTest::SetInterruptReasonCallback,
+                            weak_ptr_factory.GetWeakPtr(),
+                            loop_runner.QuitClosure(), &result),
         DownloadFile::CancelRequestCallback(), received_slices, true);
     loop_runner.Run();
 
diff --git a/content/browser/download/download_item_impl.cc b/content/browser/download/download_item_impl.cc
index 91b3e3d..fedb1f2 100644
--- a/content/browser/download/download_item_impl.cc
+++ b/content/browser/download/download_item_impl.cc
@@ -721,6 +721,10 @@
   return false;
 }
 
+int64_t DownloadItemImpl::GetBytesWasted() const {
+  return bytes_wasted_;
+}
+
 const GURL& DownloadItemImpl::GetURL() const {
   return request_info_.url_chain.empty() ? GURL::EmptyGURL()
                                          : request_info_.url_chain.back();
@@ -1511,7 +1515,8 @@
 }
 
 void DownloadItemImpl::OnDownloadFileInitialized(
-    download::DownloadInterruptReason result) {
+    download::DownloadInterruptReason result,
+    int64_t bytes_wasted) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(state_ == TARGET_PENDING_INTERNAL ||
          state_ == INTERRUPTED_TARGET_PENDING_INTERNAL)
@@ -1519,6 +1524,36 @@
 
   DVLOG(20) << __func__
             << "() result:" << DownloadInterruptReasonToString(result);
+
+  // Record bytes wasted.
+  if (bytes_wasted > 0) {
+    // Since |bytes_wasted_| is only used for testing, set it here.
+    bytes_wasted_ = bytes_wasted;
+
+    if (!GetBrowserContext())
+      return;
+
+    // Get in-progress cache entry.
+    DownloadManagerDelegate* manager_delegate =
+        GetBrowserContext()->GetDownloadManagerDelegate();
+    if (manager_delegate) {
+      download::InProgressCache* in_progress_cache =
+          manager_delegate->GetInProgressCache();
+      if (in_progress_cache) {
+        // Add |bytes_wasted| to tallying count.
+        base::Optional<download::DownloadEntry> entry_opt =
+            in_progress_cache->RetrieveEntry(guid_);
+        if (entry_opt.has_value()) {
+          download::DownloadEntry entry = entry_opt.value();
+          entry.bytes_wasted += bytes_wasted;
+          bytes_wasted_ = entry.bytes_wasted;
+          in_progress_cache->AddOrReplaceEntry(entry);
+        }
+      }
+    }
+  }
+
+  // Handle download interrupt reason.
   if (result != download::DOWNLOAD_INTERRUPT_REASON_NONE) {
     ReleaseDownloadFile(true);
     InterruptAndDiscardPartialState(result);
@@ -1845,7 +1880,6 @@
 
     auto_opened_ = true;
   }
-  UpdateObservers();
 
   base::TimeDelta time_since_start = GetEndTime() - GetStartTime();
 
@@ -1856,8 +1890,11 @@
   if (in_progress_entry) {
     download::DownloadUkmHelper::RecordDownloadCompleted(
         in_progress_entry->ukm_download_id, resulting_file_size,
-        time_since_start);
+        time_since_start, in_progress_entry->bytes_wasted);
   }
+
+  // After all of the records are done, then update the observers.
+  UpdateObservers();
 }
 
 // **** End of Download progression cascade
@@ -2017,7 +2054,7 @@
 
     download::DownloadUkmHelper::RecordDownloadInterrupted(
         in_progress_entry->ukm_download_id, change_in_file_size, reason,
-        resulting_file_size, time_since_start);
+        resulting_file_size, time_since_start, in_progress_entry->bytes_wasted);
   }
   if (reason ==
       download::DOWNLOAD_INTERRUPT_REASON_SERVER_CONTENT_LENGTH_MISMATCH) {
diff --git a/content/browser/download/download_item_impl.h b/content/browser/download/download_item_impl.h
index 002727c..66a3f8c 100644
--- a/content/browser/download/download_item_impl.h
+++ b/content/browser/download/download_item_impl.h
@@ -220,6 +220,7 @@
   bool IsTemporary() const override;
   bool CanResume() const override;
   bool IsDone() const override;
+  int64_t GetBytesWasted() const override;
   const GURL& GetURL() const override;
   const std::vector<GURL>& GetUrlChain() const override;
   const GURL& GetOriginalUrl() const override;
@@ -501,7 +502,8 @@
   void Init(bool active, download::DownloadItem::DownloadType download_type);
 
   // Callback from file thread when we initialize the DownloadFile.
-  void OnDownloadFileInitialized(download::DownloadInterruptReason result);
+  void OnDownloadFileInitialized(download::DownloadInterruptReason result,
+                                 int64_t bytes_wasted);
 
   // Called to determine the target path. Will cause OnDownloadTargetDetermined
   // to be called when the target information is available.
@@ -639,6 +641,9 @@
   // considered to be |GetTargetFilePath().BaseName()|.
   base::FilePath display_name_;
 
+  // Number of bytes wasted.
+  int64_t bytes_wasted_ = 0;
+
   // Information from the response.
 
   // The HTTP response headers. This contains a nullptr when the response has
diff --git a/content/browser/download/download_item_impl_unittest.cc b/content/browser/download/download_item_impl_unittest.cc
index d3ea641c..2892317 100644
--- a/content/browser/download/download_item_impl_unittest.cc
+++ b/content/browser/download/download_item_impl_unittest.cc
@@ -12,6 +12,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/bind.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
 #include "base/containers/circular_deque.h"
@@ -229,12 +230,12 @@
 // E.g.:
 //
 //   EXPECT_CALL(foo, Bar(1, _))
-//     .WithArg<1>(ScheduleCallbackWithParam(0));
+//     .WithArg<1>(ScheduleCallbackWithParams(0, 0));
 //
 //   .. will invoke the second argument to Bar with 0 as the parameter.
-ACTION_P(ScheduleCallbackWithParam, param) {
+ACTION_P2(ScheduleCallbackWithParams, param1, param2) {
   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                          base::BindOnce(arg0, param));
+                          base::BindOnce(std::move(arg0), param1, param2));
 }
 
 // Schedules a task to invoke a closure.
@@ -312,8 +313,8 @@
       mock_download_file = new StrictMock<MockDownloadFile>;
       download_file.reset(mock_download_file);
       EXPECT_CALL(*mock_download_file, Initialize(_, _, _, _))
-          .WillOnce(ScheduleCallbackWithParam(
-              download::DOWNLOAD_INTERRUPT_REASON_NONE));
+          .WillOnce(ScheduleCallbackWithParams(
+              download::DOWNLOAD_INTERRUPT_REASON_NONE, 0));
       EXPECT_CALL(*mock_download_file, FullPath())
           .WillRepeatedly(ReturnRefOfCopy(base::FilePath()));
     }
@@ -1185,8 +1186,8 @@
   EXPECT_CALL(*file, Cancel());
   EXPECT_CALL(*request_handle, CancelRequest(_));
   EXPECT_CALL(*file, Initialize(_, _, _, _))
-      .WillOnce(ScheduleCallbackWithParam(
-          download::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED));
+      .WillOnce(ScheduleCallbackWithParams(
+          download::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED, 0));
 
   DownloadTargetCallback download_target_callback;
   EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _))
@@ -2310,7 +2311,8 @@
   EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(_, _))
       .WillOnce(SaveArg<1>(&target_callback));
   ScheduleObservations(PostInitializeFileObservations(), destination_observer);
-  initialize_callback.Run(download::DOWNLOAD_INTERRUPT_REASON_NONE);
+  std::move(initialize_callback)
+      .Run(download::DOWNLOAD_INTERRUPT_REASON_NONE, 0);
 
   task_environment_.RunUntilIdle();
 
@@ -2356,7 +2358,8 @@
   EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(_, _))
       .WillOnce(SaveArg<1>(&target_callback));
   ScheduleObservations(PostInitializeFileObservations(), destination_observer);
-  initialize_callback.Run(download::DOWNLOAD_INTERRUPT_REASON_NONE);
+  std::move(initialize_callback)
+      .Run(download::DOWNLOAD_INTERRUPT_REASON_NONE, 0);
 
   task_environment_.RunUntilIdle();
   ASSERT_FALSE(target_callback.is_null());
@@ -2419,7 +2422,8 @@
   EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(_, _))
       .WillOnce(SaveArg<1>(&target_callback));
   ScheduleObservations(PostInitializeFileObservations(), destination_observer);
-  initialize_callback.Run(download::DOWNLOAD_INTERRUPT_REASON_NONE);
+  std::move(initialize_callback)
+      .Run(download::DOWNLOAD_INTERRUPT_REASON_NONE, 0);
 
   task_environment_.RunUntilIdle();
   ASSERT_FALSE(target_callback.is_null());
diff --git a/content/browser/download/download_job.cc b/content/browser/download/download_job.cc
index 1889e71..6aa1355 100644
--- a/content/browser/download/download_job.cc
+++ b/content/browser/download/download_job.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/download/download_job.h"
 
+#include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "components/download/public/common/download_task_runner.h"
 #include "content/browser/download/download_item_impl.h"
@@ -62,24 +63,26 @@
 
 void DownloadJob::Start(
     DownloadFile* download_file_,
-    const DownloadFile::InitializeCallback& callback,
+    DownloadFile::InitializeCallback callback,
     const download::DownloadItem::ReceivedSlices& received_slices) {
   download::GetDownloadTaskRunner()->PostTask(
       FROM_HERE,
-      base::BindOnce(&DownloadFile::Initialize,
-                     // Safe because we control download file lifetime.
-                     base::Unretained(download_file_),
-                     base::Bind(&DownloadJob::OnDownloadFileInitialized,
-                                weak_ptr_factory_.GetWeakPtr(), callback),
-                     base::Bind(&DownloadJob::CancelRequestWithOffset,
-                                weak_ptr_factory_.GetWeakPtr()),
-                     received_slices, IsParallelizable()));
+      base::BindOnce(
+          &DownloadFile::Initialize,
+          // Safe because we control download file lifetime.
+          base::Unretained(download_file_),
+          base::BindRepeating(&DownloadJob::OnDownloadFileInitialized,
+                              weak_ptr_factory_.GetWeakPtr(), callback),
+          base::BindRepeating(&DownloadJob::CancelRequestWithOffset,
+                              weak_ptr_factory_.GetWeakPtr()),
+          received_slices, IsParallelizable()));
 }
 
 void DownloadJob::OnDownloadFileInitialized(
-    const DownloadFile::InitializeCallback& callback,
-    download::DownloadInterruptReason result) {
-  callback.Run(result);
+    DownloadFile::InitializeCallback callback,
+    download::DownloadInterruptReason result,
+    int64_t bytes_wasted) {
+  std::move(callback).Run(result, bytes_wasted);
 }
 
 bool DownloadJob::AddInputStream(
diff --git a/content/browser/download/download_job.h b/content/browser/download/download_job.h
index 8c8c7462..aedb6e2 100644
--- a/content/browser/download/download_job.h
+++ b/content/browser/download/download_job.h
@@ -32,8 +32,9 @@
   // TODO(qinmin): Remove the |callback| and |download_file_| parameter if
   // DownloadJob owns download file.
   void Start(DownloadFile* download_file_,
-             const DownloadFile::InitializeCallback& callback,
+             DownloadFile::InitializeCallback callback,
              const download::DownloadItem::ReceivedSlices& received_slices);
+
   virtual void Cancel(bool user_cancel);
   virtual void Pause();
   virtual void Resume(bool resume_request);
@@ -55,8 +56,9 @@
  protected:
   // Callback from file thread when we initialize the DownloadFile.
   virtual void OnDownloadFileInitialized(
-      const DownloadFile::InitializeCallback& callback,
-      download::DownloadInterruptReason result);
+      DownloadFile::InitializeCallback callback,
+      download::DownloadInterruptReason result,
+      int64_t bytes_wasted);
 
   // Add an input stream to the download sink. Return false if we start to
   // destroy download file.
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index ae16d2d6..3776062c 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -127,16 +127,27 @@
                      params->callback()));
 }
 
-download::DownloadEntry CreateDownloadEntryFromItem(
-    const DownloadItemImpl& item) {
+// Helper functions for DownloadItem -> DownloadEntry for InProgressCache.
+
+uint64_t GetUniqueDownloadId() {
   // Get a new UKM download_id that is not 0.
   uint64_t download_id = 0;
   do {
     download_id = base::RandUint64();
   } while (download_id == 0);
+  return download_id;
+}
 
+download::DownloadEntry CreateDownloadEntryFromItemImpl(
+    const DownloadItemImpl& item) {
   return download::DownloadEntry(item.GetGuid(), item.download_source(),
-                                 download_id);
+                                 GetUniqueDownloadId());
+}
+
+download::DownloadEntry CreateDownloadEntryFromItem(
+    const download::DownloadItem& item) {
+  return download::DownloadEntry(
+      item.GetGuid(), download::DownloadSource::UNKNOWN, GetUniqueDownloadId());
 }
 
 DownloadManagerImpl::UniqueUrlDownloadHandlerPtr BeginDownload(
@@ -331,14 +342,28 @@
 
   switch (download->GetState()) {
     case download::DownloadItem::DownloadState::COMPLETE:
+    // Intentional fallthrough.
     case download::DownloadItem::DownloadState::CANCELLED:
       if (in_progress_cache_)
         in_progress_cache_->RemoveEntry(download->GetGuid());
       break;
-    case download::DownloadItem::DownloadState::IN_PROGRESS:
-      // TODO(crbug.com/778425): After RetrieveEntry has been implemented, do a
-      // check to make sure the entry exists in the cache.
+
+    case download::DownloadItem::DownloadState::INTERRUPTED:
+    // Intentional fallthrough.
+    case download::DownloadItem::DownloadState::IN_PROGRESS: {
+      // Make sure the entry exists in the cache.
+      base::Optional<download::DownloadEntry> entry_opt =
+          in_progress_cache_->RetrieveEntry(download->GetGuid());
+      download::DownloadEntry entry;
+      if (!entry_opt.has_value()) {
+        entry = CreateDownloadEntryFromItem(*download);
+        in_progress_cache_->AddOrReplaceEntry(entry);
+        break;
+      }
+      entry = entry_opt.value();
       break;
+    }
+
     default:
       break;
   }
@@ -615,7 +640,7 @@
           in_progress_cache->RetrieveEntry(download->GetGuid());
       if (!entry_opt.has_value()) {
         in_progress_cache->AddOrReplaceEntry(
-            CreateDownloadEntryFromItem(*download));
+            CreateDownloadEntryFromItemImpl(*download));
       }
     }
 
@@ -748,7 +773,7 @@
           in_progress_cache->RetrieveEntry(download_item->GetGuid());
       if (!entry_opt.has_value()) {
         in_progress_cache->AddOrReplaceEntry(
-            CreateDownloadEntryFromItem(*download_item));
+            CreateDownloadEntryFromItemImpl(*download_item));
       }
     }
   }
diff --git a/content/browser/download/mock_download_file.cc b/content/browser/download/mock_download_file.cc
index b46ccb6f..4f88dde 100644
--- a/content/browser/download/mock_download_file.cc
+++ b/content/browser/download/mock_download_file.cc
@@ -14,17 +14,15 @@
 namespace content {
 namespace {
 
-void SuccessRun(const DownloadFile::InitializeCallback& initialize_callback) {
-  initialize_callback.Run(download::DOWNLOAD_INTERRUPT_REASON_NONE);
-}
-
 void PostSuccessRun(
-    const DownloadFile::InitializeCallback& initialize_callback,
+    DownloadFile::InitializeCallback initialize_callback,
     const DownloadFile::CancelRequestCallback& cancel_request_callback,
     const download::DownloadItem::ReceivedSlices& received_slices,
     bool is_parallelizable) {
-  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                          base::BindOnce(&SuccessRun, initialize_callback));
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::BindOnce(std::move(initialize_callback),
+                     download::DOWNLOAD_INTERRUPT_REASON_NONE, 0));
 }
 
 }  // namespace
diff --git a/content/browser/download/mock_download_file.h b/content/browser/download/mock_download_file.h
index 1fa088c..9890b53 100644
--- a/content/browser/download/mock_download_file.h
+++ b/content/browser/download/mock_download_file.h
@@ -28,10 +28,11 @@
   virtual ~MockDownloadFile();
 
   // DownloadFile functions.
+  // Using the legacy workaround for move-only types in mock methods.
   MOCK_METHOD4(
       Initialize,
-      void(const InitializeCallback&,
-           const CancelRequestCallback&,
+      void(InitializeCallback initialize_callback,
+           const CancelRequestCallback& cancel_request_callback,
            const download::DownloadItem::ReceivedSlices& received_slices,
            bool is_parallelizable));
   void AddInputStream(
diff --git a/content/browser/download/parallel_download_job.cc b/content/browser/download/parallel_download_job.cc
index de25ae9..9dfcc22 100644
--- a/content/browser/download/parallel_download_job.cc
+++ b/content/browser/download/parallel_download_job.cc
@@ -41,9 +41,11 @@
 ParallelDownloadJob::~ParallelDownloadJob() = default;
 
 void ParallelDownloadJob::OnDownloadFileInitialized(
-    const DownloadFile::InitializeCallback& callback,
-    download::DownloadInterruptReason result) {
-  DownloadJobImpl::OnDownloadFileInitialized(callback, result);
+    DownloadFile::InitializeCallback callback,
+    download::DownloadInterruptReason result,
+    int64_t bytes_wasted) {
+  DownloadJobImpl::OnDownloadFileInitialized(std::move(callback), result,
+                                             bytes_wasted);
   if (result == download::DOWNLOAD_INTERRUPT_REASON_NONE)
     BuildParallelRequestAfterDelay();
 }
diff --git a/content/browser/download/parallel_download_job.h b/content/browser/download/parallel_download_job.h
index 14d16be..799f5df 100644
--- a/content/browser/download/parallel_download_job.h
+++ b/content/browser/download/parallel_download_job.h
@@ -37,9 +37,9 @@
 
  protected:
   // DownloadJobImpl implementation.
-  void OnDownloadFileInitialized(
-      const DownloadFile::InitializeCallback& callback,
-      download::DownloadInterruptReason result) override;
+  void OnDownloadFileInitialized(DownloadFile::InitializeCallback callback,
+                                 download::DownloadInterruptReason result,
+                                 int64_t bytes_wasted) override;
 
   // Virtual for testing.
   virtual int GetParallelRequestCount() const;
diff --git a/content/browser/download/parallel_download_job_unittest.cc b/content/browser/download/parallel_download_job_unittest.cc
index 00aff74..d0164be 100644
--- a/content/browser/download/parallel_download_job_unittest.cc
+++ b/content/browser/download/parallel_download_job_unittest.cc
@@ -92,9 +92,10 @@
 
   ParallelDownloadJob::WorkerMap& workers() { return workers_; }
 
-  void MakeFileInitialized(const DownloadFile::InitializeCallback& callback,
+  void MakeFileInitialized(DownloadFile::InitializeCallback callback,
                            download::DownloadInterruptReason result) {
-    ParallelDownloadJob::OnDownloadFileInitialized(callback, result);
+    ParallelDownloadJob::OnDownloadFileInitialized(std::move(callback), result,
+                                                   0);
   }
 
   int GetParallelRequestCount() const override { return request_count_; }
@@ -191,7 +192,8 @@
     EXPECT_EQ(length, job_->workers_[offset]->length());
   }
 
-  void OnFileInitialized(download::DownloadInterruptReason result) {
+  void OnFileInitialized(download::DownloadInterruptReason result,
+                         int64_t bytes_wasted) {
     file_initialized_ = true;
   }
 
@@ -524,7 +526,7 @@
 
   // Start to build the requests without any error.
   base::MockCallback<DownloadFile::InitializeCallback> callback;
-  EXPECT_CALL(callback, Run(_)).Times(1);
+  EXPECT_CALL(callback, Run(_, _)).Times(1);
   job_->MakeFileInitialized(callback.Get(),
                             download::DOWNLOAD_INTERRUPT_REASON_NONE);
 
diff --git a/content/browser/download/save_file.cc b/content/browser/download/save_file.cc
index 1d4140a..cbb514a 100644
--- a/content/browser/download/save_file.cc
+++ b/content/browser/download/save_file.cc
@@ -4,7 +4,9 @@
 
 #include "content/browser/download/save_file.h"
 
+#include "base/bind.h"
 #include "base/logging.h"
+#include "base/optional.h"
 #include "components/download/public/common/download_item.h"
 #include "components/download/public/common/download_task_runner.h"
 
@@ -28,10 +30,12 @@
 }
 
 download::DownloadInterruptReason SaveFile::Initialize() {
+  int64_t bytes_wasted = 0;
   download::DownloadInterruptReason reason = file_.Initialize(
       /*full_path=*/base::FilePath(), /*default_directory=*/base::FilePath(),
       /*file=*/base::File(), /*bytes_so_far=*/0, /*hash_so_far=*/std::string(),
-      /*hash_state=*/nullptr, /*is_sparse_file=*/false);
+      /*hash_state=*/nullptr, /*is_sparse_file=*/false,
+      /*bytes_wasted*/ &bytes_wasted);
   info_->path = FullPath();
   return reason;
 }
diff --git a/content/browser/gpu/compositor_util.cc b/content/browser/gpu/compositor_util.cc
index 63705c8f..a24431f 100644
--- a/content/browser/gpu/compositor_util.cc
+++ b/content/browser/gpu/compositor_util.cc
@@ -265,17 +265,6 @@
   return false;
 }
 
-bool IsCompositorImageAnimationEnabled() {
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableCompositorImageAnimations))
-    return true;
-
-  if (base::FeatureList::IsEnabled(features::kCompositorImageAnimation))
-    return true;
-
-  return false;
-}
-
 std::unique_ptr<base::DictionaryValue> GetFeatureStatus() {
   GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
   std::string gpu_access_blocked_reason;
diff --git a/content/browser/loader/loader_delegate.h b/content/browser/loader/loader_delegate.h
index 92d5f897..1484631 100644
--- a/content/browser/loader/loader_delegate.h
+++ b/content/browser/loader/loader_delegate.h
@@ -8,13 +8,12 @@
 #include <inttypes.h>
 
 #include <memory>
+#include <string>
 
 #include "content/common/content_export.h"
 #include "content/public/browser/resource_request_info.h"
 #include "net/base/load_states.h"
 
-class GURL;
-
 namespace content {
 
 // Delegate from loader to the rest of content. Should be interacted with on the
@@ -31,13 +30,11 @@
 
   // Notification that the load state for the given WebContents has changed.
   // NOTE: this method is called on the UI thread.
-  virtual void LoadStateChanged(
-      WebContents* web_contents,
-      const GURL& url,
-      const net::LoadStateWithParam& load_state,
-      uint64_t upload_position,
-      uint64_t upload_size) = 0;
-
+  virtual void LoadStateChanged(WebContents* web_contents,
+                                const std::string& host,
+                                const net::LoadStateWithParam& load_state,
+                                uint64_t upload_position,
+                                uint64_t upload_size) = 0;
 };
 
 }  // namespace content
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index ee88379..7cd7688 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -2330,8 +2330,7 @@
       PickMoreInterestingLoadInfos(std::move(infos));
   for (const auto& load_info: *info_map) {
     loader_delegate->LoadStateChanged(
-        load_info.first,
-        load_info.second.url, load_info.second.load_state,
+        load_info.first, load_info.second.host, load_info.second.load_state,
         load_info.second.upload_position, load_info.second.upload_size);
   }
 }
@@ -2340,7 +2339,7 @@
 std::unique_ptr<ResourceDispatcherHostImpl::LoadInfoMap>
 ResourceDispatcherHostImpl::PickMoreInterestingLoadInfos(
     std::unique_ptr<LoadInfoList> infos) {
-  std::unique_ptr<LoadInfoMap> info_map(new LoadInfoMap);
+  auto info_map = std::make_unique<LoadInfoMap>();
   for (const auto& load_info : *infos) {
     WebContents* web_contents = load_info.web_contents_getter.Run();
     if (!web_contents)
@@ -2366,7 +2365,7 @@
     LoadInfo load_info;
     load_info.web_contents_getter =
         loader.second->GetRequestInfo()->GetWebContentsGetterForRequest();
-    load_info.url = request->url();
+    load_info.host = request->url().host();
     load_info.load_state = request->GetLoadState();
     load_info.upload_size = upload_progress.size();
     load_info.upload_position = upload_progress.position();
diff --git a/content/browser/loader/resource_dispatcher_host_impl.h b/content/browser/loader/resource_dispatcher_host_impl.h
index a595aa1c..1b299ac 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.h
+++ b/content/browser/loader/resource_dispatcher_host_impl.h
@@ -375,7 +375,11 @@
     ~LoadInfo();
 
     ResourceRequestInfo::WebContentsGetter web_contents_getter;
-    GURL url;
+
+    // Comes directly from GURL::host() to avoid copying an entire GURL between
+    // threads.
+    std::string host;
+
     net::LoadStateWithParam load_state;
     uint64_t upload_position;
     uint64_t upload_size;
diff --git a/content/browser/loader/resource_dispatcher_host_unittest.cc b/content/browser/loader/resource_dispatcher_host_unittest.cc
index a7297fd9..3f10ec022 100644
--- a/content/browser/loader/resource_dispatcher_host_unittest.cc
+++ b/content/browser/loader/resource_dispatcher_host_unittest.cc
@@ -2346,24 +2346,24 @@
   info.web_contents_getter = base::Bind(WebContentsBinder, wc1);
   info.load_state = net::LoadStateWithParam(net::LOAD_STATE_SENDING_REQUEST,
                                             base::string16());
-  info.url = GURL("test://1/");
+  info.host = "a.com";
   info.upload_position = 0;
   info.upload_size = 0;
   infos->push_back(info);
 
-  info.url = GURL("test://2/");
+  info.host = "b.com";
   info.load_state = net::LoadStateWithParam(net::LOAD_STATE_READING_RESPONSE,
                                             base::string16());
   infos->push_back(info);
 
-  info.url = GURL("test://3/");
+  info.host = "c.com";
 
   std::unique_ptr<LoadInfoMap> load_info_map =
       ResourceDispatcherHostImpl::PickMoreInterestingLoadInfos(
           std::move(infos));
   ASSERT_EQ(1u, load_info_map->size());
   ASSERT_TRUE(load_info_map->find(wc1) != load_info_map->end());
-  EXPECT_EQ(GURL("test://2/"), (*load_info_map)[wc1].url);
+  EXPECT_EQ("b.com", (*load_info_map)[wc1].host);
   EXPECT_EQ(net::LOAD_STATE_READING_RESPONSE,
             (*load_info_map)[wc1].load_state.state);
   EXPECT_EQ(0u, (*load_info_map)[wc1].upload_position);
@@ -2379,12 +2379,12 @@
   info.web_contents_getter = base::Bind(WebContentsBinder, wc1);
   info.load_state = net::LoadStateWithParam(net::LOAD_STATE_IDLE,
                                             base::string16());
-  info.url = GURL("test://1/");
+  info.host = "a.com";
   info.upload_position = 0;
   info.upload_size = 0;
   infos->push_back(info);
 
-  info.url = GURL("test://2/");
+  info.host = "b.com";
   infos->push_back(info);
 
   std::unique_ptr<LoadInfoMap> load_info_map =
@@ -2392,7 +2392,7 @@
           std::move(infos));
   ASSERT_EQ(1u, load_info_map->size());
   ASSERT_TRUE(load_info_map->find(wc1) != load_info_map->end());
-  EXPECT_EQ(GURL("test://1/"), (*load_info_map)[wc1].url);
+  EXPECT_EQ("a.com", (*load_info_map)[wc1].host);
   EXPECT_EQ(net::LOAD_STATE_IDLE, (*load_info_map)[wc1].load_state.state);
   EXPECT_EQ(0u, (*load_info_map)[wc1].upload_position);
   EXPECT_EQ(0u, (*load_info_map)[wc1].upload_size);
@@ -2406,7 +2406,7 @@
   info.web_contents_getter = base::Bind(WebContentsBinder, wc1);
   info.load_state = net::LoadStateWithParam(net::LOAD_STATE_READING_RESPONSE,
                                             base::string16());
-  info.url = GURL("test://1/");
+  info.host = "a.com";
   info.upload_position = 0;
   info.upload_size = 0;
   infos->push_back(info);
@@ -2415,21 +2415,21 @@
   info.upload_size = 1000;
   infos->push_back(info);
 
-  info.url = GURL("test://2/");
+  info.host = "b.com";
   info.load_state = net::LoadStateWithParam(net::LOAD_STATE_SENDING_REQUEST,
                                             base::string16());
   info.upload_position = 50;
   info.upload_size = 100;
   infos->push_back(info);
 
-  info.url = GURL("test://1/");
+  info.host = "a.com";
   info.load_state = net::LoadStateWithParam(net::LOAD_STATE_READING_RESPONSE,
                                             base::string16());
   info.upload_position = 1000;
   info.upload_size = 1000;
   infos->push_back(info);
 
-  info.url = GURL("test://3/");
+  info.host = "c.com";
   info.upload_position = 0;
   info.upload_size = 0;
   infos->push_back(info);
@@ -2439,7 +2439,7 @@
           std::move(infos));
   ASSERT_EQ(1u, load_info_map->size());
   ASSERT_TRUE(load_info_map->find(wc1) != load_info_map->end());
-  EXPECT_EQ(GURL("test://2/"), (*load_info_map)[wc1].url);
+  EXPECT_EQ("b.com", (*load_info_map)[wc1].host);
   EXPECT_EQ(net::LOAD_STATE_SENDING_REQUEST,
             (*load_info_map)[wc1].load_state.state);
   EXPECT_EQ(50u, (*load_info_map)[wc1].upload_position);
@@ -2456,7 +2456,7 @@
   info.web_contents_getter = base::Bind(WebContentsBinder, wc1);
   info.load_state = net::LoadStateWithParam(net::LOAD_STATE_CONNECTING,
                                             base::string16());
-  info.url = GURL("test://1/");
+  info.host = "a.com";
   info.upload_position = 0;
   info.upload_size = 0;
   infos->push_back(info);
@@ -2465,17 +2465,17 @@
   info.web_contents_getter = base::Bind(WebContentsBinder, wc2);
   info.load_state = net::LoadStateWithParam(net::LOAD_STATE_IDLE,
                                             base::string16());
-  info.url = GURL("test://2/");
+  info.host = "b.com";
   infos->push_back(info);
 
   info.web_contents_getter = base::Bind(WebContentsBinder, wc1);
-  info.url = GURL("test://3/");
+  info.host = "c.com";
   infos->push_back(info);
 
   info.web_contents_getter = base::Bind(WebContentsBinder, wc2);
   info.load_state = net::LoadStateWithParam(net::LOAD_STATE_CONNECTING,
                                             base::string16());
-  info.url = GURL("test://4/");
+  info.host = "d.com";
   infos->push_back(info);
 
   std::unique_ptr<LoadInfoMap> load_info_map =
@@ -2484,14 +2484,14 @@
   ASSERT_EQ(2u, load_info_map->size());
 
   ASSERT_TRUE(load_info_map->find(wc1) != load_info_map->end());
-  EXPECT_EQ(GURL("test://1/"), (*load_info_map)[wc1].url);
+  EXPECT_EQ("a.com", (*load_info_map)[wc1].host);
   EXPECT_EQ(net::LOAD_STATE_CONNECTING,
             (*load_info_map)[wc1].load_state.state);
   EXPECT_EQ(0u, (*load_info_map)[wc1].upload_position);
   EXPECT_EQ(0u, (*load_info_map)[wc1].upload_size);
 
   ASSERT_TRUE(load_info_map->find(wc2) != load_info_map->end());
-  EXPECT_EQ(GURL("test://4/"), (*load_info_map)[wc2].url);
+  EXPECT_EQ("d.com", (*load_info_map)[wc2].host);
   EXPECT_EQ(net::LOAD_STATE_CONNECTING,
             (*load_info_map)[wc2].load_state.state);
   EXPECT_EQ(0u, (*load_info_map)[wc2].upload_position);
diff --git a/content/browser/loader_delegate_impl.cc b/content/browser/loader_delegate_impl.cc
index ec35a938..ae788cdb 100644
--- a/content/browser/loader_delegate_impl.cc
+++ b/content/browser/loader_delegate_impl.cc
@@ -20,13 +20,13 @@
 
 void LoaderDelegateImpl::LoadStateChanged(
     WebContents* web_contents,
-    const GURL& url,
+    const std::string& host,
     const net::LoadStateWithParam& load_state,
     uint64_t upload_position,
     uint64_t upload_size) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  static_cast<WebContentsImpl*>(web_contents)->LoadStateChanged(
-      url, load_state, upload_position, upload_size);
+  static_cast<WebContentsImpl*>(web_contents)
+      ->LoadStateChanged(host, load_state, upload_position, upload_size);
 }
 
 }  // namespace content
diff --git a/content/browser/loader_delegate_impl.h b/content/browser/loader_delegate_impl.h
index 30cf93a..732de80 100644
--- a/content/browser/loader_delegate_impl.h
+++ b/content/browser/loader_delegate_impl.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_BROWSER_LOADER_DELEGATE_IMPL_H_
 #define CONTENT_BROWSER_LOADER_DELEGATE_IMPL_H_
 
+#include <string>
+
 #include "content/browser/loader/loader_delegate.h"
 #include "content/common/content_export.h"
 
@@ -15,12 +17,11 @@
   ~LoaderDelegateImpl() override;
 
   // LoaderDelegate implementation:
-  void LoadStateChanged(
-      WebContents* web_contents,
-      const GURL& url,
-      const net::LoadStateWithParam& load_state,
-      uint64_t upload_position,
-      uint64_t upload_size) override;
+  void LoadStateChanged(WebContents* web_contents,
+                        const std::string& host,
+                        const net::LoadStateWithParam& load_state,
+                        uint64_t upload_position,
+                        uint64_t upload_size) override;
 };
 
 }  // namespace content
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc
index 107932a..dc5721b 100644
--- a/content/browser/media/session/media_session_impl.cc
+++ b/content/browser/media/session/media_session_impl.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 
 #include "base/memory/ptr_util.h"
+#include "base/numerics/ranges.h"
 #include "content/browser/media/session/audio_focus_delegate.h"
 #include "content/browser/media/session/media_session_controller.h"
 #include "content/browser/media/session/media_session_player_observer.h"
@@ -29,8 +30,8 @@
 
 namespace {
 
-const double kDefaultVolumeMultiplier = 1.0;
-const double kDuckingVolumeMultiplier = 0.2;
+const double kUnduckedVolumeMultiplier = 1.0;
+const double kDefaultDuckingVolumeMultiplier = 0.2;
 
 using MapRenderFrameHostToDepth = std::map<RenderFrameHost*, size_t>;
 
@@ -423,6 +424,10 @@
   return !IsActive();
 }
 
+void MediaSessionImpl::SetDuckingVolumeMultiplier(double multiplier) {
+  ducking_volume_multiplier_ = base::ClampToRange(multiplier, 0.0, 1.0);
+}
+
 void MediaSessionImpl::StartDucking() {
   if (is_ducking_)
     return;
@@ -445,7 +450,7 @@
 }
 
 double MediaSessionImpl::GetVolumeMultiplier() const {
-  return is_ducking_ ? kDuckingVolumeMultiplier : kDefaultVolumeMultiplier;
+  return is_ducking_ ? ducking_volume_multiplier_ : kUnduckedVolumeMultiplier;
 }
 
 bool MediaSessionImpl::IsActive() const {
@@ -532,7 +537,8 @@
   }
 
   for (const auto& it : pepper_players_)
-    it.observer->OnSetVolumeMultiplier(it.player_id, kDuckingVolumeMultiplier);
+    it.observer->OnSetVolumeMultiplier(it.player_id,
+                                       ducking_volume_multiplier_);
 
   NotifyAboutStateChange();
 }
@@ -558,6 +564,7 @@
       audio_focus_type_(
           AudioFocusManager::AudioFocusType::GainTransientMayDuck),
       is_ducking_(false),
+      ducking_volume_multiplier_(kDefaultDuckingVolumeMultiplier),
       routed_service_(nullptr) {
 #if defined(OS_ANDROID)
   session_android_.reset(new MediaSessionAndroid(this));
@@ -713,7 +720,7 @@
     for (const auto& player : pepper_players_) {
       if (player.observer->render_frame_host() != rfh_of_routed_service) {
         player.observer->OnSetVolumeMultiplier(player.player_id,
-                                               kDuckingVolumeMultiplier);
+                                               ducking_volume_multiplier_);
       }
     }
     for (const auto& player : one_shot_players_) {
diff --git a/content/browser/media/session/media_session_impl.h b/content/browser/media/session/media_session_impl.h
index 0bcdca3..3a681b8b 100644
--- a/content/browser/media/session/media_session_impl.h
+++ b/content/browser/media/session/media_session_impl.h
@@ -141,6 +141,9 @@
   // MediaSessionService declared state and guessed state (audio_focus_state_).
   CONTENT_EXPORT bool IsActuallyPaused() const override;
 
+  // Set the volume multiplier applied during ducking.
+  CONTENT_EXPORT void SetDuckingVolumeMultiplier(double multiplier) override;
+
   // Let the media session start ducking such that the volume multiplier is
   // reduced.
   CONTENT_EXPORT void StartDucking() override;
@@ -302,6 +305,8 @@
   // StopDucking().
   bool is_ducking_;
 
+  double ducking_volume_multiplier_;
+
   base::CallbackList<void(State)> media_session_state_listeners_;
 
   base::ObserverList<MediaSessionObserver> observers_;
diff --git a/content/browser/media/session/media_session_impl_browsertest.cc b/content/browser/media/session/media_session_impl_browsertest.cc
index 7ffbda71..df48df6df 100644
--- a/content/browser/media/session/media_session_impl_browsertest.cc
+++ b/content/browser/media/session/media_session_impl_browsertest.cc
@@ -44,6 +44,7 @@
 
 const double kDefaultVolumeMultiplier = 1.0;
 const double kDuckingVolumeMultiplier = 0.2;
+const double kDifferentDuckingVolumeMultiplier = 0.018;
 
 class MockAudioFocusDelegate : public AudioFocusDelegate {
  public:
@@ -314,6 +315,23 @@
   EXPECT_EQ(kDefaultVolumeMultiplier, player_observer->GetVolumeMultiplier(2));
 }
 
+IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest,
+                       DuckingUsesConfiguredMultiplier) {
+  auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>();
+
+  StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent);
+  StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent);
+  media_session_->SetDuckingVolumeMultiplier(kDifferentDuckingVolumeMultiplier);
+  SystemStartDucking();
+  EXPECT_EQ(kDifferentDuckingVolumeMultiplier,
+            player_observer->GetVolumeMultiplier(0));
+  EXPECT_EQ(kDifferentDuckingVolumeMultiplier,
+            player_observer->GetVolumeMultiplier(1));
+  SystemStopDucking();
+  EXPECT_EQ(kDefaultVolumeMultiplier, player_observer->GetVolumeMultiplier(0));
+  EXPECT_EQ(kDefaultVolumeMultiplier, player_observer->GetVolumeMultiplier(1));
+}
+
 IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, AudioFocusInitialState) {
   EXPECT_FALSE(IsActive());
 }
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index 2437c39..216d224 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -154,12 +154,8 @@
     request->set_area(src_subrect);
   }
 
-  // If VIZ display compositing is disabled, the request will be issued directly
-  // to CompositorFrameSinkSupport, which requires Surface pixel coordinates.
-  if (!enable_viz_) {
-    request->set_area(
-        gfx::ScaleToRoundedRect(request->area(), active_device_scale_factor_));
-  }
+  request->set_area(
+      gfx::ScaleToRoundedRect(request->area(), active_device_scale_factor_));
 
   if (!output_size.IsEmpty()) {
     request->set_result_selection(gfx::Rect(output_size));
@@ -168,12 +164,8 @@
         gfx::Vector2d(output_size.width(), output_size.height()));
   }
 
-  if (enable_viz_) {
-    client_->DelegatedFrameHostGetLayer()->RequestCopyOfOutput(
-        std::move(request));
-  } else {
-    support_->RequestCopyOfSurface(std::move(request));
-  }
+  GetHostFrameSinkManager()->RequestCopyOfOutput(frame_sink_id_,
+                                                 std::move(request));
 }
 
 bool DelegatedFrameHost::CanCopyFromCompositingSurface() const {
diff --git a/content/browser/renderer_host/input/interaction_mq_dynamic_browsertest.cc b/content/browser/renderer_host/input/interaction_mq_dynamic_browsertest.cc
index 6a1c8ff..76a8d8c 100644
--- a/content/browser/renderer_host/input/interaction_mq_dynamic_browsertest.cc
+++ b/content/browser/renderer_host/input/interaction_mq_dynamic_browsertest.cc
@@ -31,11 +31,16 @@
     (defined(OS_ANDROID) && !defined(ADDRESS_SANITIZER))
 IN_PROC_BROWSER_TEST_F(InteractionMediaQueriesDynamicTest,
                        PointerMediaQueriesDynamic) {
+  RenderViewHost* rvh = shell()->web_contents()->GetRenderViewHost();
+  ui::SetAvailablePointerAndHoverTypesForTesting(ui::POINTER_TYPE_NONE,
+                                                 ui::HOVER_TYPE_NONE);
+  rvh->OnWebkitPreferencesChanged();
+
   GURL test_url = GetTestUrl("", "interaction-mq-dynamic.html");
   const base::string16 kSuccessTitle(base::ASCIIToUTF16("SUCCESS"));
   TitleWatcher title_watcher(shell()->web_contents(), kSuccessTitle);
   NavigateToURL(shell(), test_url);
-  RenderViewHost* rvh = shell()->web_contents()->GetRenderViewHost();
+
   ui::SetAvailablePointerAndHoverTypesForTesting(ui::POINTER_TYPE_COARSE,
                                                  ui::HOVER_TYPE_HOVER);
   rvh->OnWebkitPreferencesChanged();
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index d9b5be1..b6fc81ef1 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2455,9 +2455,6 @@
   if (IsCheckerImagingEnabled())
     command_line->AppendSwitch(cc::switches::kEnableCheckerImaging);
 
-  if (IsCompositorImageAnimationEnabled())
-    command_line->AppendSwitch(switches::kEnableCompositorImageAnimations);
-
   // Slimming Paint v2 implies layer lists in the renderer.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableSlimmingPaintV2)) {
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.cc b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
index 6b4af1c..4b89eee 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
@@ -874,7 +874,8 @@
         gfx::Vector2d(output_size.width(), output_size.height()));
   }
 
-  support_->RequestCopyOfSurface(std::move(request));
+  GetHostFrameSinkManager()->RequestCopyOfOutput(frame_sink_id_,
+                                                 std::move(request));
 }
 
 void RenderWidgetHostViewChildFrame::ReclaimResources(
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index b10a727..8da1bff 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -601,20 +601,10 @@
       &div_scroll_top_start));
   EXPECT_EQ(0.0, div_scroll_top_start);
 
-  // Wait until renderer's compositor thread is synced. Otherwise the event
-  // handler won't be installed when the event arrives.
-  MainThreadFrameObserver observer(rwhv_root->GetRenderWidgetHost());
+  // Wait until renderer's compositor thread is synced. Otherwise the non fast
+  // scrollable regions won't be set when the event arrives.
+  MainThreadFrameObserver observer(rwhv_nested->GetRenderWidgetHost());
   observer.Wait();
-  {
-    base::RunLoop run_loop;
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE, run_loop.QuitClosure(),
-        // tiny_timeout() is too small to run without flakes, but
-        // action_timeout() is 100 times bigger, which is overkill. We use a
-        // custom delay here to achieve a balance.
-        base::TimeDelta::FromMilliseconds(1000));
-    run_loop.Run();
-  }
 
   // Send a wheel to scroll the div.
   gfx::Point location(point_f.x(), point_f.y());
@@ -624,11 +614,11 @@
                                2);  // This must be '2' or it gets silently
                                     // dropped.
   UpdateEventRootLocation(&scroll_event, rwhv_root);
-  rwhv_root->OnScrollEvent(&scroll_event);
 
   InputEventAckWaiter ack_observer(
       parent_iframe_node->current_frame_host()->GetRenderWidgetHost(),
       blink::WebInputEvent::kGestureScrollUpdate);
+  rwhv_root->OnScrollEvent(&scroll_event);
   ack_observer.Wait();
 
   // Check compositor layers.
@@ -759,21 +749,6 @@
                                     &div_scroll_top_start));
   EXPECT_EQ(0.0, div_scroll_top_start);
 
-  // Wait until renderer's compositor thread is synced. Otherwise the event
-  // handler won't be installed when the event arrives.
-  MainThreadFrameObserver observer(rwhv_root->GetRenderWidgetHost());
-  observer.Wait();
-  {
-    base::RunLoop run_loop;
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE, run_loop.QuitClosure(),
-        // tiny_timeout() is too small to run without flakes, but
-        // action_timeout() is 100 times bigger, which is overkill. We use a
-        // custom delay here to achieve a balance.
-        base::TimeDelta::FromMilliseconds(1000));
-    run_loop.Run();
-  }
-
   // Send a wheel to scroll the parent containing the div.
   gfx::Point location(point_f.x(), point_f.y());
   ui::ScrollEvent scroll_event(ui::ET_SCROLL, location, ui::EventTimeForNow(),
@@ -782,13 +757,16 @@
                                2);  // This must be '2' or it gets silently
                                     // dropped.
   UpdateEventRootLocation(&scroll_event, rwhv_root);
-  rwhv_root->OnScrollEvent(&scroll_event);
 
   InputEventAckWaiter ack_observer(
       parent_iframe_node->current_frame_host()->GetRenderWidgetHost(),
       blink::WebInputEvent::kGestureScrollUpdate);
+  rwhv_root->OnScrollEvent(&scroll_event);
   ack_observer.Wait();
 
+  MainThreadFrameObserver thread_observer(rwhv_parent->GetRenderWidgetHost());
+  thread_observer.Wait();
+
   // Check compositor layers.
   EXPECT_TRUE(ExecuteScriptAndExtractString(
       nested_iframe_node->current_frame_host(),
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 13c64464..13c4ff4 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3417,22 +3417,22 @@
 }
 
 void WebContentsImpl::LoadStateChanged(
-    const GURL& url,
+    const std::string& host,
     const net::LoadStateWithParam& load_state,
     uint64_t upload_position,
     uint64_t upload_size) {
-  base::string16 host = url_formatter::IDNToUnicode(url.host());
+  base::string16 host16 = url_formatter::IDNToUnicode(host);
   // Drop no-op updates.
   if (load_state_.state == load_state.state &&
       load_state_.param == load_state.param &&
       upload_position_ == upload_position && upload_size_ == upload_size &&
-      load_state_host_ == host) {
+      load_state_host_ == host16) {
     return;
   }
   load_state_ = load_state;
   upload_position_ = upload_position;
   upload_size_ = upload_size;
-  load_state_host_ = host;
+  load_state_host_ = host16;
   if (load_state_.state == net::LOAD_STATE_READING_RESPONSE)
     SetNotWaitingForResponse();
   if (IsLoading()) {
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 6095c92..b7680da 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -206,7 +206,7 @@
                          RenderWidgetHost* source_rwh);
 
   // Notification that the RenderViewHost's load state changed.
-  void LoadStateChanged(const GURL& url,
+  void LoadStateChanged(const std::string& host,
                         const net::LoadStateWithParam& load_state,
                         uint64_t upload_position,
                         uint64_t upload_size);
diff --git a/content/browser/webauth/authenticator_impl.cc b/content/browser/webauth/authenticator_impl.cc
index 26da4381..4ccb3216 100644
--- a/content/browser/webauth/authenticator_impl.cc
+++ b/content/browser/webauth/authenticator_impl.cc
@@ -220,7 +220,8 @@
 
 webauth::mojom::GetAssertionAuthenticatorResponsePtr CreateGetAssertionResponse(
     const std::string& client_data_json,
-    device::SignResponseData response_data) {
+    device::SignResponseData response_data,
+    bool echo_appid_extension) {
   auto response = webauth::mojom::GetAssertionAuthenticatorResponse::New();
   auto common_info = webauth::mojom::CommonCredentialInfo::New();
   common_info->client_data_json.assign(client_data_json.begin(),
@@ -231,6 +232,7 @@
   response->authenticator_data = response_data.GetAuthenticatorDataBytes();
   response->signature = response_data.signature();
   response->user_handle.emplace();
+  response->echo_appid_extension = echo_appid_extension;
   return response;
 }
 
@@ -456,6 +458,8 @@
     }
 
     alternative_application_parameter = std::move(appid_hash);
+    // TODO(agl): needs a test once a suitable, mock U2F device exists.
+    echo_appid_extension_ = true;
   }
 
   DCHECK(get_assertion_response_callback_.is_null());
@@ -581,7 +585,8 @@
           std::move(get_assertion_response_callback_),
           webauth::mojom::AuthenticatorStatus::SUCCESS,
           CreateGetAssertionResponse(std::move(client_data_json_),
-                                     std::move(*response_data)));
+                                     std::move(*response_data),
+                                     echo_appid_extension_));
       return;
   }
   NOTREACHED();
@@ -625,6 +630,7 @@
   make_credential_response_callback_.Reset();
   get_assertion_response_callback_.Reset();
   client_data_json_.clear();
+  echo_appid_extension_ = false;
 }
 
 }  // namespace content
diff --git a/content/browser/webauth/authenticator_impl.h b/content/browser/webauth/authenticator_impl.h
index edadc614b..c372fb59 100644
--- a/content/browser/webauth/authenticator_impl.h
+++ b/content/browser/webauth/authenticator_impl.h
@@ -137,6 +137,12 @@
   std::unique_ptr<base::OneShotTimer> timer_;
   RenderFrameHost* render_frame_host_;
   service_manager::Connector* connector_ = nullptr;
+
+  // Whether or not a GetAssertion call should return a PublicKeyCredential
+  // instance whose getClientExtensionResults() method yields a
+  // AuthenticationExtensions dictionary that contains the `appid: true`
+  // extension output.
+  bool echo_appid_extension_ = false;
   base::WeakPtrFactory<AuthenticatorImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AuthenticatorImpl);
diff --git a/content/public/browser/media_session.h b/content/public/browser/media_session.h
index 1a5ce98..12a6468 100644
--- a/content/public/browser/media_session.h
+++ b/content/public/browser/media_session.h
@@ -70,6 +70,9 @@
   // Tell the media session a user action has performed.
   virtual void DidReceiveAction(blink::mojom::MediaSessionAction action) = 0;
 
+  // Set the volume multiplier applied during ducking.
+  virtual void SetDuckingVolumeMultiplier(double multiplier) = 0;
+
   // Let the media session start ducking such that the volume multiplier is
   // reduced.
   virtual void StartDucking() = 0;
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index ca48bc8..1dbc2df 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -76,9 +76,6 @@
 const base::Feature kCompositeOpaqueScrollers{"CompositeOpaqueScrollers",
                                               base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kCompositorImageAnimation{"CompositorImageAnimation",
-                                              base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables handling touch events in compositor using impl side touch action
 // knowledge.
 const base::Feature kCompositorTouchAction{"CompositorTouchAction",
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 309f3ce..a6fddc81 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -29,7 +29,6 @@
 CONTENT_EXPORT extern const base::Feature kCodeCacheAfterExecute;
 CONTENT_EXPORT extern const base::Feature kCompositeOpaqueFixedPosition;
 CONTENT_EXPORT extern const base::Feature kCompositeOpaqueScrollers;
-CONTENT_EXPORT extern const base::Feature kCompositorImageAnimation;
 CONTENT_EXPORT extern const base::Feature kCompositorTouchAction;
 CONTENT_EXPORT extern const base::Feature kCrossSiteDocumentBlockingAlways;
 CONTENT_EXPORT extern const base::Feature kCrossSiteDocumentBlockingIfIsolating;
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 0231c38..e46385b3 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -353,10 +353,6 @@
 // features.
 const char kEnableBlinkFeatures[]           = "enable-blink-features";
 
-// Enable animating of images in the compositor instead of blink.
-const char kEnableCompositorImageAnimations[] =
-    "enable-compositor-image-animations";
-
 // Enable experimental canvas features, e.g. canvas 2D context attributes
 const char kEnableExperimentalCanvasFeatures[] =
     "enable-experimental-canvas-features";
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 04c0cc0..66f9491 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -112,7 +112,6 @@
 CONTENT_EXPORT extern const char kEnablePreferCompositingToLCDText[];
 CONTENT_EXPORT extern const char kEnableBlinkFeatures[];
 CONTENT_EXPORT extern const char kEnableBackgroundFetchPersistence[];
-CONTENT_EXPORT extern const char kEnableCompositorImageAnimations[];
 CONTENT_EXPORT extern const char kEnableDisplayList2dCanvas[];
 CONTENT_EXPORT extern const char kEnableDistanceFieldText[];
 CONTENT_EXPORT extern const char kEnableExperimentalCanvasFeatures[];
diff --git a/content/public/test/fake_download_item.cc b/content/public/test/fake_download_item.cc
index 53d7755..374e95b 100644
--- a/content/public/test/fake_download_item.cc
+++ b/content/public/test/fake_download_item.cc
@@ -258,6 +258,11 @@
   return false;
 }
 
+int64_t FakeDownloadItem::GetBytesWasted() const {
+  NOTREACHED();
+  return 0;
+}
+
 const GURL& FakeDownloadItem::GetReferrerUrl() const {
   NOTREACHED();
   return dummy_url;
diff --git a/content/public/test/fake_download_item.h b/content/public/test/fake_download_item.h
index 00521233..31c1f560 100644
--- a/content/public/test/fake_download_item.h
+++ b/content/public/test/fake_download_item.h
@@ -114,6 +114,7 @@
   bool IsPaused() const override;
   bool IsTemporary() const override;
   bool CanResume() const override;
+  int64_t GetBytesWasted() const override;
   const GURL& GetReferrerUrl() const override;
   const GURL& GetSiteUrl() const override;
   const GURL& GetTabUrl() const override;
diff --git a/content/public/test/mock_download_item.h b/content/public/test/mock_download_item.h
index b14c4141..cf9c2f6 100644
--- a/content/public/test/mock_download_item.h
+++ b/content/public/test/mock_download_item.h
@@ -56,6 +56,7 @@
   MOCK_CONST_METHOD0(IsTemporary, bool());
   MOCK_CONST_METHOD0(CanResume, bool());
   MOCK_CONST_METHOD0(IsDone, bool());
+  MOCK_CONST_METHOD0(GetBytesWasted, int64_t());
   MOCK_CONST_METHOD0(GetURL, const GURL&());
   MOCK_CONST_METHOD0(GetUrlChain, const std::vector<GURL>&());
   MOCK_CONST_METHOD0(GetOriginalUrl, const GURL&());
diff --git a/content/public/test/test_file_error_injector.cc b/content/public/test/test_file_error_injector.cc
index 85029e89..f665d7b 100644
--- a/content/public/test/test_file_error_injector.cc
+++ b/content/public/test/test_file_error_injector.cc
@@ -42,7 +42,7 @@
 
   ~DownloadFileWithError() override;
 
-  void Initialize(const InitializeCallback& initialize_callback,
+  void Initialize(InitializeCallback initialize_callback,
                   const CancelRequestCallback& cancel_request_callback,
                   const download::DownloadItem::ReceivedSlices& received_slices,
                   bool is_parallelizable) override;
@@ -89,10 +89,11 @@
 };
 
 static void InitializeErrorCallback(
-    const DownloadFile::InitializeCallback original_callback,
+    DownloadFile::InitializeCallback original_callback,
     download::DownloadInterruptReason overwrite_error,
-    download::DownloadInterruptReason original_error) {
-  original_callback.Run(overwrite_error);
+    download::DownloadInterruptReason original_error,
+    int64_t bytes_wasted) {
+  std::move(original_callback).Run(overwrite_error, bytes_wasted);
 }
 
 static void RenameErrorCallback(
@@ -138,13 +139,13 @@
 }
 
 void DownloadFileWithError::Initialize(
-    const InitializeCallback& initialize_callback,
+    InitializeCallback initialize_callback,
     const CancelRequestCallback& cancel_request_callback,
     const download::DownloadItem::ReceivedSlices& received_slices,
     bool is_parallelizable) {
   download::DownloadInterruptReason error_to_return =
       download::DOWNLOAD_INTERRUPT_REASON_NONE;
-  InitializeCallback callback_to_use = initialize_callback;
+  InitializeCallback callback_to_use = std::move(initialize_callback);
 
   // Replace callback if the error needs to be overwritten.
   if (OverwriteError(
@@ -155,17 +156,18 @@
       // return the error.
       BrowserThread::PostTask(
           BrowserThread::UI, FROM_HERE,
-          base::BindOnce(initialize_callback, error_to_return));
+          base::BindOnce(std::move(callback_to_use), error_to_return, 0));
       return;
     }
 
     // Otherwise, just wrap the return.
-    callback_to_use = base::Bind(&InitializeErrorCallback, initialize_callback,
-                                 error_to_return);
+    callback_to_use = base::BindRepeating(
+        &InitializeErrorCallback, std::move(callback_to_use), error_to_return);
   }
 
-  DownloadFileImpl::Initialize(callback_to_use, cancel_request_callback,
-                               received_slices, is_parallelizable);
+  DownloadFileImpl::Initialize(std::move(callback_to_use),
+                               cancel_request_callback, received_slices,
+                               is_parallelizable);
 }
 
 download::DownloadInterruptReason DownloadFileWithError::WriteDataToFile(
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index 961a617..28b4002 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -595,8 +595,6 @@
     settings.enable_latency_recovery = false;
   }
 
-  settings.enable_image_animations =
-      cmd.HasSwitch(switches::kEnableCompositorImageAnimations);
   settings.enable_image_animation_resync =
       !cmd.HasSwitch(switches::kDisableImageAnimationResync);
 
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index bbf9808c..1c0d456 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -74,11 +74,6 @@
 #include "media/remoting/renderer_controller.h"         // nogncheck
 #endif
 
-#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
-#include "content/renderer/media/cdm/pepper_cdm_wrapper_impl.h"
-#include "content/renderer/media/cdm/render_cdm_factory.h"
-#endif
-
 namespace {
 class FrameFetchContext : public media::ResourceFetchContext {
  public:
@@ -520,25 +515,12 @@
 #endif
 
 media::CdmFactory* MediaFactory::GetCdmFactory() {
-  blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame();
-  DCHECK(web_frame);
-
   if (cdm_factory_)
     return cdm_factory_.get();
 
-#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
-  static_assert(
-      BUILDFLAG(ENABLE_MOJO_CDM),
-      "Mojo CDM should always be enabled when library CDM is enabled");
-  if (base::FeatureList::IsEnabled(media::kMojoCdm)) {
-    cdm_factory_.reset(new media::MojoCdmFactory(GetMediaInterfaceFactory()));
-  } else {
-    cdm_factory_.reset(new RenderCdmFactory(
-        base::Bind(&PepperCdmWrapperImpl::Create, web_frame)));
-  }
-#elif BUILDFLAG(ENABLE_MOJO_CDM)
+#if BUILDFLAG(ENABLE_MOJO_CDM)
   cdm_factory_.reset(new media::MojoCdmFactory(GetMediaInterfaceFactory()));
-#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+#endif  // BUILDFLAG(ENABLE_MOJO_CDM)
 
   return cdm_factory_.get();
 }
diff --git a/content/renderer/mus/renderer_window_tree_client.cc b/content/renderer/mus/renderer_window_tree_client.cc
index 6e55fdf..bffe1f3 100644
--- a/content/renderer/mus/renderer_window_tree_client.cc
+++ b/content/renderer/mus/renderer_window_tree_client.cc
@@ -336,27 +336,26 @@
 void RendererWindowTreeClient::OnDragDropStart(
     const std::unordered_map<std::string, std::vector<uint8_t>>& mime_data) {}
 
-void RendererWindowTreeClient::OnDragEnter(
-    ui::Id window_id,
-    uint32_t event_flags,
-    const gfx::Point& position,
-    uint32_t effect_bitmask,
-    const OnDragEnterCallback& callback) {}
+void RendererWindowTreeClient::OnDragEnter(ui::Id window_id,
+                                           uint32_t event_flags,
+                                           const gfx::Point& position,
+                                           uint32_t effect_bitmask,
+                                           OnDragEnterCallback callback) {}
 
 void RendererWindowTreeClient::OnDragOver(ui::Id window_id,
                                           uint32_t event_flags,
                                           const gfx::Point& position,
                                           uint32_t effect_bitmask,
-                                          const OnDragOverCallback& callback) {}
+                                          OnDragOverCallback callback) {}
 
 void RendererWindowTreeClient::OnDragLeave(ui::Id window_id) {}
 
-void RendererWindowTreeClient::OnCompleteDrop(
-    ui::Id window_id,
-    uint32_t event_flags,
-    const gfx::Point& position,
-    uint32_t effect_bitmask,
-    const OnCompleteDropCallback& callback) {}
+void RendererWindowTreeClient::OnCompleteDrop(ui::Id window_id,
+                                              uint32_t event_flags,
+                                              const gfx::Point& position,
+                                              uint32_t effect_bitmask,
+                                              OnCompleteDropCallback callback) {
+}
 
 void RendererWindowTreeClient::OnPerformDragDropCompleted(
     uint32_t change_id,
diff --git a/content/renderer/mus/renderer_window_tree_client.h b/content/renderer/mus/renderer_window_tree_client.h
index b0d7f53..d61ad5b 100644
--- a/content/renderer/mus/renderer_window_tree_client.h
+++ b/content/renderer/mus/renderer_window_tree_client.h
@@ -185,18 +185,18 @@
                    uint32_t event_flags,
                    const gfx::Point& position,
                    uint32_t effect_bitmask,
-                   const OnDragEnterCallback& callback) override;
+                   OnDragEnterCallback callback) override;
   void OnDragOver(ui::Id window_id,
                   uint32_t event_flags,
                   const gfx::Point& position,
                   uint32_t effect_bitmask,
-                  const OnDragOverCallback& callback) override;
+                  OnDragOverCallback callback) override;
   void OnDragLeave(ui::Id window_id) override;
   void OnCompleteDrop(ui::Id window_id,
                       uint32_t event_flags,
                       const gfx::Point& position,
                       uint32_t effect_bitmask,
-                      const OnCompleteDropCallback& callback) override;
+                      OnCompleteDropCallback callback) override;
   void OnPerformDragDropCompleted(uint32_t change_id,
                                   bool success,
                                   uint32_t action_taken) override;
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 1d44950..262c94f6 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1006,9 +1006,6 @@
     is_distance_field_text_enabled_ = false;
   }
 
-  WebRuntimeFeatures::EnableCompositorImageAnimations(
-      command_line.HasSwitch(switches::kEnableCompositorImageAnimations));
-
   // Note that under Linux, the media library will normally already have
   // been initialized by the Zygote before this instance became a Renderer.
   media::InitializeMediaLibrary();
diff --git a/device/vr/public/mojom/vr_service.mojom b/device/vr/public/mojom/vr_service.mojom
index 093aaca..e6b977d0 100644
--- a/device/vr/public/mojom/vr_service.mojom
+++ b/device/vr/public/mojom/vr_service.mojom
@@ -9,6 +9,56 @@
 import "gpu/ipc/common/sync_token.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 import "ui/gfx/mojo/gpu_fence_handle.mojom";
+import "ui/gfx/mojo/transform.mojom";
+
+//
+// WebXR interfaces
+//
+
+enum XRHandedness {
+  NONE = 0,
+  LEFT = 1,
+  RIGHT = 2
+};
+
+enum XRPointerOrigin {
+  HEAD = 1,
+  HAND = 2,
+  SCREEN = 3
+};
+
+struct XRInputSourceDescription {
+  XRPointerOrigin pointer_origin;
+  XRHandedness handedness;
+  bool emulated_position;
+
+  // Transform from the grip matrix to the pointer's origin and orientation.
+  gfx.mojom.Transform? pointer_offset;
+};
+
+struct XRInputSourceState {
+  uint32 source_id;
+
+  // Description of this input source's behavior. Should be mostly static, only
+  // need periodic updates.
+  XRInputSourceDescription? description;
+
+  // Transform to the controllers grip (users palm) from start space origin.
+  gfx.mojom.Transform? grip;
+
+  // Describes the current state of the primary input.
+  bool primary_input_pressed;
+
+  // Indicates if the input's primary input has been released (clicked) since
+  // the last report. May indicate that the button was pressed and released in
+  // the space of a single frame, so it may not have been preceeded by a
+  // primary_input_pressed = true;
+  bool primary_input_clicked;
+};
+
+//
+// WebVR/WebXR shared interfaces
+//
 
 // A field of view, given by 4 degrees describing the view from a center point.
 struct VRFieldOfView {
@@ -27,6 +77,10 @@
   array<float, 3>? linearVelocity;
   array<float, 3>? angularAcceleration;
   array<float, 3>? linearAcceleration;
+
+  // For WebXR sessions only, reports the state of all active input devices
+  // synced with the head pose.
+  array<XRInputSourceState>? input_state;
 };
 
 struct VRDisplayCapabilities {
@@ -66,6 +120,8 @@
   // If true, must use a render path that can preserve drawing buffer
   // contents across frames. If false, each frame is drawn independently.
   bool preserve_drawing_buffer;
+  // If true, indicates that WebXR input poses should be reported on VSync.
+  bool webxr_input;
 };
 
 // Frame transport method from the Renderer's point of view.
diff --git a/docs/windows_build_instructions.md b/docs/windows_build_instructions.md
index af4f4699..793c13bef 100644
--- a/docs/windows_build_instructions.md
+++ b/docs/windows_build_instructions.md
@@ -244,7 +244,7 @@
 
 Many things can make builds slow, with Windows Defender slowing process startups
 being a frequent culprit. Have you ensured that the entire Chromium src
-directory is excluded from anti-virus scanning (on Google machines this means
+directory is excluded from antivirus scanning (on Google machines this means
 putting it in a ``src`` directory in the root of a drive)? Have you tried the
 different settings listed above, including different link settings and -j
 values? Have you asked on the chromium-dev mailing list to see if your build is
@@ -282,6 +282,12 @@
     86 build steps completed, average of 2.71/s
 ```
 
+You can also generate these reports by manually running the script after a build:
+
+```shell
+$ python depot_tools\post_build_ninja_summary.py -C out\Default
+```
+
 You can also get a visual report of the build performance with
 [ninjatracing](https://github.com/nico/ninjatracing). This converts the
 .ninja_log file into a .json file which can be loaded into chrome://tracing:
@@ -291,7 +297,7 @@
 ```
 
 Finally, Ninja can report on its own overhead which can be helpful if, for
-instance, process creation is making builds slow, perhaps due to anti-virus
+instance, process creation is making builds slow, perhaps due to antivirus
 interference due to clang-cl not being in an excluded directory:
 
 ```shell
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index 1c127b5..dc414725 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -250,6 +250,7 @@
   ]
 
   data_deps = [
+    "//testing/buildbot/filters:extensions_browsertests_filters",
     "//third_party/mesa:osmesa",
   ]
 }
diff --git a/extensions/shell/browser/shell_content_browser_client.cc b/extensions/shell/browser/shell_content_browser_client.cc
index f6aebe4f..16285974 100644
--- a/extensions/shell/browser/shell_content_browser_client.cc
+++ b/extensions/shell/browser/shell_content_browser_client.cc
@@ -15,6 +15,7 @@
 #include "components/nacl/common/buildflags.h"
 #include "content/public/browser/browser_main_runner.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/storage_partition.h"
@@ -23,6 +24,7 @@
 #include "content/public/common/url_constants.h"
 #include "content/shell/browser/shell_browser_context.h"
 #include "content/shell/browser/shell_devtools_manager_delegate.h"
+#include "extensions/browser/api/web_request/web_request_api.h"
 #include "extensions/browser/extension_message_filter.h"
 #include "extensions/browser/extension_navigation_throttle.h"
 #include "extensions/browser/extension_navigation_ui_data.h"
@@ -262,6 +264,42 @@
   return std::make_unique<ShellNavigationUIData>(navigation_handle);
 }
 
+void ShellContentBrowserClient::RegisterNonNetworkNavigationURLLoaderFactories(
+    content::RenderFrameHost* frame_host,
+    NonNetworkURLLoaderFactoryMap* factories) {
+  content::BrowserContext* browser_context =
+      frame_host->GetProcess()->GetBrowserContext();
+  factories->emplace(
+      extensions::kExtensionScheme,
+      extensions::CreateExtensionNavigationURLLoaderFactory(
+          frame_host,
+          extensions::ExtensionSystem::Get(browser_context)->info_map()));
+}
+
+void ShellContentBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories(
+    content::RenderFrameHost* frame_host,
+    const GURL& frame_url,
+    NonNetworkURLLoaderFactoryMap* factories) {
+  content::BrowserContext* browser_context =
+      frame_host->GetProcess()->GetBrowserContext();
+  auto factory = extensions::MaybeCreateExtensionSubresourceURLLoaderFactory(
+      frame_host, frame_url,
+      extensions::ExtensionSystem::Get(browser_context)->info_map());
+  if (factory)
+    factories->emplace(extensions::kExtensionScheme, std::move(factory));
+}
+
+bool ShellContentBrowserClient::WillCreateURLLoaderFactory(
+    content::RenderFrameHost* frame,
+    bool is_navigation,
+    network::mojom::URLLoaderFactoryRequest* factory_request) {
+  auto* web_request_api =
+      extensions::BrowserContextKeyedAPIFactory<extensions::WebRequestAPI>::Get(
+          frame->GetProcess()->GetBrowserContext());
+  return web_request_api->MaybeProxyURLLoaderFactory(frame, is_navigation,
+                                                     factory_request);
+}
+
 ShellBrowserMainParts* ShellContentBrowserClient::CreateShellBrowserMainParts(
     const content::MainFunctionParams& parameters,
     ShellBrowserMainDelegate* browser_main_delegate) {
diff --git a/extensions/shell/browser/shell_content_browser_client.h b/extensions/shell/browser/shell_content_browser_client.h
index 3d21ef6..3b7c035 100644
--- a/extensions/shell/browser/shell_content_browser_client.h
+++ b/extensions/shell/browser/shell_content_browser_client.h
@@ -66,6 +66,17 @@
       content::NavigationHandle* navigation_handle) override;
   std::unique_ptr<content::NavigationUIData> GetNavigationUIData(
       content::NavigationHandle* navigation_handle) override;
+  void RegisterNonNetworkNavigationURLLoaderFactories(
+      content::RenderFrameHost* frame_host,
+      NonNetworkURLLoaderFactoryMap* factories) override;
+  void RegisterNonNetworkSubresourceURLLoaderFactories(
+      content::RenderFrameHost* frame_host,
+      const GURL& frame_url,
+      NonNetworkURLLoaderFactoryMap* factories) override;
+  bool WillCreateURLLoaderFactory(
+      content::RenderFrameHost* frame_host,
+      bool is_navigation,
+      network::mojom::URLLoaderFactoryRequest* factory_request) override;
 
  protected:
   // Subclasses may wish to provide their own ShellBrowserMainParts.
diff --git a/extensions/test/data/manifest_tests/default_locale_invalid.json b/extensions/test/data/manifest_tests/default_locale_invalid.json
index b97077a..24711d58 100644
--- a/extensions/test/data/manifest_tests/default_locale_invalid.json
+++ b/extensions/test/data/manifest_tests/default_locale_invalid.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "version": "1",
   "default_locale": "German"
 }
diff --git a/extensions/test/data/manifest_tests/default_locale_valid.json b/extensions/test/data/manifest_tests/default_locale_valid.json
index f3680e48..6e08ddd8 100644
--- a/extensions/test/data/manifest_tests/default_locale_valid.json
+++ b/extensions/test/data/manifest_tests/default_locale_valid.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "version": "1",
   "default_locale": "de-AT"
 }
diff --git a/extensions/test/data/manifest_tests/file_handlers_invalid_extension.json b/extensions/test/data/manifest_tests/file_handlers_invalid_extension.json
index deda5c2..432c065 100644
--- a/extensions/test/data/manifest_tests/file_handlers_invalid_extension.json
+++ b/extensions/test/data/manifest_tests/file_handlers_invalid_extension.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "description": "App with file_handlers manifest.",
   "version": "1",
   "app": {
diff --git a/extensions/test/data/manifest_tests/file_handlers_invalid_extension_element.json b/extensions/test/data/manifest_tests/file_handlers_invalid_extension_element.json
index 48c71ef..4c8dda7 100644
--- a/extensions/test/data/manifest_tests/file_handlers_invalid_extension_element.json
+++ b/extensions/test/data/manifest_tests/file_handlers_invalid_extension_element.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "description": "App with file_handlers manifest.",
   "version": "1",
   "app": {
diff --git a/extensions/test/data/manifest_tests/file_handlers_invalid_handlers.json b/extensions/test/data/manifest_tests/file_handlers_invalid_handlers.json
index f1d65d1a..5d617ec6 100644
--- a/extensions/test/data/manifest_tests/file_handlers_invalid_handlers.json
+++ b/extensions/test/data/manifest_tests/file_handlers_invalid_handlers.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "description": "App with file_handlers manifest.",
   "version": "1",
   "app": {
diff --git a/extensions/test/data/manifest_tests/file_handlers_invalid_include_directories.json b/extensions/test/data/manifest_tests/file_handlers_invalid_include_directories.json
index 765230834..73be2f2 100644
--- a/extensions/test/data/manifest_tests/file_handlers_invalid_include_directories.json
+++ b/extensions/test/data/manifest_tests/file_handlers_invalid_include_directories.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "description": "App with file_handlers manifest.",
   "version": "1",
   "app": {
diff --git a/extensions/test/data/manifest_tests/file_handlers_invalid_no_type_or_extension.json b/extensions/test/data/manifest_tests/file_handlers_invalid_no_type_or_extension.json
index 1366625e..8ae6ed8 100644
--- a/extensions/test/data/manifest_tests/file_handlers_invalid_no_type_or_extension.json
+++ b/extensions/test/data/manifest_tests/file_handlers_invalid_no_type_or_extension.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "description": "App with file_handlers manifest.",
   "version": "1",
   "app": {
diff --git a/extensions/test/data/manifest_tests/file_handlers_invalid_not_app.json b/extensions/test/data/manifest_tests/file_handlers_invalid_not_app.json
index 39b1c67..e8add66 100644
--- a/extensions/test/data/manifest_tests/file_handlers_invalid_not_app.json
+++ b/extensions/test/data/manifest_tests/file_handlers_invalid_not_app.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "description": "App with file_handlers manifest.",
   "version": "1",
   "file_handlers": {
diff --git a/extensions/test/data/manifest_tests/file_handlers_invalid_too_many.json b/extensions/test/data/manifest_tests/file_handlers_invalid_too_many.json
index 7d3c04d8..ae8f6c7 100644
--- a/extensions/test/data/manifest_tests/file_handlers_invalid_too_many.json
+++ b/extensions/test/data/manifest_tests/file_handlers_invalid_too_many.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "description": "App with file_handlers manifest.",
   "version": "1",
   "app": {
diff --git a/extensions/test/data/manifest_tests/file_handlers_invalid_type.json b/extensions/test/data/manifest_tests/file_handlers_invalid_type.json
index 7415fa02..dae3636 100644
--- a/extensions/test/data/manifest_tests/file_handlers_invalid_type.json
+++ b/extensions/test/data/manifest_tests/file_handlers_invalid_type.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "description": "App with file_handlers manifest.",
   "version": "1",
   "app": {
diff --git a/extensions/test/data/manifest_tests/file_handlers_invalid_type_element.json b/extensions/test/data/manifest_tests/file_handlers_invalid_type_element.json
index e2067bf..86d4993 100644
--- a/extensions/test/data/manifest_tests/file_handlers_invalid_type_element.json
+++ b/extensions/test/data/manifest_tests/file_handlers_invalid_type_element.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "description": "App with file_handlers manifest.",
   "version": "1",
   "app": {
diff --git a/extensions/test/data/manifest_tests/file_handlers_invalid_verb.json b/extensions/test/data/manifest_tests/file_handlers_invalid_verb.json
index 65e21aa..2b01c14 100644
--- a/extensions/test/data/manifest_tests/file_handlers_invalid_verb.json
+++ b/extensions/test/data/manifest_tests/file_handlers_invalid_verb.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "description": "App with file_handlers manifest.",
   "version": "1",
   "app": {
diff --git a/extensions/test/data/manifest_tests/file_handlers_valid.json b/extensions/test/data/manifest_tests/file_handlers_valid.json
index 0e6335c3..13c344b2 100644
--- a/extensions/test/data/manifest_tests/file_handlers_valid.json
+++ b/extensions/test/data/manifest_tests/file_handlers_valid.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "description": "App with file_handlers manifest.",
   "version": "1",
   "app": {
diff --git a/extensions/test/data/manifest_tests/incognito_not_allowed.json b/extensions/test/data/manifest_tests/incognito_not_allowed.json
index f44d252..9ec028b 100644
--- a/extensions/test/data/manifest_tests/incognito_not_allowed.json
+++ b/extensions/test/data/manifest_tests/incognito_not_allowed.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "version": "1",
   "incognito": "not_allowed"
 }
diff --git a/extensions/test/data/manifest_tests/incognito_split.json b/extensions/test/data/manifest_tests/incognito_split.json
index 5bb8b67..39fd3d8 100644
--- a/extensions/test/data/manifest_tests/incognito_split.json
+++ b/extensions/test/data/manifest_tests/incognito_split.json
@@ -1,5 +1,6 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "version": "1",
   "incognito": "split"
 }
diff --git a/extensions/test/data/manifest_tests/minimal.json b/extensions/test/data/manifest_tests/minimal.json
index 0d9792a..a9dbc2f 100644
--- a/extensions/test/data/manifest_tests/minimal.json
+++ b/extensions/test/data/manifest_tests/minimal.json
@@ -1,4 +1,5 @@
 {
   "name": "test",
+  "manifest_version": 2,
   "version": "1"
 }
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index 87f7d15..9499f69 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -38,6 +38,7 @@
   public_deps = [
     "//gpu/command_buffer/client:gles2_cmd_helper_sources",
     "//gpu/command_buffer/common:gles2_sources",
+    "//gpu/command_buffer/common:raster_sources",
     "//gpu/command_buffer/service:gles2_sources",
   ]
 }
diff --git a/gpu/command_buffer/build_cmd_buffer_lib.py b/gpu/command_buffer/build_cmd_buffer_lib.py
index 6e869597c1..3209864 100644
--- a/gpu/command_buffer/build_cmd_buffer_lib.py
+++ b/gpu/command_buffer/build_cmd_buffer_lib.py
@@ -139,6 +139,7 @@
     # 'some_TEXT_' -> 'some TEXT'
     return input_string.replace('_', ' ').strip().split()
   else:
+    input_string = input_string.replace('::', ' ')
     if re.search('[A-Z]', input_string) and re.search('[a-z]', input_string):
       # mixed case.
       # look for capitalization to cut input_strings
@@ -153,6 +154,12 @@
   words = SplitWords(input_string)
   return '_'.join([word.lower() for word in words])
 
+def ValidatorClassName(type_name):
+  """Converts some::namespace::TypeName to SomeNamespaceTypeNameValidator."""
+  words = SplitWords(type_name)
+  prefix = ''.join([word.title() for word in words])
+  return '%sValidator' % prefix
+
 def CachedStateName(item):
   if item.get('cached', False):
     return 'cached_' + item['name']
@@ -431,8 +438,8 @@
     f.write("            cmd.header.command);\n")
     func.type_handler.WriteCmdSizeTest(func, f)
     for value, arg in enumerate(args):
-      f.write("  EXPECT_EQ(static_cast<%s>(%d), cmd.%s);\n" %
-                 (arg.type, value + 11, arg.GetArgAccessor()))
+      f.write("  EXPECT_EQ(static_cast<%s>(%d), %s);\n" %
+                 (arg.type, value + 11, arg.GetArgAccessor('cmd')))
     f.write("  CheckBytesWrittenMatchesExpectedSize(\n")
     f.write("      next_cmd, sizeof(cmd));\n")
     f.write("}\n")
@@ -1940,13 +1947,19 @@
     for arg in func.GetOriginalArgs():
       arg.WriteClientSideValidationCode(f, func)
     f.write("  GLuint client_id;\n")
-    if func.return_type == "GLsync":
-      f.write(
-          "  GetIdHandler(SharedIdNamespaces::kSyncs)->\n")
+    not_shared = func.GetInfo('not_shared')
+    if not_shared:
+      f.write('IdAllocator* id_allocator = GetIdAllocator(IdNamespaces::k%s);' %
+              func.GetInfo('resource_types'))
+      f.write('client_id = id_allocator->AllocateID();')
     else:
-      f.write(
-          "  GetIdHandler(SharedIdNamespaces::kProgramsAndShaders)->\n")
-    f.write("      MakeIds(this, 0, 1, &client_id);\n")
+      if func.return_type == "GLsync":
+        f.write(
+            "  GetIdHandler(SharedIdNamespaces::kSyncs)->\n")
+      else:
+        f.write(
+            "  GetIdHandler(SharedIdNamespaces::kProgramsAndShaders)->\n")
+      f.write("      MakeIds(this, 0, 1, &client_id);\n")
     f.write("  helper_->%s(%s);\n" %
                (func.name, func.MakeCmdArgString("")))
     f.write('  GPU_CLIENT_LOG("returned " << client_id);\n')
@@ -4271,9 +4284,9 @@
     """returns an invalid value and expected parse result by index."""
     return ("---ERROR0---", "---ERROR2---", None)
 
-  def GetArgAccessor(self):
+  def GetArgAccessor(self, cmd_struct_name):
     """Returns the name of the accessor for the argument within the struct."""
-    return self.name
+    return '%s.%s' % (cmd_struct_name, self.name)
 
   def GetLogArg(self):
     """Get argument appropriate for LOG macro."""
@@ -4335,6 +4348,33 @@
 
 
 class BoolArgument(Argument):
+  """class for C++ bool"""
+
+  def __init__(self, name, _type):
+    Argument.__init__(self, name, _type)
+
+  def GetValidArg(self, func):
+    """Gets a valid value for this argument."""
+    return 'true'
+
+  def GetValidClientSideArg(self, func):
+    """Gets a valid value for this argument."""
+    return 'true'
+
+  def GetValidClientSideCmdArg(self, func):
+    """Gets a valid value for this argument."""
+    return 'true'
+
+  def GetValidGLArg(self, func):
+    """Gets a valid GL value for this argument."""
+    return 'true'
+
+  def GetArgAccessor(self, struct_name):
+    """Returns the name of the accessor for the argument within the struct."""
+    return 'static_cast<bool>(%s.%s)' % (struct_name, self.name)
+
+
+class GLBooleanArgument(Argument):
   """class for GLboolean"""
 
   def __init__(self, name, _type):
@@ -4432,11 +4472,11 @@
 class EnumBaseArgument(Argument):
   """Base class for EnumArgument, IntArgument, and BitfieldArgument."""
 
-  def __init__(self, name, gl_type, arg_type, gl_error, named_type_info):
+  def __init__(self, name, gl_type, type_name, arg_type, gl_error,
+               named_type_info):
     Argument.__init__(self, name, gl_type)
 
     self.gl_error = gl_error
-    type_name = arg_type[len(gl_type):]
     self.type_name = type_name
     self.named_type = NamedType(named_type_info[type_name])
 
@@ -4532,14 +4572,35 @@
   """A class that represents a GLenum argument"""
 
   def __init__(self, name, arg_type, named_type_info):
-    EnumBaseArgument.__init__(self, name, "GLenum", arg_type, "GL_INVALID_ENUM",
-                              named_type_info)
+    EnumBaseArgument.__init__(self, name, "GLenum", arg_type[len("GLenum"):],
+                              arg_type, "GL_INVALID_ENUM", named_type_info)
 
   def GetLogArg(self):
     """Overridden from Argument."""
     return ("GLES2Util::GetString%s(%s)" %
             (self.type_name, self.name))
 
+
+class EnumClassArgument(EnumBaseArgument):
+  """A class that represents a C++ enum argument encoded as uint32_t"""
+
+  def __init__(self, name, arg_type, named_type_info):
+    type_name = arg_type[len("EnumClass"):]
+    EnumBaseArgument.__init__(self, name, type_name, type_name, arg_type,
+                              "GL_INVALID_ENUM", named_type_info)
+
+  def GetArgAccessor(self, struct_name):
+    """Returns the name of the accessor for the argument within the struct."""
+    return 'static_cast<%s>(%s.%s)' % (self.type_name, struct_name, self.name)
+
+  def WriteSetCode(self, f, indent, var):
+    f.write("%s%s = static_cast<uint32_t>(%s);\n" %
+            (' ' * indent, self.name, var))
+
+  def GetLogArg(self):
+    return 'static_cast<uint32_t>(%s)' % self.name
+
+
 class IntArgument(EnumBaseArgument):
   """A class for a GLint argument that can only accept specific values.
 
@@ -4548,8 +4609,8 @@
   """
 
   def __init__(self, name, arg_type, named_type_info):
-    EnumBaseArgument.__init__(self, name, "GLint", arg_type, "GL_INVALID_VALUE",
-                              named_type_info)
+    EnumBaseArgument.__init__(self, name, "GLint", arg_type[len("GLint"):],
+                              arg_type, "GL_INVALID_VALUE", named_type_info)
 
 
 class BitFieldArgument(EnumBaseArgument):
@@ -4560,7 +4621,8 @@
   """
 
   def __init__(self, name, arg_type, named_type_info):
-    EnumBaseArgument.__init__(self, name, "GLbitfield", arg_type,
+    EnumBaseArgument.__init__(self, name, "GLbitfield",
+                              arg_type[len("GLbitfield"):], arg_type,
                               "GL_INVALID_VALUE", named_type_info)
 
 
@@ -4805,7 +4867,7 @@
       my_type = "GLuint"
     else:
       my_type = self.type
-    f.write("  %s %s = c.%s;\n" % (my_type, self.name, self.GetArgAccessor()))
+    f.write("  %s %s = %s;\n" % (my_type, self.name, self.GetArgAccessor('c')))
 
   def GetValidArg(self, func):
     return "client_%s_id_" % self.resource_type.lower()
@@ -4849,7 +4911,8 @@
 
   def WriteGetCode(self, f):
     """Overridden from Argument."""
-    f.write("  %s %s = c.%s;\n" % (self.type, self.name, self.GetArgAccessor()))
+    f.write("  %s %s = %s;\n" % (self.type, self.name,
+                                 self.GetArgAccessor('c')))
 
   def GetValidArg(self, func):
     return "client_%s_id_" % self.resource_type.lower()
@@ -4872,8 +4935,8 @@
   def __init__(self, name, arg_type):
     Argument.__init__(self, name, arg_type)
 
-  def GetArgAccessor(self):
-    return "%s()" % self.name
+  def GetArgAccessor(self, cmd_struct_name):
+    return "%s.%s()" % (cmd_struct_name, self.name)
 
   def WriteArgAccessor(self, f):
     """Writes specialized accessor for compound members."""
@@ -5548,6 +5611,8 @@
   # Is this a pointer argument?
   if arg_string.find('*') >= 0:
     return PointerArgument(arg_name, arg_type)
+  elif t.startswith('EnumClass'):
+    return EnumClassArgument(arg_name, arg_type, named_type_info)
   # Is this a resource argument? Must come after pointer check.
   elif t.startswith('GLidBind'):
     return ResourceIdBindArgument(arg_name, arg_type)
@@ -5560,12 +5625,14 @@
   elif t.startswith('GLbitfield') and t != 'GLbitfield':
     return BitFieldArgument(arg_name, arg_type, named_type_info)
   elif t.startswith('GLboolean'):
-    return BoolArgument(arg_name, arg_type)
+    return GLBooleanArgument(arg_name, arg_type)
   elif t.startswith('GLintUniformLocation'):
     return UniformLocationArgument(arg_name)
   elif (t.startswith('GLint') and t != 'GLint' and
         not t.startswith('GLintptr')):
     return IntArgument(arg_name, arg_type, named_type_info)
+  elif t == 'bool':
+    return BoolArgument(arg_name, arg_type)
   elif t == 'GLsizeiNotNegative' or t == 'GLintptrNotNegative':
     return SizeNotNegativeArgument(arg_name, t.replace('NotNegative', ''))
   elif t.startswith('GLsize'):
@@ -6473,21 +6540,22 @@
         named_type = NamedType(self.named_type_info[name])
         if not named_type.CreateValidator():
           continue
+        class_name = ValidatorClassName(name)
         if named_type.IsComplete():
-          f.write("""class %(name)sValidator {
+          f.write("""class %(class_name)s {
                       public:
                        bool IsValid(const %(type)s value) const;"""% {
-            'name': name,
+            'class_name': class_name,
             'type': named_type.GetType()
           })
           if named_type.HasES3Values():
-            f.write("""%sValidator();
+            f.write("""%s();
                        void SetIsES3(bool is_es3) { is_es3_ = is_es3; }
                       private:
-                       bool is_es3_;""" % name)
+                       bool is_es3_;""" % class_name)
           f.write("};\n")
-          f.write("%sValidator %s;\n\n" %
-                     (name, ToUnderscore(name)))
+          f.write("%s %s;\n\n" %
+                     (class_name, ToUnderscore(name)))
         else:
           f.write("ValueValidator<%s> %s;\n" %
                      (named_type.GetType(), ToUnderscore(name)))
@@ -6500,17 +6568,18 @@
       names = sorted(self.named_type_info.keys())
       for name in names:
         named_type = NamedType(self.named_type_info[name])
+        class_name = ValidatorClassName(name)
         if not named_type.CreateValidator():
           continue
         if named_type.IsComplete():
           if named_type.HasES3Values():
-            f.write("""Validators::%(name)sValidator::%(name)sValidator()
-                         : is_es3_(false) {}""" % { 'name': name })
+            f.write("""Validators::%(class_name)s::%(class_name)s()
+                         : is_es3_(false) {}""" % { 'class_name': class_name })
 
-          f.write("""bool Validators::%(name)sValidator::IsValid(
+          f.write("""bool Validators::%(class_name)s::IsValid(
                          const %(type)s value) const {
                        switch(value) {\n""" % {
-            'name': name,
+            'class_name': class_name,
             'type': named_type.GetType()
           })
           if named_type.GetValidValues():
@@ -6568,41 +6637,42 @@
       f.write(" {\n");
       f.write("}\n\n");
 
-      f.write("void Validators::UpdateValuesES3() {\n")
-      for name in names:
-        named_type = NamedType(self.named_type_info[name])
-        if not named_type.IsConstant() and named_type.IsComplete():
-          if named_type.HasES3Values():
-            f.write("  %(name)s.SetIsES3(true);" % {
-              'name': ToUnderscore(name),
-            })
-          continue
-        if named_type.GetDeprecatedValuesES3():
-          code = """  %(name)s.RemoveValues(
+      if _prefix != 'Raster':
+        f.write("void Validators::UpdateValuesES3() {\n")
+        for name in names:
+          named_type = NamedType(self.named_type_info[name])
+          if not named_type.IsConstant() and named_type.IsComplete():
+            if named_type.HasES3Values():
+              f.write("  %(name)s.SetIsES3(true);" % {
+                'name': ToUnderscore(name),
+              })
+            continue
+          if named_type.GetDeprecatedValuesES3():
+            code = """  %(name)s.RemoveValues(
       deprecated_%(name)s_table_es3, arraysize(deprecated_%(name)s_table_es3));
 """
-          f.write(code % {
-            'name': ToUnderscore(name),
-          })
-        if named_type.GetValidValuesES3():
-          code = """  %(name)s.AddValues(
+            f.write(code % {
+              'name': ToUnderscore(name),
+            })
+          if named_type.GetValidValuesES3():
+            code = """  %(name)s.AddValues(
       valid_%(name)s_table_es3, arraysize(valid_%(name)s_table_es3));
 """
-          f.write(code % {
-            'name': ToUnderscore(name),
-          })
-      f.write("}\n\n");
+            f.write(code % {
+              'name': ToUnderscore(name),
+            })
+        f.write("}\n\n");
 
-      f.write("void Validators::UpdateETCCompressedTextureFormats() {\n")
-      for name in ['CompressedTextureFormat', 'TextureInternalFormatStorage']:
-        for fmt in _ETC_COMPRESSED_TEXTURE_FORMATS:
-          code = """  %(name)s.AddValue(%(format)s);
+        f.write("void Validators::UpdateETCCompressedTextureFormats() {\n")
+        for name in ['CompressedTextureFormat', 'TextureInternalFormatStorage']:
+          for fmt in _ETC_COMPRESSED_TEXTURE_FORMATS:
+            code = """  %(name)s.AddValue(%(format)s);
 """
-          f.write(code % {
-            'name': ToUnderscore(name),
-            'format': fmt,
-          })
-      f.write("}\n\n");
+            f.write(code % {
+              'name': ToUnderscore(name),
+              'format': fmt,
+            })
+        f.write("}\n\n");
     self.generated_cpp_filenames.append(filename)
 
   def WriteCommonUtilsHeader(self, filename):
diff --git a/gpu/command_buffer/build_raster_cmd_buffer.py b/gpu/command_buffer/build_raster_cmd_buffer.py
index cd9d64c..9f0b9cd 100755
--- a/gpu/command_buffer/build_raster_cmd_buffer.py
+++ b/gpu/command_buffer/build_raster_cmd_buffer.py
@@ -16,8 +16,6 @@
 
 _STATE_INFO = {}
 
-# TODO(backer): Figure out which of these enums are actually valid.
-#
 # Named type info object represents a named type that is used in OpenGL call
 # arguments.  Each named type defines a set of valid OpenGL call arguments.  The
 # named types are used in 'raster_cmd_buffer_functions.txt'.
@@ -33,203 +31,15 @@
 # validator: If set to False will prevent creation of a ValueValidator. Values
 #            are still expected to be checked for validity and will be tested.
 _NAMED_TYPE_INFO = {
-  'CompressedTextureFormat': {
-    'type': 'GLenum',
-    'valid': [
-    ],
-    'valid_es3': [
-    ],
-  },
   'GLState': {
     'type': 'GLenum',
     'valid': [
-      # NOTE: State an Capability entries added later.
       'GL_ACTIVE_TEXTURE',
-      'GL_ALIASED_LINE_WIDTH_RANGE',
-      'GL_ALIASED_POINT_SIZE_RANGE',
-      'GL_ALPHA_BITS',
-      'GL_ARRAY_BUFFER_BINDING',
-      'GL_BLUE_BITS',
-      'GL_COMPRESSED_TEXTURE_FORMATS',
-      'GL_CURRENT_PROGRAM',
-      'GL_DEPTH_BITS',
-      'GL_DEPTH_RANGE',
-      'GL_ELEMENT_ARRAY_BUFFER_BINDING',
-      'GL_FRAMEBUFFER_BINDING',
-      'GL_GENERATE_MIPMAP_HINT',
-      'GL_GREEN_BITS',
-      'GL_IMPLEMENTATION_COLOR_READ_FORMAT',
-      'GL_IMPLEMENTATION_COLOR_READ_TYPE',
-      'GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS',
-      'GL_MAX_CUBE_MAP_TEXTURE_SIZE',
-      'GL_MAX_FRAGMENT_UNIFORM_VECTORS',
-      'GL_MAX_RENDERBUFFER_SIZE',
-      'GL_MAX_TEXTURE_IMAGE_UNITS',
-      'GL_MAX_TEXTURE_SIZE',
-      'GL_MAX_VARYING_VECTORS',
-      'GL_MAX_VERTEX_ATTRIBS',
-      'GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS',
-      'GL_MAX_VERTEX_UNIFORM_VECTORS',
-      'GL_MAX_VIEWPORT_DIMS',
-      'GL_NUM_COMPRESSED_TEXTURE_FORMATS',
-      'GL_NUM_SHADER_BINARY_FORMATS',
-      'GL_PACK_ALIGNMENT',
-      'GL_RED_BITS',
-      'GL_RENDERBUFFER_BINDING',
-      'GL_SAMPLE_BUFFERS',
-      'GL_SAMPLE_COVERAGE_INVERT',
-      'GL_SAMPLE_COVERAGE_VALUE',
-      'GL_SAMPLES',
-      'GL_SCISSOR_BOX',
-      'GL_SHADER_BINARY_FORMATS',
-      'GL_SHADER_COMPILER',
-      'GL_SUBPIXEL_BITS',
-      'GL_STENCIL_BITS',
-      'GL_TEXTURE_BINDING_2D',
-      'GL_TEXTURE_BINDING_CUBE_MAP',
-      'GL_UNPACK_ALIGNMENT',
-      'GL_BIND_GENERATES_RESOURCE_CHROMIUM',
-      # we can add this because we emulate it if the driver does not support it.
-      'GL_VERTEX_ARRAY_BINDING_OES',
-      'GL_VIEWPORT',
-    ],
-    'valid_es3': [
-      'GL_COPY_READ_BUFFER_BINDING',
-      'GL_COPY_WRITE_BUFFER_BINDING',
-      'GL_DRAW_BUFFER0',
-      'GL_DRAW_BUFFER1',
-      'GL_DRAW_BUFFER2',
-      'GL_DRAW_BUFFER3',
-      'GL_DRAW_BUFFER4',
-      'GL_DRAW_BUFFER5',
-      'GL_DRAW_BUFFER6',
-      'GL_DRAW_BUFFER7',
-      'GL_DRAW_BUFFER8',
-      'GL_DRAW_BUFFER9',
-      'GL_DRAW_BUFFER10',
-      'GL_DRAW_BUFFER11',
-      'GL_DRAW_BUFFER12',
-      'GL_DRAW_BUFFER13',
-      'GL_DRAW_BUFFER14',
-      'GL_DRAW_BUFFER15',
-      'GL_DRAW_FRAMEBUFFER_BINDING',
-      'GL_FRAGMENT_SHADER_DERIVATIVE_HINT',
-      'GL_GPU_DISJOINT_EXT',
-      'GL_MAJOR_VERSION',
-      'GL_MAX_3D_TEXTURE_SIZE',
-      'GL_MAX_ARRAY_TEXTURE_LAYERS',
-      'GL_MAX_COLOR_ATTACHMENTS',
-      'GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS',
-      'GL_MAX_COMBINED_UNIFORM_BLOCKS',
-      'GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS',
-      'GL_MAX_DRAW_BUFFERS',
-      'GL_MAX_ELEMENT_INDEX',
-      'GL_MAX_ELEMENTS_INDICES',
-      'GL_MAX_ELEMENTS_VERTICES',
-      'GL_MAX_FRAGMENT_INPUT_COMPONENTS',
-      'GL_MAX_FRAGMENT_UNIFORM_BLOCKS',
-      'GL_MAX_FRAGMENT_UNIFORM_COMPONENTS',
-      'GL_MAX_PROGRAM_TEXEL_OFFSET',
-      'GL_MAX_SAMPLES',
-      'GL_MAX_SERVER_WAIT_TIMEOUT',
-      'GL_MAX_TEXTURE_LOD_BIAS',
-      'GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS',
-      'GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS',
-      'GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS',
-      'GL_MAX_UNIFORM_BLOCK_SIZE',
-      'GL_MAX_UNIFORM_BUFFER_BINDINGS',
-      'GL_MAX_VARYING_COMPONENTS',
-      'GL_MAX_VERTEX_OUTPUT_COMPONENTS',
-      'GL_MAX_VERTEX_UNIFORM_BLOCKS',
-      'GL_MAX_VERTEX_UNIFORM_COMPONENTS',
-      'GL_MIN_PROGRAM_TEXEL_OFFSET',
-      'GL_MINOR_VERSION',
-      'GL_NUM_EXTENSIONS',
-      'GL_NUM_PROGRAM_BINARY_FORMATS',
-      'GL_PACK_ROW_LENGTH',
-      'GL_PACK_SKIP_PIXELS',
-      'GL_PACK_SKIP_ROWS',
-      'GL_PIXEL_PACK_BUFFER_BINDING',
-      'GL_PIXEL_UNPACK_BUFFER_BINDING',
-      'GL_PROGRAM_BINARY_FORMATS',
-      'GL_READ_BUFFER',
-      'GL_READ_FRAMEBUFFER_BINDING',
-      'GL_SAMPLER_BINDING',
-      'GL_TIMESTAMP_EXT',
-      'GL_TEXTURE_BINDING_2D_ARRAY',
-      'GL_TEXTURE_BINDING_3D',
-      'GL_TRANSFORM_FEEDBACK_BINDING',
-      'GL_TRANSFORM_FEEDBACK_ACTIVE',
-      'GL_TRANSFORM_FEEDBACK_BUFFER_BINDING',
-      'GL_TRANSFORM_FEEDBACK_PAUSED',
-      'GL_TRANSFORM_FEEDBACK_BUFFER_SIZE',
-      'GL_TRANSFORM_FEEDBACK_BUFFER_START',
-      'GL_UNIFORM_BUFFER_BINDING',
-      'GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT',
-      'GL_UNIFORM_BUFFER_SIZE',
-      'GL_UNIFORM_BUFFER_START',
-      'GL_UNPACK_IMAGE_HEIGHT',
-      'GL_UNPACK_ROW_LENGTH',
-      'GL_UNPACK_SKIP_IMAGES',
-      'GL_UNPACK_SKIP_PIXELS',
-      'GL_UNPACK_SKIP_ROWS',
-      # GL_VERTEX_ARRAY_BINDING is the same as GL_VERTEX_ARRAY_BINDING_OES
-      # 'GL_VERTEX_ARRAY_BINDING',
     ],
     'invalid': [
       'GL_FOG_HINT',
     ],
   },
-  'BufferUsage': {
-    'type': 'GLenum',
-    'is_complete': True,
-    'valid': [
-      'GL_STREAM_DRAW',
-      'GL_STATIC_DRAW',
-      'GL_DYNAMIC_DRAW',
-    ],
-    'valid_es3': [
-      'GL_STREAM_READ',
-      'GL_STREAM_COPY',
-      'GL_STATIC_READ',
-      'GL_STATIC_COPY',
-      'GL_DYNAMIC_READ',
-      'GL_DYNAMIC_COPY',
-    ],
-    'invalid': [
-      'GL_NONE',
-    ],
-  },
-  'TextureTarget': {
-    'type': 'GLenum',
-    'valid': [
-      'GL_TEXTURE_2D',
-      'GL_TEXTURE_CUBE_MAP_POSITIVE_X',
-      'GL_TEXTURE_CUBE_MAP_NEGATIVE_X',
-      'GL_TEXTURE_CUBE_MAP_POSITIVE_Y',
-      'GL_TEXTURE_CUBE_MAP_NEGATIVE_Y',
-      'GL_TEXTURE_CUBE_MAP_POSITIVE_Z',
-      'GL_TEXTURE_CUBE_MAP_NEGATIVE_Z',
-    ],
-    'invalid': [
-      'GL_PROXY_TEXTURE_CUBE_MAP',
-    ]
-  },
-  'TextureBindTarget': {
-    'type': 'GLenum',
-    'valid': [
-      'GL_TEXTURE_2D',
-      'GL_TEXTURE_CUBE_MAP',
-    ],
-    'valid_es3': [
-      'GL_TEXTURE_3D',
-      'GL_TEXTURE_2D_ARRAY',
-    ],
-    'invalid': [
-      'GL_TEXTURE_1D',
-      'GL_TEXTURE_3D',
-    ],
-  },
   'QueryObjectParameter': {
     'type': 'GLenum',
     'is_complete': True,
@@ -242,14 +52,12 @@
     'type': 'GLenum',
     'is_complete': True,
     'valid': [
-      'GL_SAMPLES_PASSED_ARB',
-      'GL_ANY_SAMPLES_PASSED_EXT',
-      'GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT',
       'GL_COMMANDS_ISSUED_CHROMIUM',
-      'GL_LATENCY_QUERY_CHROMIUM',
-      'GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM',
       'GL_COMMANDS_COMPLETED_CHROMIUM',
     ],
+    'invalid': [
+      'GL_LATENCY_QUERY_CHROMIUM',
+    ],
   },
   'TextureParameter': {
     'type': 'GLenum',
@@ -259,263 +67,35 @@
       'GL_TEXTURE_WRAP_S',
       'GL_TEXTURE_WRAP_T',
     ],
-    'valid_es3': [
-      'GL_TEXTURE_BASE_LEVEL',
-      'GL_TEXTURE_COMPARE_FUNC',
-      'GL_TEXTURE_COMPARE_MODE',
-      'GL_TEXTURE_IMMUTABLE_FORMAT',
-      'GL_TEXTURE_IMMUTABLE_LEVELS',
-      'GL_TEXTURE_MAX_LEVEL',
-      'GL_TEXTURE_MAX_LOD',
-      'GL_TEXTURE_MIN_LOD',
-      'GL_TEXTURE_WRAP_R',
-    ],
     'invalid': [
       'GL_GENERATE_MIPMAP',
     ],
   },
-  'PixelStore': {
+  'TextureWrapMode': {
     'type': 'GLenum',
     'valid': [
-      'GL_PACK_ALIGNMENT',
-      'GL_UNPACK_ALIGNMENT',
-    ],
-    'valid_es3': [
-      'GL_PACK_ROW_LENGTH',
-      'GL_PACK_SKIP_PIXELS',
-      'GL_PACK_SKIP_ROWS',
-      'GL_UNPACK_ROW_LENGTH',
-      'GL_UNPACK_IMAGE_HEIGHT',
-      'GL_UNPACK_SKIP_PIXELS',
-      'GL_UNPACK_SKIP_ROWS',
-      'GL_UNPACK_SKIP_IMAGES',
+      'GL_CLAMP_TO_EDGE',
     ],
     'invalid': [
-      'GL_PACK_SWAP_BYTES',
-      'GL_UNPACK_SWAP_BYTES',
+      'GL_REPEAT',
     ],
   },
-  'PixelStoreAlignment': {
-    'type': 'GLint',
-    'is_complete': True,
-    'valid': [
-      '1',
-      '2',
-      '4',
-      '8',
-    ],
-    'invalid': [
-      '3',
-      '9',
-    ],
-  },
-  'PixelType': {
+  'TextureMinFilterMode': {
     'type': 'GLenum',
     'valid': [
-      'GL_UNSIGNED_BYTE',
-      'GL_UNSIGNED_SHORT_5_6_5',
-      'GL_UNSIGNED_SHORT_4_4_4_4',
-      'GL_UNSIGNED_SHORT_5_5_5_1',
-    ],
-    'valid_es3': [
-      'GL_BYTE',
-      'GL_UNSIGNED_SHORT',
-      'GL_SHORT',
-      'GL_UNSIGNED_INT',
-      'GL_INT',
-      'GL_HALF_FLOAT',
-      'GL_FLOAT',
-      'GL_UNSIGNED_INT_2_10_10_10_REV',
-      'GL_UNSIGNED_INT_10F_11F_11F_REV',
-      'GL_UNSIGNED_INT_5_9_9_9_REV',
-      'GL_UNSIGNED_INT_24_8',
-      'GL_FLOAT_32_UNSIGNED_INT_24_8_REV',
+      'GL_NEAREST',
     ],
     'invalid': [
-      'GL_UNSIGNED_BYTE_3_3_2',
+      'GL_NEAREST_MIPMAP_NEAREST',
     ],
   },
-  'TextureFormat': {
+  'TextureMagFilterMode': {
     'type': 'GLenum',
     'valid': [
-      'GL_ALPHA',
-      'GL_LUMINANCE',
-      'GL_LUMINANCE_ALPHA',
-      'GL_RGB',
-      'GL_RGBA',
-    ],
-    'valid_es3': [
-      'GL_RED',
-      'GL_RED_INTEGER',
-      'GL_RG',
-      'GL_RG_INTEGER',
-      'GL_RGB_INTEGER',
-      'GL_RGBA_INTEGER',
-      'GL_DEPTH_COMPONENT',
-      'GL_DEPTH_STENCIL',
+      'GL_NEAREST',
     ],
     'invalid': [
-      'GL_BGRA',
-      'GL_BGR',
-    ],
-  },
-  'TextureInternalFormat': {
-    'type': 'GLenum',
-    'valid': [
-      'GL_ALPHA',
-      'GL_LUMINANCE',
-      'GL_LUMINANCE_ALPHA',
-      'GL_RGB',
-      'GL_RGBA',
-    ],
-    'valid_es3': [
-      'GL_R8',
-      'GL_R8_SNORM',
-      'GL_R16F',
-      'GL_R32F',
-      'GL_R8UI',
-      'GL_R8I',
-      'GL_R16UI',
-      'GL_R16I',
-      'GL_R32UI',
-      'GL_R32I',
-      'GL_RG8',
-      'GL_RG8_SNORM',
-      'GL_RG16F',
-      'GL_RG32F',
-      'GL_RG8UI',
-      'GL_RG8I',
-      'GL_RG16UI',
-      'GL_RG16I',
-      'GL_RG32UI',
-      'GL_RG32I',
-      'GL_RGB8',
-      'GL_SRGB8',
-      'GL_RGB565',
-      'GL_RGB8_SNORM',
-      'GL_R11F_G11F_B10F',
-      'GL_RGB9_E5',
-      'GL_RGB16F',
-      'GL_RGB32F',
-      'GL_RGB8UI',
-      'GL_RGB8I',
-      'GL_RGB16UI',
-      'GL_RGB16I',
-      'GL_RGB32UI',
-      'GL_RGB32I',
-      'GL_RGBA8',
-      'GL_SRGB8_ALPHA8',
-      'GL_RGBA8_SNORM',
-      'GL_RGB5_A1',
-      'GL_RGBA4',
-      'GL_RGB10_A2',
-      'GL_RGBA16F',
-      'GL_RGBA32F',
-      'GL_RGBA8UI',
-      'GL_RGBA8I',
-      'GL_RGB10_A2UI',
-      'GL_RGBA16UI',
-      'GL_RGBA16I',
-      'GL_RGBA32UI',
-      'GL_RGBA32I',
-      # The DEPTH/STENCIL formats are not supported in CopyTexImage2D.
-      # We will reject them dynamically in GPU command buffer.
-      'GL_DEPTH_COMPONENT16',
-      'GL_DEPTH_COMPONENT24',
-      'GL_DEPTH_COMPONENT32F',
-      'GL_DEPTH24_STENCIL8',
-      'GL_DEPTH32F_STENCIL8',
-    ],
-    'invalid': [
-      'GL_BGRA',
-      'GL_BGR',
-    ],
-  },
-  'TextureInternalFormatStorage': {
-    'type': 'GLenum',
-    'valid': [
-      'GL_RGB565',
-      'GL_RGBA4',
-      'GL_RGB5_A1',
-      'GL_ALPHA8_EXT',
-      'GL_LUMINANCE8_EXT',
-      'GL_LUMINANCE8_ALPHA8_EXT',
-      'GL_RGB8_OES',
-      'GL_RGBA8_OES',
-    ],
-    'valid_es3': [
-      'GL_R8',
-      'GL_R8_SNORM',
-      'GL_R16F',
-      'GL_R32F',
-      'GL_R8UI',
-      'GL_R8I',
-      'GL_R16UI',
-      'GL_R16I',
-      'GL_R32UI',
-      'GL_R32I',
-      'GL_RG8',
-      'GL_RG8_SNORM',
-      'GL_RG16F',
-      'GL_RG32F',
-      'GL_RG8UI',
-      'GL_RG8I',
-      'GL_RG16UI',
-      'GL_RG16I',
-      'GL_RG32UI',
-      'GL_RG32I',
-      'GL_RGB8',
-      'GL_SRGB8',
-      'GL_RGB8_SNORM',
-      'GL_R11F_G11F_B10F',
-      'GL_RGB9_E5',
-      'GL_RGB16F',
-      'GL_RGB32F',
-      'GL_RGB8UI',
-      'GL_RGB8I',
-      'GL_RGB16UI',
-      'GL_RGB16I',
-      'GL_RGB32UI',
-      'GL_RGB32I',
-      'GL_RGBA8',
-      'GL_SRGB8_ALPHA8',
-      'GL_RGBA8_SNORM',
-      'GL_RGB10_A2',
-      'GL_RGBA16F',
-      'GL_RGBA32F',
-      'GL_RGBA8UI',
-      'GL_RGBA8I',
-      'GL_RGB10_A2UI',
-      'GL_RGBA16UI',
-      'GL_RGBA16I',
-      'GL_RGBA32UI',
-      'GL_RGBA32I',
-      'GL_DEPTH_COMPONENT16',
-      'GL_DEPTH_COMPONENT24',
-      'GL_DEPTH_COMPONENT32F',
-      'GL_DEPTH24_STENCIL8',
-      'GL_DEPTH32F_STENCIL8',
-    ],
-    'deprecated_es3': [
-      'GL_ALPHA8_EXT',
-      'GL_LUMINANCE8_EXT',
-      'GL_LUMINANCE8_ALPHA8_EXT',
-      'GL_ALPHA16F_EXT',
-      'GL_LUMINANCE16F_EXT',
-      'GL_LUMINANCE_ALPHA16F_EXT',
-      'GL_ALPHA32F_EXT',
-      'GL_LUMINANCE32F_EXT',
-      'GL_LUMINANCE_ALPHA32F_EXT',
-    ],
-  },
-  'TextureBorder': {
-    'type': 'GLint',
-    'is_complete': True,
-    'valid': [
-      '0',
-    ],
-    'invalid': [
-      '1',
+      'GL_LINEAR',
     ],
   },
   'ResetStatus': {
@@ -527,14 +107,33 @@
       'GL_UNKNOWN_CONTEXT_RESET_ARB',
     ],
   },
-  'ClientBufferUsage': {
-    'type': 'GLenum',
-    'is_complete': True,
+  'gfx::BufferUsage': {
+    'type': 'gfx::BufferUsage',
     'valid': [
-      'GL_SCANOUT_CHROMIUM',
+      'gfx::BufferUsage::GPU_READ',
+      'gfx::BufferUsage::SCANOUT',
+      'gfx::BufferUsage::GPU_READ_CPU_READ_WRITE',
+      'gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT',
     ],
     'invalid': [
-      'GL_NONE',
+      'gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE',
+    ],
+  },
+  'viz::ResourceFormat': {
+    'type': 'viz::ResourceFormat',
+    'is_complete': True,
+    'valid': [
+      'viz::ResourceFormat::RGBA_8888',
+      'viz::ResourceFormat::RGBA_4444',
+      'viz::ResourceFormat::BGRA_8888',
+      'viz::ResourceFormat::ALPHA_8',
+      'viz::ResourceFormat::LUMINANCE_8',
+      'viz::ResourceFormat::RGB_565',
+      'viz::ResourceFormat::ETC1',
+      'viz::ResourceFormat::RED_8',
+      'viz::ResourceFormat::LUMINANCE_F16',
+      'viz::ResourceFormat::RGBA_F16',
+      'viz::ResourceFormat::R16_EXT',
     ],
   },
 }
@@ -547,12 +146,10 @@
 #
 # Must match function names specified in "raster_cmd_buffer_functions.txt".
 #
-# cmd_comment:  A comment added to the cmd format.
 # type:         defines which handler will be used to generate code.
 # decoder_func: defines which function to call in the decoder to execute the
 #               corresponding GL command. If not specified the GL command will
 #               be called directly.
-# gl_test_func: GL function that is expected to be called when testing.
 # cmd_args:     The arguments to use for the command. This overrides generating
 #               them based on the GL function arguments.
 # data_transfer_methods: Array of methods that are used for transfering the
@@ -567,78 +164,37 @@
 #               command.
 # internal:     If true, this is an internal command only, not exposed to the
 #               client.
-# needs_size:   If True a data_size field is added to the command.
 # count:        The number of units per element. For PUTn or PUT types.
 # use_count_func: If True the actual data count needs to be computed; the count
 #               argument specifies the maximum count.
 # unit_test:    If False no service side unit test will be generated.
 # client_test:  If False no client side unit test will be generated.
 # expectation:  If False the unit test will have no expected calls.
-# gen_func:     Name of function that generates GL resource for corresponding
-#               bind function.
-# states:       array of states that get set by this function corresponding to
-#               the given arguments
-# state_flag:   name of flag that is set to true when function is called.
-# no_gl:        no GL function is called.
 # valid_args:   A dictionary of argument indices to args to use in unit tests
 #               when they can not be automatically determined.
-# pepper_interface: The pepper interface that is used for this extension
-# pepper_name:  The name of the function as exposed to pepper.
-# pepper_args:  A string representing the argument list (what would appear in
-#               C/C++ between the parentheses for the function declaration)
-#               that the Pepper API expects for this function. Use this only if
-#               the stable Pepper API differs from the GLES2 argument list.
 # invalid_test: False if no invalid test needed.
-# shadowed:     True = the value is shadowed so no glGetXXX call will be made.
-# first_element_only: For PUT types, True if only the first element of an
-#               array is used and we end up calling the single value
-#               corresponding function. eg. TexParameteriv -> TexParameteri
-# extension:    Function is an extension to GL and should not be exposed to
-#               pepper unless pepper_interface is defined.
-# extension_flag: Function is an extension and should be enabled only when
-#               the corresponding feature info flag is enabled. Implies
-#               'extension': True.
 # not_shared:   For GENn types, True if objects can't be shared between contexts
-# es3:          ES3 API. True if the function requires an ES3 or WebGL2 context.
 
 _FUNCTION_INFO = {
-  # TODO(backer): Kept for unittests remove once new raster API implemented.
- 'BindTexture': {
-    'type': 'Bind',
-    'internal' : True,
-    'decoder_func': 'DoBindTexture',
-    'gen_func': 'GenTextures',
-    # TODO: remove this once client side caching works.
-    'client_test': False,
-    'unit_test': False,
+  'CreateAndConsumeTexture': {
+    'type': 'NoCommand',
     'trace_level': 2,
   },
-  'CompressedTexSubImage2D': {
-    'type': 'Custom',
-    'data_transfer_methods': ['bucket', 'shm'],
-    'trace_level': 1,
-  },
-  'CopyTexImage2D': {
-    'decoder_func': 'DoCopyTexImage2D',
-    'unit_test': False,
-    'trace_level': 1,
-  },
-  'CopyTexSubImage2D': {
-    'decoder_func': 'DoCopyTexSubImage2D',
-    'trace_level': 1,
-  },
   'CreateImageCHROMIUM': {
     'type': 'NoCommand',
     'cmd_args':
         'ClientBuffer buffer, GLsizei width, GLsizei height, '
         'GLenum internalformat',
     'result': ['GLuint'],
-    'extension': "CHROMIUM_image",
     'trace_level': 1,
   },
+  'CopySubTexture': {
+    'decoder_func': 'DoCopySubTexture',
+    'unit_test': False,
+    'trace_level': 2,
+  },
   'DestroyImageCHROMIUM': {
     'type': 'NoCommand',
-    'extension': "CHROMIUM_image",
     'trace_level': 1,
   },
   'DeleteTextures': {
@@ -657,13 +213,8 @@
     'decoder_func': 'DoFlush',
     'trace_level': 1,
   },
-  # TODO(backer): Kept for unittests remove once new raster API implemented.
-  'GenTextures': {
-    'type': 'GENn',
-    'internal' : True,
-    'gl_test_func': 'glGenTextures',
-    'resource_type': 'Texture',
-    'resource_types': 'Textures',
+  'GenMailbox': {
+    'type': 'NoCommand',
   },
   'GetError': {
     'type': 'Is',
@@ -674,7 +225,6 @@
   },
   'GetGraphicsResetStatusKHR': {
     'type': 'NoCommand',
-    'extension': True,
     'trace_level': 1,
   },
   'GetIntegerv': {
@@ -683,12 +233,26 @@
     'decoder_func': 'DoGetIntegerv',
     'client_test': False,
   },
+  'ProduceTextureDirect': {
+    'decoder_func': 'DoProduceTextureDirect',
+    'impl_func': False,
+    'type': 'PUT',
+    'count': 16,  # GL_MAILBOX_SIZE_CHROMIUM
+    'unit_test': False,
+    'client_test': False,
+    'trace_level': 1,
+  },
   'TexParameteri': {
     'decoder_func': 'DoTexParameteri',
+    'unit_test' : False,
     'valid_args': {
       '2': 'GL_NEAREST'
     },
   },
+  'TexStorage2D': {
+    'decoder_func': 'DoTexStorage2D',
+    'unit_test': False,
+  },
   'WaitSync': {
     'type': 'Custom',
     'cmd_args': 'GLuint sync, GLbitfieldSyncFlushFlags flags, '
@@ -701,7 +265,6 @@
   'CompressedCopyTextureCHROMIUM': {
     'decoder_func': 'DoCompressedCopyTextureCHROMIUM',
     'unit_test': False,
-    'extension': 'CHROMIUM_copy_compressed_texture',
   },
   'GenQueriesEXT': {
     'type': 'GENn',
@@ -711,7 +274,6 @@
     'unit_test': False,
     'pepper_interface': 'Query',
     'not_shared': 'True',
-    'extension': "occlusion_query_EXT",
   },
   'DeleteQueriesEXT': {
     'type': 'DELn',
@@ -720,7 +282,6 @@
     'resource_types': 'Queries',
     'unit_test': False,
     'pepper_interface': 'Query',
-    'extension': "occlusion_query_EXT",
   },
   'BeginQueryEXT': {
     'type': 'Custom',
@@ -729,7 +290,6 @@
     'data_transfer_methods': ['shm'],
     'gl_test_func': 'glBeginQuery',
     'pepper_interface': 'Query',
-    'extension': "occlusion_query_EXT",
   },
   'EndQueryEXT': {
     'type': 'Custom',
@@ -738,47 +298,46 @@
     'gl_test_func': 'glEndnQuery',
     'client_test': False,
     'pepper_interface': 'Query',
-    'extension': "occlusion_query_EXT",
   },
   'GetQueryObjectuivEXT': {
     'type': 'NoCommand',
     'gl_test_func': 'glGetQueryObjectuiv',
     'pepper_interface': 'Query',
-    'extension': "occlusion_query_EXT",
+  },
+  'BindTexImage2DCHROMIUM': {
+    'decoder_func': 'DoBindTexImage2DCHROMIUM',
+    'unit_test': False,
+  },
+  'ReleaseTexImage2DCHROMIUM': {
+    'decoder_func': 'DoReleaseTexImage2DCHROMIUM',
+    'unit_test': False,
   },
   'ShallowFlushCHROMIUM': {
     'type': 'NoCommand',
-    'extension': 'CHROMIUM_ordering_barrier',
   },
   'OrderingBarrierCHROMIUM': {
     'type': 'NoCommand',
-    'extension': 'CHROMIUM_ordering_barrier',
   },
   'InsertFenceSyncCHROMIUM': {
     'type': 'Custom',
     'internal': True,
     'impl_func': False,
     'cmd_args': 'GLuint64 release_count',
-    'extension': "CHROMIUM_sync_point",
     'trace_level': 1,
   },
   'LoseContextCHROMIUM': {
     'decoder_func': 'DoLoseContextCHROMIUM',
     'unit_test': False,
-    'extension': 'CHROMIUM_lose_context',
     'trace_level': 1,
   },
   'GenSyncTokenCHROMIUM': {
     'type': 'NoCommand',
-    'extension': "CHROMIUM_sync_point",
   },
   'GenUnverifiedSyncTokenCHROMIUM': {
     'type': 'NoCommand',
-    'extension': "CHROMIUM_sync_point",
   },
   'VerifySyncTokensCHROMIUM' : {
     'type': 'NoCommand',
-    'extension': "CHROMIUM_sync_point",
   },
   'WaitSyncTokenCHROMIUM': {
     'type': 'Custom',
@@ -787,7 +346,6 @@
                 'GLuint64 command_buffer_id, '
                 'GLuint64 release_count',
     'client_test': False,
-    'extension': "CHROMIUM_sync_point",
   },
   'InitializeDiscardableTextureCHROMIUM': {
     'type': 'Custom',
@@ -795,44 +353,35 @@
                 'uint32_t shm_offset',
     'impl_func': False,
     'client_test': False,
-    'extension': True,
   },
   'UnlockDiscardableTextureCHROMIUM': {
     'type': 'Custom',
     'cmd_args': 'GLuint texture_id',
     'impl_func': False,
     'client_test': False,
-    'extension': True,
   },
   'LockDiscardableTextureCHROMIUM': {
     'type': 'Custom',
     'cmd_args': 'GLuint texture_id',
     'impl_func': False,
     'client_test': False,
-    'extension': True,
   },
   'BeginRasterCHROMIUM': {
     'decoder_func': 'DoBeginRasterCHROMIUM',
     'internal': True,
     'impl_func': False,
     'unit_test': False,
-    'extension': 'CHROMIUM_raster_transport',
-    'extension_flag': 'chromium_raster_transport',
   },
   'RasterCHROMIUM': {
     'type': 'Data',
     'internal': True,
     'decoder_func': 'DoRasterCHROMIUM',
     'data_transfer_methods': ['shm'],
-    'extension': 'CHROMIUM_raster_transport',
-    'extension_flag': 'chromium_raster_transport',
   },
   'EndRasterCHROMIUM': {
     'decoder_func': 'DoEndRasterCHROMIUM',
     'impl_func': True,
     'unit_test': False,
-    'extension': 'CHROMIUM_raster_transport',
-    'extension_flag': 'chromium_raster_transport',
   },
   'CreateTransferCacheEntryINTERNAL': {
     'decoder_func': 'DoCreateTransferCacheEntryINTERNAL',
@@ -843,7 +392,6 @@
     'impl_func': True,
     'client_test': False,
     'unit_test': False,
-    'extension': True,
   },
   'DeleteTransferCacheEntryINTERNAL': {
     'decoder_func': 'DoDeleteTransferCacheEntryINTERNAL',
@@ -852,7 +400,6 @@
     'impl_func': True,
     'client_test': False,
     'unit_test': False,
-    'extension': True,
   },
   'UnlockTransferCacheEntryINTERNAL': {
     'decoder_func': 'DoUnlockTransferCacheEntryINTERNAL',
@@ -861,7 +408,21 @@
     'impl_func': True,
     'client_test': False,
     'unit_test': False,
-    'extension': True,
+  },
+  'CreateTexture': {
+    'type': 'Create',
+    'resource_type': 'Texture',
+    'resource_types': 'Textures',
+    'decoder_func': 'DoCreateTexture',
+    'not_shared': 'True',
+    'unit_test': False,
+  },
+  'SetColorSpaceMetadata': {
+    'type': 'Custom',
+    'impl_func': False,
+    'client_test': False,
+    'cmd_args': 'GLuint texture_id, GLuint shm_id, GLuint shm_offset, '
+                'GLsizei color_space_size',
   },
   'UnpremultiplyAndDitherCopyCHROMIUM': {
     'decoder_func': 'DoUnpremultiplyAndDitherCopyCHROMIUM',
@@ -870,8 +431,6 @@
     'client_test': False,
     'unit_test': False,
     'impl_func': True,
-    'extension': 'CHROMIUM_unpremultiply_and_dither_copy',
-    'extension_flag': 'unpremultiply_and_dither_copy',
   },
 }
 
@@ -936,11 +495,11 @@
   # gen.WriteServiceUnitTestsForExtensions(
   #   "gpu/command_buffer/service/"
   #   "raster_cmd_decoder_unittest_extensions_autogen.h")
-  # gen.WriteServiceUtilsHeader(
-  #   "gpu/command_buffer/service/raster_cmd_validation_autogen.h")
-  # gen.WriteServiceUtilsImplementation(
-  #   "gpu/command_buffer/service/"
-  #   "raster_cmd_validation_implementation_autogen.h")
+  gen.WriteServiceUtilsHeader(
+    "gpu/command_buffer/service/raster_cmd_validation_autogen.h")
+  gen.WriteServiceUtilsImplementation(
+    "gpu/command_buffer/service/"
+    "raster_cmd_validation_implementation_autogen.h")
 
   build_cmd_buffer_lib.Format(gen.generated_cpp_filenames)
 
diff --git a/gpu/command_buffer/client/BUILD.gn b/gpu/command_buffer/client/BUILD.gn
index 28a17d9..5e0bce18d 100644
--- a/gpu/command_buffer/client/BUILD.gn
+++ b/gpu/command_buffer/client/BUILD.gn
@@ -211,6 +211,7 @@
     "//gpu/command_buffer/common",
     "//gpu/command_buffer/common:gles2",
     "//gpu/command_buffer/common:gles2_utils",
+    "//gpu/command_buffer/common:raster",
     "//ui/gfx:color_space",
     "//ui/gfx/geometry",
     "//ui/gfx/ipc/color",
diff --git a/gpu/command_buffer/client/raster_cmd_helper_autogen.h b/gpu/command_buffer/client/raster_cmd_helper_autogen.h
index e67d590..b2156981 100644
--- a/gpu/command_buffer/client/raster_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/raster_cmd_helper_autogen.h
@@ -11,13 +11,6 @@
 #ifndef GPU_COMMAND_BUFFER_CLIENT_RASTER_CMD_HELPER_AUTOGEN_H_
 #define GPU_COMMAND_BUFFER_CLIENT_RASTER_CMD_HELPER_AUTOGEN_H_
 
-void BindTexture(GLenum target, GLuint texture) {
-  raster::cmds::BindTexture* c = GetCmdSpace<raster::cmds::BindTexture>();
-  if (c) {
-    c->Init(target, texture);
-  }
-}
-
 void DeleteTexturesImmediate(GLsizei n, const GLuint* textures) {
   const uint32_t size = raster::cmds::DeleteTexturesImmediate::ComputeSize(n);
   raster::cmds::DeleteTexturesImmediate* c =
@@ -42,15 +35,6 @@
   }
 }
 
-void GenTexturesImmediate(GLsizei n, GLuint* textures) {
-  const uint32_t size = raster::cmds::GenTexturesImmediate::ComputeSize(n);
-  raster::cmds::GenTexturesImmediate* c =
-      GetImmediateCmdSpaceTotalSize<raster::cmds::GenTexturesImmediate>(size);
-  if (c) {
-    c->Init(n, textures);
-  }
-}
-
 void GetError(uint32_t result_shm_id, uint32_t result_shm_offset) {
   raster::cmds::GetError* c = GetCmdSpace<raster::cmds::GetError>();
   if (c) {
@@ -67,13 +51,6 @@
   }
 }
 
-void TexParameteri(GLenum target, GLenum pname, GLint param) {
-  raster::cmds::TexParameteri* c = GetCmdSpace<raster::cmds::TexParameteri>();
-  if (c) {
-    c->Init(target, pname, param);
-  }
-}
-
 void GenQueriesEXTImmediate(GLsizei n, GLuint* queries) {
   const uint32_t size = raster::cmds::GenQueriesEXTImmediate::ComputeSize(n);
   raster::cmds::GenQueriesEXTImmediate* c =
@@ -245,4 +222,83 @@
   }
 }
 
+void CreateTexture(bool use_buffer,
+                   gfx::BufferUsage buffer_usage,
+                   viz::ResourceFormat format,
+                   uint32_t client_id) {
+  raster::cmds::CreateTexture* c = GetCmdSpace<raster::cmds::CreateTexture>();
+  if (c) {
+    c->Init(use_buffer, buffer_usage, format, client_id);
+  }
+}
+
+void SetColorSpaceMetadata(GLuint texture_id,
+                           GLuint shm_id,
+                           GLuint shm_offset,
+                           GLsizei color_space_size) {
+  raster::cmds::SetColorSpaceMetadata* c =
+      GetCmdSpace<raster::cmds::SetColorSpaceMetadata>();
+  if (c) {
+    c->Init(texture_id, shm_id, shm_offset, color_space_size);
+  }
+}
+
+void ProduceTextureDirectImmediate(GLuint texture, const GLbyte* mailbox) {
+  const uint32_t size =
+      raster::cmds::ProduceTextureDirectImmediate::ComputeSize();
+  raster::cmds::ProduceTextureDirectImmediate* c =
+      GetImmediateCmdSpaceTotalSize<
+          raster::cmds::ProduceTextureDirectImmediate>(size);
+  if (c) {
+    c->Init(texture, mailbox);
+  }
+}
+
+void TexParameteri(GLuint texture_id, GLenum pname, GLint param) {
+  raster::cmds::TexParameteri* c = GetCmdSpace<raster::cmds::TexParameteri>();
+  if (c) {
+    c->Init(texture_id, pname, param);
+  }
+}
+
+void BindTexImage2DCHROMIUM(GLuint texture_id, GLint image_id) {
+  raster::cmds::BindTexImage2DCHROMIUM* c =
+      GetCmdSpace<raster::cmds::BindTexImage2DCHROMIUM>();
+  if (c) {
+    c->Init(texture_id, image_id);
+  }
+}
+
+void ReleaseTexImage2DCHROMIUM(GLuint texture_id, GLint image_id) {
+  raster::cmds::ReleaseTexImage2DCHROMIUM* c =
+      GetCmdSpace<raster::cmds::ReleaseTexImage2DCHROMIUM>();
+  if (c) {
+    c->Init(texture_id, image_id);
+  }
+}
+
+void TexStorage2D(GLuint texture_id,
+                  GLsizei levels,
+                  GLsizei width,
+                  GLsizei height) {
+  raster::cmds::TexStorage2D* c = GetCmdSpace<raster::cmds::TexStorage2D>();
+  if (c) {
+    c->Init(texture_id, levels, width, height);
+  }
+}
+
+void CopySubTexture(GLuint source_id,
+                    GLuint dest_id,
+                    GLint xoffset,
+                    GLint yoffset,
+                    GLint x,
+                    GLint y,
+                    GLsizei width,
+                    GLsizei height) {
+  raster::cmds::CopySubTexture* c = GetCmdSpace<raster::cmds::CopySubTexture>();
+  if (c) {
+    c->Init(source_id, dest_id, xoffset, yoffset, x, y, width, height);
+  }
+}
+
 #endif  // GPU_COMMAND_BUFFER_CLIENT_RASTER_CMD_HELPER_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/raster_implementation.cc b/gpu/command_buffer/client/raster_implementation.cc
index ced4cbf..a71958323 100644
--- a/gpu/command_buffer/client/raster_implementation.cc
+++ b/gpu/command_buffer/client/raster_implementation.cc
@@ -143,8 +143,15 @@
 }
 
 IdAllocator* RasterImplementation::GetIdAllocator(IdNamespaces namespace_id) {
-  DCHECK_EQ(namespace_id, IdNamespaces::kQueries);
-  return &query_id_allocator_;
+  switch (namespace_id) {
+    case IdNamespaces::kQueries:
+      return &query_id_allocator_;
+    case IdNamespaces::kTextures:
+      return &texture_id_allocator_;
+    default:
+      DCHECK(false);
+      return nullptr;
+  }
 }
 
 void RasterImplementation::OnGpuControlLostContext() {
@@ -924,38 +931,6 @@
   NOTIMPLEMENTED();
   return 0;
 }
-void RasterImplementation::BindTexImage2DCHROMIUM(GLenum target,
-                                                  GLint imageId) {
-  NOTIMPLEMENTED();
-}
-void RasterImplementation::ReleaseTexImage2DCHROMIUM(GLenum target,
-                                                     GLint imageId) {
-  NOTIMPLEMENTED();
-}
-
-GLuint RasterImplementation::CreateTexture(bool use_buffer,
-                                           gfx::BufferUsage buffer_usage,
-                                           viz::ResourceFormat format) {
-  NOTIMPLEMENTED();
-  return 0;
-}
-
-void RasterImplementation::TexStorage2D(GLuint texture_id,
-                                        GLint levels,
-                                        GLsizei width,
-                                        GLsizei height) {
-  NOTIMPLEMENTED();
-}
-void RasterImplementation::CopySubTexture(GLuint source_id,
-                                          GLuint dest_id,
-                                          GLint xoffset,
-                                          GLint yoffset,
-                                          GLint x,
-                                          GLint y,
-                                          GLsizei width,
-                                          GLsizei height) {
-  NOTIMPLEMENTED();
-}
 void RasterImplementation::BeginRasterCHROMIUM(
     GLuint texture_id,
     GLuint sk_color,
diff --git a/gpu/command_buffer/client/raster_implementation.h b/gpu/command_buffer/client/raster_implementation.h
index 26342ca..9ce7ebe 100644
--- a/gpu/command_buffer/client/raster_implementation.h
+++ b/gpu/command_buffer/client/raster_implementation.h
@@ -28,6 +28,7 @@
 #include "gpu/command_buffer/common/context_result.h"
 #include "gpu/command_buffer/common/debug_marker_manager.h"
 #include "gpu/command_buffer/common/id_allocator.h"
+#include "gpu/command_buffer/common/raster_cmd_format.h"
 #include "gpu/raster_export.h"
 
 namespace gpu {
@@ -99,39 +100,6 @@
 #include "gpu/command_buffer/client/raster_implementation_autogen.h"
 
   // RasterInterface implementation.
-  // Texture objects.
-  GLuint CreateTexture(bool use_buffer,
-                       gfx::BufferUsage buffer_usage,
-                       viz::ResourceFormat format) override;
-  void SetColorSpaceMetadata(GLuint texture_id,
-                             GLColorSpace color_space) override;
-
-  // Mailboxes.
-  void GenMailbox(GLbyte* mailbox) override;
-  void ProduceTextureDirect(GLuint texture, const GLbyte* mailbox) override;
-  GLuint CreateAndConsumeTexture(bool use_buffer,
-                                 gfx::BufferUsage buffer_usage,
-                                 viz::ResourceFormat format,
-                                 const GLbyte* mailbox) override;
-
-  // Image objects.
-  void BindTexImage2DCHROMIUM(GLuint texture_id, GLint image_id) override;
-  void ReleaseTexImage2DCHROMIUM(GLuint texture_id, GLint image_id) override;
-
-  // Texture allocation and copying.
-  void TexStorage2D(GLuint texture_id,
-                    GLint levels,
-                    GLsizei width,
-                    GLsizei height) override;
-  void CopySubTexture(GLuint source_id,
-                      GLuint dest_id,
-                      GLint xoffset,
-                      GLint yoffset,
-                      GLint x,
-                      GLint y,
-                      GLsizei width,
-                      GLsizei height) override;
-
   void BeginRasterCHROMIUM(
       GLuint texture_id,
       GLuint sk_color,
@@ -180,7 +148,7 @@
  private:
   friend class RasterImplementationTest;
 
-  using IdNamespaces = gles2::id_namespaces::IdNamespaces;
+  using IdNamespaces = raster::id_namespaces::IdNamespaces;
 
   struct TextureUnit {
     TextureUnit() : bound_texture_2d(0) {}
diff --git a/gpu/command_buffer/client/raster_implementation_autogen.h b/gpu/command_buffer/client/raster_implementation_autogen.h
index 2c50af8..b2393ae 100644
--- a/gpu/command_buffer/client/raster_implementation_autogen.h
+++ b/gpu/command_buffer/client/raster_implementation_autogen.h
@@ -27,8 +27,6 @@
 
 void OrderingBarrierCHROMIUM() override;
 
-void TexParameteri(GLenum target, GLenum pname, GLint param) override;
-
 void GenQueriesEXT(GLsizei n, GLuint* queries) override;
 
 void DeleteQueriesEXT(GLsizei n, const GLuint* queries) override;
@@ -75,4 +73,40 @@
 
 void EndRasterCHROMIUM() override;
 
+GLuint CreateTexture(bool use_buffer,
+                     gfx::BufferUsage buffer_usage,
+                     viz::ResourceFormat format) override;
+
+void SetColorSpaceMetadata(GLuint texture_id,
+                           GLColorSpace color_space) override;
+
+void GenMailbox(GLbyte* mailbox) override;
+
+void ProduceTextureDirect(GLuint texture, const GLbyte* mailbox) override;
+
+GLuint CreateAndConsumeTexture(bool use_buffer,
+                               gfx::BufferUsage buffer_usage,
+                               viz::ResourceFormat format,
+                               const GLbyte* mailbox) override;
+
+void TexParameteri(GLuint texture_id, GLenum pname, GLint param) override;
+
+void BindTexImage2DCHROMIUM(GLuint texture_id, GLint image_id) override;
+
+void ReleaseTexImage2DCHROMIUM(GLuint texture_id, GLint image_id) override;
+
+void TexStorage2D(GLuint texture_id,
+                  GLsizei levels,
+                  GLsizei width,
+                  GLsizei height) override;
+
+void CopySubTexture(GLuint source_id,
+                    GLuint dest_id,
+                    GLint xoffset,
+                    GLint yoffset,
+                    GLint x,
+                    GLint y,
+                    GLsizei width,
+                    GLsizei height) override;
+
 #endif  // GPU_COMMAND_BUFFER_CLIENT_RASTER_IMPLEMENTATION_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/raster_implementation_gles.cc b/gpu/command_buffer/client/raster_implementation_gles.cc
index 90f4e81c..ff39b915 100644
--- a/gpu/command_buffer/client/raster_implementation_gles.cc
+++ b/gpu/command_buffer/client/raster_implementation_gles.cc
@@ -345,7 +345,7 @@
 }
 
 void RasterImplementationGLES::TexStorage2D(GLuint texture_id,
-                                            GLint levels,
+                                            GLsizei levels,
                                             GLsizei width,
                                             GLsizei height) {
   Texture* texture = EnsureTextureBound(GetTexture(texture_id));
diff --git a/gpu/command_buffer/client/raster_implementation_gles.h b/gpu/command_buffer/client/raster_implementation_gles.h
index 18659eb..cf26d477 100644
--- a/gpu/command_buffer/client/raster_implementation_gles.h
+++ b/gpu/command_buffer/client/raster_implementation_gles.h
@@ -84,7 +84,7 @@
 
   // Texture allocation and copying.
   void TexStorage2D(GLuint texture_id,
-                    GLint levels,
+                    GLsizei levels,
                     GLsizei width,
                     GLsizei height) override;
 
diff --git a/gpu/command_buffer/client/raster_implementation_impl_autogen.h b/gpu/command_buffer/client/raster_implementation_impl_autogen.h
index 2b281ec2..074e55c1 100644
--- a/gpu/command_buffer/client/raster_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/raster_implementation_impl_autogen.h
@@ -61,18 +61,6 @@
   });
   CheckGLError();
 }
-void RasterImplementation::TexParameteri(GLenum target,
-                                         GLenum pname,
-                                         GLint param) {
-  GPU_CLIENT_SINGLE_THREAD_CHECK();
-  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTexParameteri("
-                     << GLES2Util::GetStringTextureBindTarget(target) << ", "
-                     << GLES2Util::GetStringTextureParameter(pname) << ", "
-                     << param << ")");
-  helper_->TexParameteri(target, pname, param);
-  CheckGLError();
-}
-
 void RasterImplementation::GenQueriesEXT(GLsizei n, GLuint* queries) {
   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGenQueriesEXT(" << n << ", "
                      << static_cast<const void*>(queries) << ")");
@@ -168,4 +156,99 @@
   CheckGLError();
 }
 
+GLuint RasterImplementation::CreateTexture(bool use_buffer,
+                                           gfx::BufferUsage buffer_usage,
+                                           viz::ResourceFormat format) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glCreateTexture(" << use_buffer
+                     << ", " << static_cast<uint32_t>(buffer_usage) << ", "
+                     << static_cast<uint32_t>(format) << ")");
+  GLuint client_id;
+  IdAllocator* id_allocator = GetIdAllocator(IdNamespaces::kTextures);
+  client_id = id_allocator->AllocateID();
+  helper_->CreateTexture(use_buffer, buffer_usage, format, client_id);
+  GPU_CLIENT_LOG("returned " << client_id);
+  CheckGLError();
+  return client_id;
+}
+
+void RasterImplementation::TexParameteri(GLuint texture_id,
+                                         GLenum pname,
+                                         GLint param) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTexParameteri(" << texture_id
+                     << ", " << GLES2Util::GetStringTextureParameter(pname)
+                     << ", " << param << ")");
+  helper_->TexParameteri(texture_id, pname, param);
+  CheckGLError();
+}
+
+void RasterImplementation::BindTexImage2DCHROMIUM(GLuint texture_id,
+                                                  GLint image_id) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBindTexImage2DCHROMIUM("
+                     << texture_id << ", " << image_id << ")");
+  helper_->BindTexImage2DCHROMIUM(texture_id, image_id);
+  CheckGLError();
+}
+
+void RasterImplementation::ReleaseTexImage2DCHROMIUM(GLuint texture_id,
+                                                     GLint image_id) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glReleaseTexImage2DCHROMIUM("
+                     << texture_id << ", " << image_id << ")");
+  helper_->ReleaseTexImage2DCHROMIUM(texture_id, image_id);
+  CheckGLError();
+}
+
+void RasterImplementation::TexStorage2D(GLuint texture_id,
+                                        GLsizei levels,
+                                        GLsizei width,
+                                        GLsizei height) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTexStorage2D(" << texture_id
+                     << ", " << levels << ", " << width << ", " << height
+                     << ")");
+  if (levels < 0) {
+    SetGLError(GL_INVALID_VALUE, "glTexStorage2D", "levels < 0");
+    return;
+  }
+  if (width < 0) {
+    SetGLError(GL_INVALID_VALUE, "glTexStorage2D", "width < 0");
+    return;
+  }
+  if (height < 0) {
+    SetGLError(GL_INVALID_VALUE, "glTexStorage2D", "height < 0");
+    return;
+  }
+  helper_->TexStorage2D(texture_id, levels, width, height);
+  CheckGLError();
+}
+
+void RasterImplementation::CopySubTexture(GLuint source_id,
+                                          GLuint dest_id,
+                                          GLint xoffset,
+                                          GLint yoffset,
+                                          GLint x,
+                                          GLint y,
+                                          GLsizei width,
+                                          GLsizei height) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glCopySubTexture(" << source_id
+                     << ", " << dest_id << ", " << xoffset << ", " << yoffset
+                     << ", " << x << ", " << y << ", " << width << ", "
+                     << height << ")");
+  if (width < 0) {
+    SetGLError(GL_INVALID_VALUE, "glCopySubTexture", "width < 0");
+    return;
+  }
+  if (height < 0) {
+    SetGLError(GL_INVALID_VALUE, "glCopySubTexture", "height < 0");
+    return;
+  }
+  helper_->CopySubTexture(source_id, dest_id, xoffset, yoffset, x, y, width,
+                          height);
+  CheckGLError();
+}
+
 #endif  // GPU_COMMAND_BUFFER_CLIENT_RASTER_IMPLEMENTATION_IMPL_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/raster_implementation_unittest_autogen.h b/gpu/command_buffer/client/raster_implementation_unittest_autogen.h
index 345a7d5..36a40934 100644
--- a/gpu/command_buffer/client/raster_implementation_unittest_autogen.h
+++ b/gpu/command_buffer/client/raster_implementation_unittest_autogen.h
@@ -56,17 +56,6 @@
   EXPECT_EQ(static_cast<ResultType>(1), result);
 }
 
-TEST_F(RasterImplementationTest, TexParameteri) {
-  struct Cmds {
-    cmds::TexParameteri cmd;
-  };
-  Cmds expected;
-  expected.cmd.Init(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
-  gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
-}
-
 TEST_F(RasterImplementationTest, GenQueriesEXT) {
   GLuint ids[2] = {
       0,
@@ -132,4 +121,59 @@
   gl_->EndRasterCHROMIUM();
   EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
 }
+
+TEST_F(RasterImplementationTest, TexParameteri) {
+  struct Cmds {
+    cmds::TexParameteri cmd;
+  };
+  Cmds expected;
+  expected.cmd.Init(1, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+  gl_->TexParameteri(1, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(RasterImplementationTest, BindTexImage2DCHROMIUM) {
+  struct Cmds {
+    cmds::BindTexImage2DCHROMIUM cmd;
+  };
+  Cmds expected;
+  expected.cmd.Init(1, 2);
+
+  gl_->BindTexImage2DCHROMIUM(1, 2);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(RasterImplementationTest, ReleaseTexImage2DCHROMIUM) {
+  struct Cmds {
+    cmds::ReleaseTexImage2DCHROMIUM cmd;
+  };
+  Cmds expected;
+  expected.cmd.Init(1, 2);
+
+  gl_->ReleaseTexImage2DCHROMIUM(1, 2);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(RasterImplementationTest, TexStorage2D) {
+  struct Cmds {
+    cmds::TexStorage2D cmd;
+  };
+  Cmds expected;
+  expected.cmd.Init(1, 2, 3, 4);
+
+  gl_->TexStorage2D(1, 2, 3, 4);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(RasterImplementationTest, CopySubTexture) {
+  struct Cmds {
+    cmds::CopySubTexture cmd;
+  };
+  Cmds expected;
+  expected.cmd.Init(1, 2, 3, 4, 5, 6, 7, 8);
+
+  gl_->CopySubTexture(1, 2, 3, 4, 5, 6, 7, 8);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
 #endif  // GPU_COMMAND_BUFFER_CLIENT_RASTER_IMPLEMENTATION_UNITTEST_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/raster_interface.h b/gpu/command_buffer/client/raster_interface.h
index 444c7beb..7b4e8ac 100644
--- a/gpu/command_buffer/client/raster_interface.h
+++ b/gpu/command_buffer/client/raster_interface.h
@@ -8,7 +8,6 @@
 #include <GLES2/gl2.h>
 #include "base/compiler_specific.h"
 #include "components/viz/common/resources/resource_format.h"
-#include "ui/gfx/buffer_types.h"
 
 namespace cc {
 class DisplayItemList;
@@ -20,6 +19,7 @@
 class Rect;
 class Size;
 class Vector2dF;
+enum class BufferUsage;
 }  // namespace gfx
 
 extern "C" typedef struct _ClientBuffer* ClientBuffer;
@@ -35,39 +35,6 @@
   RasterInterface() {}
   virtual ~RasterInterface() {}
 
-  // Texture objects.
-  virtual GLuint CreateTexture(bool use_buffer,
-                               gfx::BufferUsage buffer_usage,
-                               viz::ResourceFormat format) = 0;
-  virtual void SetColorSpaceMetadata(GLuint texture_id,
-                                     GLColorSpace color_space) = 0;
-
-  // Mailboxes.
-  virtual void GenMailbox(GLbyte* mailbox) = 0;
-  virtual void ProduceTextureDirect(GLuint texture, const GLbyte* mailbox) = 0;
-  virtual GLuint CreateAndConsumeTexture(bool use_buffer,
-                                         gfx::BufferUsage buffer_usage,
-                                         viz::ResourceFormat format,
-                                         const GLbyte* mailbox) = 0;
-
-  // Image objects.
-  virtual void BindTexImage2DCHROMIUM(GLuint texture_id, GLint image_id) = 0;
-  virtual void ReleaseTexImage2DCHROMIUM(GLuint texture_id, GLint image_id) = 0;
-
-  // Texture allocation and copying.
-  virtual void TexStorage2D(GLuint texture_id,
-                            GLint levels,
-                            GLsizei width,
-                            GLsizei height) = 0;
-  virtual void CopySubTexture(GLuint source_id,
-                              GLuint dest_id,
-                              GLint xoffset,
-                              GLint yoffset,
-                              GLint x,
-                              GLint y,
-                              GLsizei width,
-                              GLsizei height) = 0;
-
   // OOP-Raster
   virtual void BeginRasterCHROMIUM(
       GLuint texture_id,
diff --git a/gpu/command_buffer/client/raster_interface_autogen.h b/gpu/command_buffer/client/raster_interface_autogen.h
index bc6be3e..e88018d 100644
--- a/gpu/command_buffer/client/raster_interface_autogen.h
+++ b/gpu/command_buffer/client/raster_interface_autogen.h
@@ -20,7 +20,6 @@
 virtual void GetIntegerv(GLenum pname, GLint* params) = 0;
 virtual void ShallowFlushCHROMIUM() = 0;
 virtual void OrderingBarrierCHROMIUM() = 0;
-virtual void TexParameteri(GLenum target, GLenum pname, GLint param) = 0;
 virtual void GenQueriesEXT(GLsizei n, GLuint* queries) = 0;
 virtual void DeleteQueriesEXT(GLsizei n, const GLuint* queries) = 0;
 virtual void BeginQueryEXT(GLenum target, GLuint id) = 0;
@@ -49,4 +48,30 @@
 virtual void UnlockDiscardableTextureCHROMIUM(GLuint texture_id) = 0;
 virtual bool LockDiscardableTextureCHROMIUM(GLuint texture_id) = 0;
 virtual void EndRasterCHROMIUM() = 0;
+virtual GLuint CreateTexture(bool use_buffer,
+                             gfx::BufferUsage buffer_usage,
+                             viz::ResourceFormat format) = 0;
+virtual void SetColorSpaceMetadata(GLuint texture_id,
+                                   GLColorSpace color_space) = 0;
+virtual void GenMailbox(GLbyte* mailbox) = 0;
+virtual void ProduceTextureDirect(GLuint texture, const GLbyte* mailbox) = 0;
+virtual GLuint CreateAndConsumeTexture(bool use_buffer,
+                                       gfx::BufferUsage buffer_usage,
+                                       viz::ResourceFormat format,
+                                       const GLbyte* mailbox) = 0;
+virtual void TexParameteri(GLuint texture_id, GLenum pname, GLint param) = 0;
+virtual void BindTexImage2DCHROMIUM(GLuint texture_id, GLint image_id) = 0;
+virtual void ReleaseTexImage2DCHROMIUM(GLuint texture_id, GLint image_id) = 0;
+virtual void TexStorage2D(GLuint texture_id,
+                          GLsizei levels,
+                          GLsizei width,
+                          GLsizei height) = 0;
+virtual void CopySubTexture(GLuint source_id,
+                            GLuint dest_id,
+                            GLint xoffset,
+                            GLint yoffset,
+                            GLint x,
+                            GLint y,
+                            GLsizei width,
+                            GLsizei height) = 0;
 #endif  // GPU_COMMAND_BUFFER_CLIENT_RASTER_INTERFACE_AUTOGEN_H_
diff --git a/gpu/command_buffer/common/BUILD.gn b/gpu/command_buffer/common/BUILD.gn
index 7b11ef4..2c655cd7 100644
--- a/gpu/command_buffer/common/BUILD.gn
+++ b/gpu/command_buffer/common/BUILD.gn
@@ -31,6 +31,18 @@
   }
 }
 
+group("raster") {
+  if (is_component_build) {
+    public_deps = [
+      "//gpu:gles2",
+    ]
+  } else {
+    public_deps = [
+      ":raster_sources",
+    ]
+  }
+}
+
 source_set("common_sources") {
   visibility = [ "//gpu/*" ]
 
@@ -50,8 +62,11 @@
     "context_creation_attribs.cc",
     "context_creation_attribs.h",
     "context_result.h",
+    "debug_marker_manager.cc",
+    "debug_marker_manager.h",
     "discardable_handle.cc",
     "discardable_handle.h",
+    "gl2_types.h",
     "gpu_memory_buffer_support.cc",
     "gpu_memory_buffer_support.h",
     "id_allocator.cc",
@@ -93,21 +108,11 @@
   visibility = [ "//gpu/*" ]
 
   sources = [
-    "debug_marker_manager.cc",
-    "debug_marker_manager.h",
-    "gl2_types.h",
     "gles2_cmd_format.cc",
     "gles2_cmd_format.h",
     "gles2_cmd_format_autogen.h",
     "gles2_cmd_ids.h",
     "gles2_cmd_ids_autogen.h",
-
-    # TODO(backer): Separate into distinct raster target.
-    "raster_cmd_format.cc",
-    "raster_cmd_format.h",
-    "raster_cmd_format_autogen.h",
-    "raster_cmd_ids.h",
-    "raster_cmd_ids_autogen.h",
   ]
 
   configs += [ "//gpu:gpu_gles2_implementation" ]
@@ -121,6 +126,29 @@
   ]
 }
 
+source_set("raster_sources") {
+  visibility = [ "//gpu/*" ]
+
+  sources = [
+    "raster_cmd_format.cc",
+    "raster_cmd_format.h",
+    "raster_cmd_format_autogen.h",
+    "raster_cmd_ids.h",
+    "raster_cmd_ids_autogen.h",
+  ]
+
+  configs += [ "//gpu:raster_implementation" ]
+
+  deps = [
+    ":gles2_utils",
+    "//base",
+  ]
+  public_deps = [
+    ":common",
+    "//components/viz/common:resource_format",
+  ]
+}
+
 component("gles2_utils") {
   sources = [
     "gles2_cmd_utils.cc",
diff --git a/gpu/command_buffer/common/DEPS b/gpu/command_buffer/common/DEPS
index 5945e36..c7504c1bf 100644
--- a/gpu/command_buffer/common/DEPS
+++ b/gpu/command_buffer/common/DEPS
@@ -1,3 +1,7 @@
+include_rules = [
+  "+components/viz/common/resources/resource_format.h",
+  "+components/viz/common/resources/resource_format_utils.h",
+]
 specific_include_rules = {
   "unittest_main\.cc": [
     "+mojo/edk/embedder/embedder.h",
diff --git a/gpu/command_buffer/common/debug_marker_manager.h b/gpu/command_buffer/common/debug_marker_manager.h
index 6d8b1a771..abe319d 100644
--- a/gpu/command_buffer/common/debug_marker_manager.h
+++ b/gpu/command_buffer/common/debug_marker_manager.h
@@ -8,13 +8,13 @@
 #include <string>
 
 #include "base/containers/stack.h"
-#include "gpu/gpu_gles2_export.h"
+#include "gpu/gpu_export.h"
 
 namespace gpu {
 namespace gles2 {
 
 // Tracks debug marker.
-class GPU_GLES2_EXPORT DebugMarkerManager {
+class GPU_EXPORT DebugMarkerManager {
  public:
    DebugMarkerManager();
    ~DebugMarkerManager();
@@ -60,4 +60,3 @@
 }  // namespace gpu
 
 #endif  // GPU_COMMAND_BUFFER_SERVICE_DEBUG_MARKER_MANAGER_H_
-
diff --git a/gpu/command_buffer/common/raster_cmd_format.h b/gpu/command_buffer/common/raster_cmd_format.h
index b8a7e98..9ecdd289 100644
--- a/gpu/command_buffer/common/raster_cmd_format.h
+++ b/gpu/command_buffer/common/raster_cmd_format.h
@@ -14,12 +14,14 @@
 #include "base/atomicops.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "components/viz/common/resources/resource_format.h"
 #include "gpu/command_buffer/common/bitfield_helpers.h"
 #include "gpu/command_buffer/common/cmd_buffer_common.h"
 #include "gpu/command_buffer/common/constants.h"
 #include "gpu/command_buffer/common/gl2_types.h"
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
 #include "gpu/command_buffer/common/raster_cmd_ids.h"
+#include "ui/gfx/buffer_types.h"
 
 namespace gpu {
 namespace raster {
@@ -30,6 +32,12 @@
               "pragma pack alignment must be equal to "
               "GPU_COMMAND_BUFFER_ENTRY_ALIGNMENT");
 
+namespace id_namespaces {
+
+enum class IdNamespaces { kQueries, kTextures };
+
+}  // namespace id_namespaces
+
 // Used for some glGetXXX commands that return a result through a pointer. We
 // need to know if the command succeeded or not and the size of the result. If
 // the command failed its result size will 0.
diff --git a/gpu/command_buffer/common/raster_cmd_format_autogen.h b/gpu/command_buffer/common/raster_cmd_format_autogen.h
index 27b2434..46bd4a0 100644
--- a/gpu/command_buffer/common/raster_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_format_autogen.h
@@ -13,42 +13,6 @@
 
 #define GL_SCANOUT_CHROMIUM 0x6000
 
-struct BindTexture {
-  typedef BindTexture ValueType;
-  static const CommandId kCmdId = kBindTexture;
-  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
-  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(2);
-
-  static uint32_t ComputeSize() {
-    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
-  }
-
-  void SetHeader() { header.SetCmd<ValueType>(); }
-
-  void Init(GLenum _target, GLuint _texture) {
-    SetHeader();
-    target = _target;
-    texture = _texture;
-  }
-
-  void* Set(void* cmd, GLenum _target, GLuint _texture) {
-    static_cast<ValueType*>(cmd)->Init(_target, _texture);
-    return NextCmdAddress<ValueType>(cmd);
-  }
-
-  gpu::CommandHeader header;
-  uint32_t target;
-  uint32_t texture;
-};
-
-static_assert(sizeof(BindTexture) == 12, "size of BindTexture should be 12");
-static_assert(offsetof(BindTexture, header) == 0,
-              "offset of BindTexture header should be 0");
-static_assert(offsetof(BindTexture, target) == 4,
-              "offset of BindTexture target should be 4");
-static_assert(offsetof(BindTexture, texture) == 8,
-              "offset of BindTexture texture should be 8");
-
 struct DeleteTexturesImmediate {
   typedef DeleteTexturesImmediate ValueType;
   static const CommandId kCmdId = kDeleteTexturesImmediate;
@@ -143,48 +107,6 @@
 static_assert(offsetof(Flush, header) == 0,
               "offset of Flush header should be 0");
 
-struct GenTexturesImmediate {
-  typedef GenTexturesImmediate ValueType;
-  static const CommandId kCmdId = kGenTexturesImmediate;
-  static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN;
-  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
-
-  static uint32_t ComputeDataSize(GLsizei _n) {
-    return static_cast<uint32_t>(sizeof(GLuint) * _n);  // NOLINT
-  }
-
-  static uint32_t ComputeSize(GLsizei _n) {
-    return static_cast<uint32_t>(sizeof(ValueType) +
-                                 ComputeDataSize(_n));  // NOLINT
-  }
-
-  void SetHeader(GLsizei _n) {
-    header.SetCmdByTotalSize<ValueType>(ComputeSize(_n));
-  }
-
-  void Init(GLsizei _n, GLuint* _textures) {
-    SetHeader(_n);
-    n = _n;
-    memcpy(ImmediateDataAddress(this), _textures, ComputeDataSize(_n));
-  }
-
-  void* Set(void* cmd, GLsizei _n, GLuint* _textures) {
-    static_cast<ValueType*>(cmd)->Init(_n, _textures);
-    const uint32_t size = ComputeSize(_n);
-    return NextImmediateCmdAddressTotalSize<ValueType>(cmd, size);
-  }
-
-  gpu::CommandHeader header;
-  int32_t n;
-};
-
-static_assert(sizeof(GenTexturesImmediate) == 8,
-              "size of GenTexturesImmediate should be 8");
-static_assert(offsetof(GenTexturesImmediate, header) == 0,
-              "offset of GenTexturesImmediate header should be 0");
-static_assert(offsetof(GenTexturesImmediate, n) == 4,
-              "offset of GenTexturesImmediate n should be 4");
-
 struct GetError {
   typedef GetError ValueType;
   static const CommandId kCmdId = kGetError;
@@ -271,47 +193,6 @@
 static_assert(offsetof(GetIntegerv, params_shm_offset) == 12,
               "offset of GetIntegerv params_shm_offset should be 12");
 
-struct TexParameteri {
-  typedef TexParameteri ValueType;
-  static const CommandId kCmdId = kTexParameteri;
-  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
-  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
-
-  static uint32_t ComputeSize() {
-    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
-  }
-
-  void SetHeader() { header.SetCmd<ValueType>(); }
-
-  void Init(GLenum _target, GLenum _pname, GLint _param) {
-    SetHeader();
-    target = _target;
-    pname = _pname;
-    param = _param;
-  }
-
-  void* Set(void* cmd, GLenum _target, GLenum _pname, GLint _param) {
-    static_cast<ValueType*>(cmd)->Init(_target, _pname, _param);
-    return NextCmdAddress<ValueType>(cmd);
-  }
-
-  gpu::CommandHeader header;
-  uint32_t target;
-  uint32_t pname;
-  int32_t param;
-};
-
-static_assert(sizeof(TexParameteri) == 16,
-              "size of TexParameteri should be 16");
-static_assert(offsetof(TexParameteri, header) == 0,
-              "offset of TexParameteri header should be 0");
-static_assert(offsetof(TexParameteri, target) == 4,
-              "offset of TexParameteri target should be 4");
-static_assert(offsetof(TexParameteri, pname) == 8,
-              "offset of TexParameteri pname should be 8");
-static_assert(offsetof(TexParameteri, param) == 12,
-              "offset of TexParameteri param should be 12");
-
 struct GenQueriesEXTImmediate {
   typedef GenQueriesEXTImmediate ValueType;
   static const CommandId kCmdId = kGenQueriesEXTImmediate;
@@ -1155,4 +1036,392 @@
     offsetof(UnlockTransferCacheEntryINTERNAL, entry_id) == 8,
     "offset of UnlockTransferCacheEntryINTERNAL entry_id should be 8");
 
+struct CreateTexture {
+  typedef CreateTexture ValueType;
+  static const CommandId kCmdId = kCreateTexture;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(bool _use_buffer,
+            gfx::BufferUsage _buffer_usage,
+            viz::ResourceFormat _format,
+            uint32_t _client_id) {
+    SetHeader();
+    use_buffer = _use_buffer;
+    buffer_usage = static_cast<uint32_t>(_buffer_usage);
+    format = static_cast<uint32_t>(_format);
+    client_id = _client_id;
+  }
+
+  void* Set(void* cmd,
+            bool _use_buffer,
+            gfx::BufferUsage _buffer_usage,
+            viz::ResourceFormat _format,
+            uint32_t _client_id) {
+    static_cast<ValueType*>(cmd)->Init(_use_buffer, _buffer_usage, _format,
+                                       _client_id);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t use_buffer;
+  uint32_t buffer_usage;
+  uint32_t format;
+  uint32_t client_id;
+};
+
+static_assert(sizeof(CreateTexture) == 20,
+              "size of CreateTexture should be 20");
+static_assert(offsetof(CreateTexture, header) == 0,
+              "offset of CreateTexture header should be 0");
+static_assert(offsetof(CreateTexture, use_buffer) == 4,
+              "offset of CreateTexture use_buffer should be 4");
+static_assert(offsetof(CreateTexture, buffer_usage) == 8,
+              "offset of CreateTexture buffer_usage should be 8");
+static_assert(offsetof(CreateTexture, format) == 12,
+              "offset of CreateTexture format should be 12");
+static_assert(offsetof(CreateTexture, client_id) == 16,
+              "offset of CreateTexture client_id should be 16");
+
+struct SetColorSpaceMetadata {
+  typedef SetColorSpaceMetadata ValueType;
+  static const CommandId kCmdId = kSetColorSpaceMetadata;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLuint _texture_id,
+            GLuint _shm_id,
+            GLuint _shm_offset,
+            GLsizei _color_space_size) {
+    SetHeader();
+    texture_id = _texture_id;
+    shm_id = _shm_id;
+    shm_offset = _shm_offset;
+    color_space_size = _color_space_size;
+  }
+
+  void* Set(void* cmd,
+            GLuint _texture_id,
+            GLuint _shm_id,
+            GLuint _shm_offset,
+            GLsizei _color_space_size) {
+    static_cast<ValueType*>(cmd)->Init(_texture_id, _shm_id, _shm_offset,
+                                       _color_space_size);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t texture_id;
+  uint32_t shm_id;
+  uint32_t shm_offset;
+  int32_t color_space_size;
+};
+
+static_assert(sizeof(SetColorSpaceMetadata) == 20,
+              "size of SetColorSpaceMetadata should be 20");
+static_assert(offsetof(SetColorSpaceMetadata, header) == 0,
+              "offset of SetColorSpaceMetadata header should be 0");
+static_assert(offsetof(SetColorSpaceMetadata, texture_id) == 4,
+              "offset of SetColorSpaceMetadata texture_id should be 4");
+static_assert(offsetof(SetColorSpaceMetadata, shm_id) == 8,
+              "offset of SetColorSpaceMetadata shm_id should be 8");
+static_assert(offsetof(SetColorSpaceMetadata, shm_offset) == 12,
+              "offset of SetColorSpaceMetadata shm_offset should be 12");
+static_assert(offsetof(SetColorSpaceMetadata, color_space_size) == 16,
+              "offset of SetColorSpaceMetadata color_space_size should be 16");
+
+struct ProduceTextureDirectImmediate {
+  typedef ProduceTextureDirectImmediate ValueType;
+  static const CommandId kCmdId = kProduceTextureDirectImmediate;
+  static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN;
+  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(1);
+
+  static uint32_t ComputeDataSize() {
+    return static_cast<uint32_t>(sizeof(GLbyte) * 16);
+  }
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType) + ComputeDataSize());
+  }
+
+  void SetHeader() { header.SetCmdByTotalSize<ValueType>(ComputeSize()); }
+
+  void Init(GLuint _texture, const GLbyte* _mailbox) {
+    SetHeader();
+    texture = _texture;
+    memcpy(ImmediateDataAddress(this), _mailbox, ComputeDataSize());
+  }
+
+  void* Set(void* cmd, GLuint _texture, const GLbyte* _mailbox) {
+    static_cast<ValueType*>(cmd)->Init(_texture, _mailbox);
+    const uint32_t size = ComputeSize();
+    return NextImmediateCmdAddressTotalSize<ValueType>(cmd, size);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t texture;
+};
+
+static_assert(sizeof(ProduceTextureDirectImmediate) == 8,
+              "size of ProduceTextureDirectImmediate should be 8");
+static_assert(offsetof(ProduceTextureDirectImmediate, header) == 0,
+              "offset of ProduceTextureDirectImmediate header should be 0");
+static_assert(offsetof(ProduceTextureDirectImmediate, texture) == 4,
+              "offset of ProduceTextureDirectImmediate texture should be 4");
+
+struct TexParameteri {
+  typedef TexParameteri ValueType;
+  static const CommandId kCmdId = kTexParameteri;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLuint _texture_id, GLenum _pname, GLint _param) {
+    SetHeader();
+    texture_id = _texture_id;
+    pname = _pname;
+    param = _param;
+  }
+
+  void* Set(void* cmd, GLuint _texture_id, GLenum _pname, GLint _param) {
+    static_cast<ValueType*>(cmd)->Init(_texture_id, _pname, _param);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t texture_id;
+  uint32_t pname;
+  int32_t param;
+};
+
+static_assert(sizeof(TexParameteri) == 16,
+              "size of TexParameteri should be 16");
+static_assert(offsetof(TexParameteri, header) == 0,
+              "offset of TexParameteri header should be 0");
+static_assert(offsetof(TexParameteri, texture_id) == 4,
+              "offset of TexParameteri texture_id should be 4");
+static_assert(offsetof(TexParameteri, pname) == 8,
+              "offset of TexParameteri pname should be 8");
+static_assert(offsetof(TexParameteri, param) == 12,
+              "offset of TexParameteri param should be 12");
+
+struct BindTexImage2DCHROMIUM {
+  typedef BindTexImage2DCHROMIUM ValueType;
+  static const CommandId kCmdId = kBindTexImage2DCHROMIUM;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLuint _texture_id, GLint _image_id) {
+    SetHeader();
+    texture_id = _texture_id;
+    image_id = _image_id;
+  }
+
+  void* Set(void* cmd, GLuint _texture_id, GLint _image_id) {
+    static_cast<ValueType*>(cmd)->Init(_texture_id, _image_id);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t texture_id;
+  int32_t image_id;
+};
+
+static_assert(sizeof(BindTexImage2DCHROMIUM) == 12,
+              "size of BindTexImage2DCHROMIUM should be 12");
+static_assert(offsetof(BindTexImage2DCHROMIUM, header) == 0,
+              "offset of BindTexImage2DCHROMIUM header should be 0");
+static_assert(offsetof(BindTexImage2DCHROMIUM, texture_id) == 4,
+              "offset of BindTexImage2DCHROMIUM texture_id should be 4");
+static_assert(offsetof(BindTexImage2DCHROMIUM, image_id) == 8,
+              "offset of BindTexImage2DCHROMIUM image_id should be 8");
+
+struct ReleaseTexImage2DCHROMIUM {
+  typedef ReleaseTexImage2DCHROMIUM ValueType;
+  static const CommandId kCmdId = kReleaseTexImage2DCHROMIUM;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLuint _texture_id, GLint _image_id) {
+    SetHeader();
+    texture_id = _texture_id;
+    image_id = _image_id;
+  }
+
+  void* Set(void* cmd, GLuint _texture_id, GLint _image_id) {
+    static_cast<ValueType*>(cmd)->Init(_texture_id, _image_id);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t texture_id;
+  int32_t image_id;
+};
+
+static_assert(sizeof(ReleaseTexImage2DCHROMIUM) == 12,
+              "size of ReleaseTexImage2DCHROMIUM should be 12");
+static_assert(offsetof(ReleaseTexImage2DCHROMIUM, header) == 0,
+              "offset of ReleaseTexImage2DCHROMIUM header should be 0");
+static_assert(offsetof(ReleaseTexImage2DCHROMIUM, texture_id) == 4,
+              "offset of ReleaseTexImage2DCHROMIUM texture_id should be 4");
+static_assert(offsetof(ReleaseTexImage2DCHROMIUM, image_id) == 8,
+              "offset of ReleaseTexImage2DCHROMIUM image_id should be 8");
+
+struct TexStorage2D {
+  typedef TexStorage2D ValueType;
+  static const CommandId kCmdId = kTexStorage2D;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLuint _texture_id,
+            GLsizei _levels,
+            GLsizei _width,
+            GLsizei _height) {
+    SetHeader();
+    texture_id = _texture_id;
+    levels = _levels;
+    width = _width;
+    height = _height;
+  }
+
+  void* Set(void* cmd,
+            GLuint _texture_id,
+            GLsizei _levels,
+            GLsizei _width,
+            GLsizei _height) {
+    static_cast<ValueType*>(cmd)->Init(_texture_id, _levels, _width, _height);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t texture_id;
+  int32_t levels;
+  int32_t width;
+  int32_t height;
+};
+
+static_assert(sizeof(TexStorage2D) == 20, "size of TexStorage2D should be 20");
+static_assert(offsetof(TexStorage2D, header) == 0,
+              "offset of TexStorage2D header should be 0");
+static_assert(offsetof(TexStorage2D, texture_id) == 4,
+              "offset of TexStorage2D texture_id should be 4");
+static_assert(offsetof(TexStorage2D, levels) == 8,
+              "offset of TexStorage2D levels should be 8");
+static_assert(offsetof(TexStorage2D, width) == 12,
+              "offset of TexStorage2D width should be 12");
+static_assert(offsetof(TexStorage2D, height) == 16,
+              "offset of TexStorage2D height should be 16");
+
+struct CopySubTexture {
+  typedef CopySubTexture ValueType;
+  static const CommandId kCmdId = kCopySubTexture;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(2);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLuint _source_id,
+            GLuint _dest_id,
+            GLint _xoffset,
+            GLint _yoffset,
+            GLint _x,
+            GLint _y,
+            GLsizei _width,
+            GLsizei _height) {
+    SetHeader();
+    source_id = _source_id;
+    dest_id = _dest_id;
+    xoffset = _xoffset;
+    yoffset = _yoffset;
+    x = _x;
+    y = _y;
+    width = _width;
+    height = _height;
+  }
+
+  void* Set(void* cmd,
+            GLuint _source_id,
+            GLuint _dest_id,
+            GLint _xoffset,
+            GLint _yoffset,
+            GLint _x,
+            GLint _y,
+            GLsizei _width,
+            GLsizei _height) {
+    static_cast<ValueType*>(cmd)->Init(_source_id, _dest_id, _xoffset, _yoffset,
+                                       _x, _y, _width, _height);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t source_id;
+  uint32_t dest_id;
+  int32_t xoffset;
+  int32_t yoffset;
+  int32_t x;
+  int32_t y;
+  int32_t width;
+  int32_t height;
+};
+
+static_assert(sizeof(CopySubTexture) == 36,
+              "size of CopySubTexture should be 36");
+static_assert(offsetof(CopySubTexture, header) == 0,
+              "offset of CopySubTexture header should be 0");
+static_assert(offsetof(CopySubTexture, source_id) == 4,
+              "offset of CopySubTexture source_id should be 4");
+static_assert(offsetof(CopySubTexture, dest_id) == 8,
+              "offset of CopySubTexture dest_id should be 8");
+static_assert(offsetof(CopySubTexture, xoffset) == 12,
+              "offset of CopySubTexture xoffset should be 12");
+static_assert(offsetof(CopySubTexture, yoffset) == 16,
+              "offset of CopySubTexture yoffset should be 16");
+static_assert(offsetof(CopySubTexture, x) == 20,
+              "offset of CopySubTexture x should be 20");
+static_assert(offsetof(CopySubTexture, y) == 24,
+              "offset of CopySubTexture y should be 24");
+static_assert(offsetof(CopySubTexture, width) == 28,
+              "offset of CopySubTexture width should be 28");
+static_assert(offsetof(CopySubTexture, height) == 32,
+              "offset of CopySubTexture height should be 32");
+
 #endif  // GPU_COMMAND_BUFFER_COMMON_RASTER_CMD_FORMAT_AUTOGEN_H_
diff --git a/gpu/command_buffer/common/raster_cmd_format_test_autogen.h b/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
index da15742..12445a2 100644
--- a/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
@@ -14,18 +14,6 @@
 #ifndef GPU_COMMAND_BUFFER_COMMON_RASTER_CMD_FORMAT_TEST_AUTOGEN_H_
 #define GPU_COMMAND_BUFFER_COMMON_RASTER_CMD_FORMAT_TEST_AUTOGEN_H_
 
-TEST_F(RasterFormatTest, BindTexture) {
-  cmds::BindTexture& cmd = *GetBufferAs<cmds::BindTexture>();
-  void* next_cmd =
-      cmd.Set(&cmd, static_cast<GLenum>(11), static_cast<GLuint>(12));
-  EXPECT_EQ(static_cast<uint32_t>(cmds::BindTexture::kCmdId),
-            cmd.header.command);
-  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
-  EXPECT_EQ(static_cast<GLenum>(11), cmd.target);
-  EXPECT_EQ(static_cast<GLuint>(12), cmd.texture);
-  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
-}
-
 TEST_F(RasterFormatTest, DeleteTexturesImmediate) {
   static GLuint ids[] = {
       12, 23, 34,
@@ -60,23 +48,6 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
-TEST_F(RasterFormatTest, GenTexturesImmediate) {
-  static GLuint ids[] = {
-      12, 23, 34,
-  };
-  cmds::GenTexturesImmediate& cmd = *GetBufferAs<cmds::GenTexturesImmediate>();
-  void* next_cmd = cmd.Set(&cmd, static_cast<GLsizei>(arraysize(ids)), ids);
-  EXPECT_EQ(static_cast<uint32_t>(cmds::GenTexturesImmediate::kCmdId),
-            cmd.header.command);
-  EXPECT_EQ(sizeof(cmd) + RoundSizeToMultipleOfEntries(cmd.n * 4u),
-            cmd.header.size * 4u);
-  EXPECT_EQ(static_cast<GLsizei>(arraysize(ids)), cmd.n);
-  CheckBytesWrittenMatchesExpectedSize(
-      next_cmd,
-      sizeof(cmd) + RoundSizeToMultipleOfEntries(arraysize(ids) * 4u));
-  EXPECT_EQ(0, memcmp(ids, ImmediateDataAddress(&cmd), sizeof(ids)));
-}
-
 TEST_F(RasterFormatTest, GetError) {
   cmds::GetError& cmd = *GetBufferAs<cmds::GetError>();
   void* next_cmd =
@@ -102,19 +73,6 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
-TEST_F(RasterFormatTest, TexParameteri) {
-  cmds::TexParameteri& cmd = *GetBufferAs<cmds::TexParameteri>();
-  void* next_cmd = cmd.Set(&cmd, static_cast<GLenum>(11),
-                           static_cast<GLenum>(12), static_cast<GLint>(13));
-  EXPECT_EQ(static_cast<uint32_t>(cmds::TexParameteri::kCmdId),
-            cmd.header.command);
-  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
-  EXPECT_EQ(static_cast<GLenum>(11), cmd.target);
-  EXPECT_EQ(static_cast<GLenum>(12), cmd.pname);
-  EXPECT_EQ(static_cast<GLint>(13), cmd.param);
-  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
-}
-
 TEST_F(RasterFormatTest, GenQueriesEXTImmediate) {
   static GLuint ids[] = {
       12, 23, 34,
@@ -378,4 +336,144 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
+TEST_F(RasterFormatTest, CreateTexture) {
+  cmds::CreateTexture& cmd = *GetBufferAs<cmds::CreateTexture>();
+  void* next_cmd =
+      cmd.Set(&cmd, static_cast<bool>(11), static_cast<gfx::BufferUsage>(12),
+              static_cast<viz::ResourceFormat>(13), static_cast<uint32_t>(14));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::CreateTexture::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<bool>(11), static_cast<bool>(cmd.use_buffer));
+  EXPECT_EQ(static_cast<gfx::BufferUsage>(12),
+            static_cast<gfx::BufferUsage>(cmd.buffer_usage));
+  EXPECT_EQ(static_cast<viz::ResourceFormat>(13),
+            static_cast<viz::ResourceFormat>(cmd.format));
+  EXPECT_EQ(static_cast<uint32_t>(14), cmd.client_id);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
+TEST_F(RasterFormatTest, SetColorSpaceMetadata) {
+  cmds::SetColorSpaceMetadata& cmd =
+      *GetBufferAs<cmds::SetColorSpaceMetadata>();
+  void* next_cmd =
+      cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<GLuint>(12),
+              static_cast<GLuint>(13), static_cast<GLsizei>(14));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::SetColorSpaceMetadata::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLuint>(11), cmd.texture_id);
+  EXPECT_EQ(static_cast<GLuint>(12), cmd.shm_id);
+  EXPECT_EQ(static_cast<GLuint>(13), cmd.shm_offset);
+  EXPECT_EQ(static_cast<GLsizei>(14), cmd.color_space_size);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
+TEST_F(RasterFormatTest, ProduceTextureDirectImmediate) {
+  const int kSomeBaseValueToTestWith = 51;
+  static GLbyte data[] = {
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 0),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 1),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 2),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 3),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 4),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 5),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 6),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 7),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 8),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 9),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 10),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 11),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 12),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 13),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 14),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 15),
+  };
+  cmds::ProduceTextureDirectImmediate& cmd =
+      *GetBufferAs<cmds::ProduceTextureDirectImmediate>();
+  void* next_cmd = cmd.Set(&cmd, static_cast<GLuint>(11), data);
+  EXPECT_EQ(static_cast<uint32_t>(cmds::ProduceTextureDirectImmediate::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd) + RoundSizeToMultipleOfEntries(sizeof(data)),
+            cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLuint>(11), cmd.texture);
+  CheckBytesWrittenMatchesExpectedSize(
+      next_cmd, sizeof(cmd) + RoundSizeToMultipleOfEntries(sizeof(data)));
+}
+
+TEST_F(RasterFormatTest, TexParameteri) {
+  cmds::TexParameteri& cmd = *GetBufferAs<cmds::TexParameteri>();
+  void* next_cmd = cmd.Set(&cmd, static_cast<GLuint>(11),
+                           static_cast<GLenum>(12), static_cast<GLint>(13));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::TexParameteri::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLuint>(11), cmd.texture_id);
+  EXPECT_EQ(static_cast<GLenum>(12), cmd.pname);
+  EXPECT_EQ(static_cast<GLint>(13), cmd.param);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
+TEST_F(RasterFormatTest, BindTexImage2DCHROMIUM) {
+  cmds::BindTexImage2DCHROMIUM& cmd =
+      *GetBufferAs<cmds::BindTexImage2DCHROMIUM>();
+  void* next_cmd =
+      cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<GLint>(12));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::BindTexImage2DCHROMIUM::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLuint>(11), cmd.texture_id);
+  EXPECT_EQ(static_cast<GLint>(12), cmd.image_id);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
+TEST_F(RasterFormatTest, ReleaseTexImage2DCHROMIUM) {
+  cmds::ReleaseTexImage2DCHROMIUM& cmd =
+      *GetBufferAs<cmds::ReleaseTexImage2DCHROMIUM>();
+  void* next_cmd =
+      cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<GLint>(12));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::ReleaseTexImage2DCHROMIUM::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLuint>(11), cmd.texture_id);
+  EXPECT_EQ(static_cast<GLint>(12), cmd.image_id);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
+TEST_F(RasterFormatTest, TexStorage2D) {
+  cmds::TexStorage2D& cmd = *GetBufferAs<cmds::TexStorage2D>();
+  void* next_cmd =
+      cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<GLsizei>(12),
+              static_cast<GLsizei>(13), static_cast<GLsizei>(14));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::TexStorage2D::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLuint>(11), cmd.texture_id);
+  EXPECT_EQ(static_cast<GLsizei>(12), cmd.levels);
+  EXPECT_EQ(static_cast<GLsizei>(13), cmd.width);
+  EXPECT_EQ(static_cast<GLsizei>(14), cmd.height);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
+TEST_F(RasterFormatTest, CopySubTexture) {
+  cmds::CopySubTexture& cmd = *GetBufferAs<cmds::CopySubTexture>();
+  void* next_cmd =
+      cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<GLuint>(12),
+              static_cast<GLint>(13), static_cast<GLint>(14),
+              static_cast<GLint>(15), static_cast<GLint>(16),
+              static_cast<GLsizei>(17), static_cast<GLsizei>(18));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::CopySubTexture::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLuint>(11), cmd.source_id);
+  EXPECT_EQ(static_cast<GLuint>(12), cmd.dest_id);
+  EXPECT_EQ(static_cast<GLint>(13), cmd.xoffset);
+  EXPECT_EQ(static_cast<GLint>(14), cmd.yoffset);
+  EXPECT_EQ(static_cast<GLint>(15), cmd.x);
+  EXPECT_EQ(static_cast<GLint>(16), cmd.y);
+  EXPECT_EQ(static_cast<GLsizei>(17), cmd.width);
+  EXPECT_EQ(static_cast<GLsizei>(18), cmd.height);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
 #endif  // GPU_COMMAND_BUFFER_COMMON_RASTER_CMD_FORMAT_TEST_AUTOGEN_H_
diff --git a/gpu/command_buffer/common/raster_cmd_ids_autogen.h b/gpu/command_buffer/common/raster_cmd_ids_autogen.h
index e3e1258d6..9dfd8fd 100644
--- a/gpu/command_buffer/common/raster_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_ids_autogen.h
@@ -12,32 +12,37 @@
 #define GPU_COMMAND_BUFFER_COMMON_RASTER_CMD_IDS_AUTOGEN_H_
 
 #define RASTER_COMMAND_LIST(OP)                      \
-  OP(BindTexture)                          /* 256 */ \
-  OP(DeleteTexturesImmediate)              /* 257 */ \
-  OP(Finish)                               /* 258 */ \
-  OP(Flush)                                /* 259 */ \
-  OP(GenTexturesImmediate)                 /* 260 */ \
-  OP(GetError)                             /* 261 */ \
-  OP(GetIntegerv)                          /* 262 */ \
-  OP(TexParameteri)                        /* 263 */ \
-  OP(GenQueriesEXTImmediate)               /* 264 */ \
-  OP(DeleteQueriesEXTImmediate)            /* 265 */ \
-  OP(BeginQueryEXT)                        /* 266 */ \
-  OP(EndQueryEXT)                          /* 267 */ \
-  OP(CompressedCopyTextureCHROMIUM)        /* 268 */ \
-  OP(LoseContextCHROMIUM)                  /* 269 */ \
-  OP(InsertFenceSyncCHROMIUM)              /* 270 */ \
-  OP(WaitSyncTokenCHROMIUM)                /* 271 */ \
-  OP(UnpremultiplyAndDitherCopyCHROMIUM)   /* 272 */ \
-  OP(InitializeDiscardableTextureCHROMIUM) /* 273 */ \
-  OP(UnlockDiscardableTextureCHROMIUM)     /* 274 */ \
-  OP(LockDiscardableTextureCHROMIUM)       /* 275 */ \
-  OP(BeginRasterCHROMIUM)                  /* 276 */ \
-  OP(RasterCHROMIUM)                       /* 277 */ \
-  OP(EndRasterCHROMIUM)                    /* 278 */ \
-  OP(CreateTransferCacheEntryINTERNAL)     /* 279 */ \
-  OP(DeleteTransferCacheEntryINTERNAL)     /* 280 */ \
-  OP(UnlockTransferCacheEntryINTERNAL)     /* 281 */
+  OP(DeleteTexturesImmediate)              /* 256 */ \
+  OP(Finish)                               /* 257 */ \
+  OP(Flush)                                /* 258 */ \
+  OP(GetError)                             /* 259 */ \
+  OP(GetIntegerv)                          /* 260 */ \
+  OP(GenQueriesEXTImmediate)               /* 261 */ \
+  OP(DeleteQueriesEXTImmediate)            /* 262 */ \
+  OP(BeginQueryEXT)                        /* 263 */ \
+  OP(EndQueryEXT)                          /* 264 */ \
+  OP(CompressedCopyTextureCHROMIUM)        /* 265 */ \
+  OP(LoseContextCHROMIUM)                  /* 266 */ \
+  OP(InsertFenceSyncCHROMIUM)              /* 267 */ \
+  OP(WaitSyncTokenCHROMIUM)                /* 268 */ \
+  OP(UnpremultiplyAndDitherCopyCHROMIUM)   /* 269 */ \
+  OP(InitializeDiscardableTextureCHROMIUM) /* 270 */ \
+  OP(UnlockDiscardableTextureCHROMIUM)     /* 271 */ \
+  OP(LockDiscardableTextureCHROMIUM)       /* 272 */ \
+  OP(BeginRasterCHROMIUM)                  /* 273 */ \
+  OP(RasterCHROMIUM)                       /* 274 */ \
+  OP(EndRasterCHROMIUM)                    /* 275 */ \
+  OP(CreateTransferCacheEntryINTERNAL)     /* 276 */ \
+  OP(DeleteTransferCacheEntryINTERNAL)     /* 277 */ \
+  OP(UnlockTransferCacheEntryINTERNAL)     /* 278 */ \
+  OP(CreateTexture)                        /* 279 */ \
+  OP(SetColorSpaceMetadata)                /* 280 */ \
+  OP(ProduceTextureDirectImmediate)        /* 281 */ \
+  OP(TexParameteri)                        /* 282 */ \
+  OP(BindTexImage2DCHROMIUM)               /* 283 */ \
+  OP(ReleaseTexImage2DCHROMIUM)            /* 284 */ \
+  OP(TexStorage2D)                         /* 285 */ \
+  OP(CopySubTexture)                       /* 286 */
 
 enum CommandId {
   kOneBeforeStartPoint =
diff --git a/gpu/command_buffer/raster_cmd_buffer_functions.txt b/gpu/command_buffer/raster_cmd_buffer_functions.txt
index f4450ba..24b9bd1 100644
--- a/gpu/command_buffer/raster_cmd_buffer_functions.txt
+++ b/gpu/command_buffer/raster_cmd_buffer_functions.txt
@@ -4,16 +4,13 @@
 
 // This file is read by build_raster_cmd_buffer.py to generate commands.
 
-GL_APICALL void         GL_APIENTRY glBindTexture (GLenumTextureBindTarget target, GLidBindTexture texture);
 GL_APICALL void         GL_APIENTRY glDeleteTextures (GLsizeiNotNegative n, const GLuint* textures);
 GL_APICALL void         GL_APIENTRY glFinish (void);
 GL_APICALL void         GL_APIENTRY glFlush (void);
-GL_APICALL void         GL_APIENTRY glGenTextures (GLsizeiNotNegative n, GLuint* textures);
 GL_APICALL GLenum       GL_APIENTRY glGetError (void);
 GL_APICALL void         GL_APIENTRY glGetIntegerv (GLenumGLState pname, GLint* params);
 GL_APICALL void         GL_APIENTRY glShallowFlushCHROMIUM (void);
 GL_APICALL void         GL_APIENTRY glOrderingBarrierCHROMIUM (void);
-GL_APICALL void         GL_APIENTRY glTexParameteri (GLenumTextureBindTarget target, GLenumTextureParameter pname, GLint param);
 GL_APICALL void         GL_APIENTRY glGenQueriesEXT (GLsizeiNotNegative n, GLuint* queries);
 GL_APICALL void         GL_APIENTRY glDeleteQueriesEXT (GLsizeiNotNegative n, const GLuint* queries);
 GL_APICALL void         GL_APIENTRY glBeginQueryEXT (GLenumQueryTarget target, GLidQuery id);
@@ -47,3 +44,15 @@
 GL_APICALL void         GL_APIENTRY glCreateTransferCacheEntryINTERNAL (GLuint entry_type, GLuint entry_id, GLuint handle_shm_id, GLuint handle_shm_offset, GLuint data_shm_id, GLuint data_shm_offset, GLuint data_size);
 GL_APICALL void         GL_APIENTRY glDeleteTransferCacheEntryINTERNAL (GLuint entry_type, GLuint entry_id);
 GL_APICALL void         GL_APIENTRY glUnlockTransferCacheEntryINTERNAL (GLuint entry_type, GLuint entry_id);
+
+// TOOD(backer): Remove GL encoding. These are not GL functions.
+GL_APICALL GLuint       GL_APIENTRY glCreateTexture (bool use_buffer, EnumClassgfx::BufferUsage buffer_usage, EnumClassviz::ResourceFormat format);
+GL_APICALL void         GL_APIENTRY glSetColorSpaceMetadata (GLuint texture_id, GLColorSpace color_space);
+GL_APICALL void         GL_APIENTRY glGenMailbox (GLbyte* mailbox);
+GL_APICALL void         GL_APIENTRY glProduceTextureDirect (GLuint texture, const GLbyte* mailbox);
+GL_APICALL GLuint       GL_APIENTRY glCreateAndConsumeTexture (bool use_buffer, EnumClassgfx::BufferUsage buffer_usage, EnumClassviz::ResourceFormat format, const GLbyte* mailbox);
+GL_APICALL void         GL_APIENTRY glTexParameteri (GLuint texture_id, GLenumTextureParameter pname, GLint param);
+GL_APICALL void         GL_APIENTRY glBindTexImage2DCHROMIUM (GLuint texture_id, GLint image_id);
+GL_APICALL void         GL_APIENTRY glReleaseTexImage2DCHROMIUM (GLuint texture_id, GLint image_id);
+GL_APICALL void         GL_APIENTRY glTexStorage2D (GLuint texture_id, GLsizei levels, GLsizei width, GLsizei height);
+GL_APICALL void         GL_APIENTRY glCopySubTexture (GLuint source_id, GLuint dest_id, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index d8f56fd..fb591a6 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -183,6 +183,10 @@
     "program_manager.h",
     "query_manager.cc",
     "query_manager.h",
+    "raster_cmd_validation.cc",
+    "raster_cmd_validation.h",
+    "raster_cmd_validation_autogen.h",
+    "raster_cmd_validation_implementation_autogen.h",
     "raster_decoder.cc",
     "raster_decoder.h",
     "renderbuffer_manager.cc",
@@ -225,6 +229,7 @@
   public_deps = [
     "//gpu/command_buffer/common",
     "//gpu/command_buffer/common:gles2_sources",
+    "//gpu/command_buffer/common:raster_sources",
   ]
   deps = [
     ":disk_cache_proto",
diff --git a/gpu/command_buffer/service/raster_cmd_validation.cc b/gpu/command_buffer/service/raster_cmd_validation.cc
new file mode 100644
index 0000000..e2f3622
--- /dev/null
+++ b/gpu/command_buffer/service/raster_cmd_validation.cc
@@ -0,0 +1,16 @@
+// 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.
+
+// Contains various validation functions for the Raster service.
+
+#include "gpu/command_buffer/service/raster_cmd_validation.h"
+#include "gpu/command_buffer/service/gl_utils.h"
+
+namespace gpu {
+namespace raster {
+
+#include "gpu/command_buffer/service/raster_cmd_validation_implementation_autogen.h"
+
+}  // namespace raster
+}  // namespace gpu
diff --git a/gpu/command_buffer/service/raster_cmd_validation.h b/gpu/command_buffer/service/raster_cmd_validation.h
new file mode 100644
index 0000000..10e6552
--- /dev/null
+++ b/gpu/command_buffer/service/raster_cmd_validation.h
@@ -0,0 +1,70 @@
+// 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.
+
+// Contains various validation functions for the Raster service.
+
+#ifndef GPU_COMMAND_BUFFER_SERVICE_RASTER_CMD_VALIDATION_H_
+#define GPU_COMMAND_BUFFER_SERVICE_RASTER_CMD_VALIDATION_H_
+
+#include <algorithm>
+#include <vector>
+#include "gpu/command_buffer/common/raster_cmd_format.h"
+
+namespace gpu {
+namespace raster {
+
+// ValueValidator returns true if a value is valid.
+template <typename T>
+class ValueValidator {
+ public:
+  ValueValidator() = default;
+
+  ValueValidator(const T* valid_values, int num_values) {
+    AddValues(valid_values, num_values);
+  }
+
+  void AddValue(const T value) {
+    if (!IsValid(value)) {
+      valid_values_.push_back(value);
+    }
+  }
+
+  void AddValues(const T* valid_values, int num_values) {
+    for (int ii = 0; ii < num_values; ++ii) {
+      AddValue(valid_values[ii]);
+    }
+  }
+
+  void RemoveValues(const T* invalid_values, int num_values) {
+    for (int ii = 0; ii < num_values; ++ii) {
+      auto iter = std::find(valid_values_.begin(), valid_values_.end(),
+                            invalid_values[ii]);
+      if (iter != valid_values_.end()) {
+        valid_values_.erase(iter);
+        DCHECK(!IsValid(invalid_values[ii]));
+      }
+    }
+  }
+
+  bool IsValid(const T value) const {
+    return std::find(valid_values_.begin(), valid_values_.end(), value) !=
+           valid_values_.end();
+  }
+
+  const std::vector<T>& GetValues() const { return valid_values_; }
+
+ private:
+  std::vector<T> valid_values_;
+};
+
+struct Validators {
+  Validators();
+
+#include "gpu/command_buffer/service/raster_cmd_validation_autogen.h"
+};
+
+}  // namespace raster
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_SERVICE_RASTER_CMD_VALIDATION_H_
diff --git a/gpu/command_buffer/service/raster_cmd_validation_autogen.h b/gpu/command_buffer/service/raster_cmd_validation_autogen.h
new file mode 100644
index 0000000..1abdb1eb
--- /dev/null
+++ b/gpu/command_buffer/service/raster_cmd_validation_autogen.h
@@ -0,0 +1,44 @@
+// 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.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_raster_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+//    clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+#ifndef GPU_COMMAND_BUFFER_SERVICE_RASTER_CMD_VALIDATION_AUTOGEN_H_
+#define GPU_COMMAND_BUFFER_SERVICE_RASTER_CMD_VALIDATION_AUTOGEN_H_
+
+ValueValidator<GLenum> g_l_state;
+class QueryObjectParameterValidator {
+ public:
+  bool IsValid(const GLenum value) const;
+};
+QueryObjectParameterValidator query_object_parameter;
+
+class QueryTargetValidator {
+ public:
+  bool IsValid(const GLenum value) const;
+};
+QueryTargetValidator query_target;
+
+class ResetStatusValidator {
+ public:
+  bool IsValid(const GLenum value) const;
+};
+ResetStatusValidator reset_status;
+
+ValueValidator<GLenum> texture_mag_filter_mode;
+ValueValidator<GLenum> texture_min_filter_mode;
+ValueValidator<GLenum> texture_parameter;
+ValueValidator<GLenum> texture_wrap_mode;
+ValueValidator<gfx::BufferUsage> gfx_buffer_usage;
+class VizResourceFormatValidator {
+ public:
+  bool IsValid(const viz::ResourceFormat value) const;
+};
+VizResourceFormatValidator viz_resource_format;
+
+#endif  // GPU_COMMAND_BUFFER_SERVICE_RASTER_CMD_VALIDATION_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/raster_cmd_validation_implementation_autogen.h b/gpu/command_buffer/service/raster_cmd_validation_implementation_autogen.h
new file mode 100644
index 0000000..0f4d0b9c
--- /dev/null
+++ b/gpu/command_buffer/service/raster_cmd_validation_implementation_autogen.h
@@ -0,0 +1,102 @@
+// 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.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_raster_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+//    clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+#ifndef GPU_COMMAND_BUFFER_SERVICE_RASTER_CMD_VALIDATION_IMPLEMENTATION_AUTOGEN_H_
+#define GPU_COMMAND_BUFFER_SERVICE_RASTER_CMD_VALIDATION_IMPLEMENTATION_AUTOGEN_H_
+
+static const GLenum valid_g_l_state_table[] = {
+    GL_ACTIVE_TEXTURE,
+};
+
+bool Validators::QueryObjectParameterValidator::IsValid(
+    const GLenum value) const {
+  switch (value) {
+    case GL_QUERY_RESULT_EXT:
+    case GL_QUERY_RESULT_AVAILABLE_EXT:
+      return true;
+  }
+  return false;
+};
+
+bool Validators::QueryTargetValidator::IsValid(const GLenum value) const {
+  switch (value) {
+    case GL_COMMANDS_ISSUED_CHROMIUM:
+    case GL_COMMANDS_COMPLETED_CHROMIUM:
+      return true;
+  }
+  return false;
+};
+
+bool Validators::ResetStatusValidator::IsValid(const GLenum value) const {
+  switch (value) {
+    case GL_GUILTY_CONTEXT_RESET_ARB:
+    case GL_INNOCENT_CONTEXT_RESET_ARB:
+    case GL_UNKNOWN_CONTEXT_RESET_ARB:
+      return true;
+  }
+  return false;
+};
+
+static const GLenum valid_texture_mag_filter_mode_table[] = {
+    GL_NEAREST,
+};
+
+static const GLenum valid_texture_min_filter_mode_table[] = {
+    GL_NEAREST,
+};
+
+static const GLenum valid_texture_parameter_table[] = {
+    GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_WRAP_S,
+    GL_TEXTURE_WRAP_T,
+};
+
+static const GLenum valid_texture_wrap_mode_table[] = {
+    GL_CLAMP_TO_EDGE,
+};
+
+static const gfx::BufferUsage valid_gfx_buffer_usage_table[] = {
+    gfx::BufferUsage::GPU_READ, gfx::BufferUsage::SCANOUT,
+    gfx::BufferUsage::GPU_READ_CPU_READ_WRITE,
+    gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT,
+};
+
+bool Validators::VizResourceFormatValidator::IsValid(
+    const viz::ResourceFormat value) const {
+  switch (value) {
+    case viz::ResourceFormat::RGBA_8888:
+    case viz::ResourceFormat::RGBA_4444:
+    case viz::ResourceFormat::BGRA_8888:
+    case viz::ResourceFormat::ALPHA_8:
+    case viz::ResourceFormat::LUMINANCE_8:
+    case viz::ResourceFormat::RGB_565:
+    case viz::ResourceFormat::ETC1:
+    case viz::ResourceFormat::RED_8:
+    case viz::ResourceFormat::LUMINANCE_F16:
+    case viz::ResourceFormat::RGBA_F16:
+    case viz::ResourceFormat::R16_EXT:
+      return true;
+  }
+  return false;
+};
+
+Validators::Validators()
+    : g_l_state(valid_g_l_state_table, arraysize(valid_g_l_state_table)),
+      texture_mag_filter_mode(valid_texture_mag_filter_mode_table,
+                              arraysize(valid_texture_mag_filter_mode_table)),
+      texture_min_filter_mode(valid_texture_min_filter_mode_table,
+                              arraysize(valid_texture_min_filter_mode_table)),
+      texture_parameter(valid_texture_parameter_table,
+                        arraysize(valid_texture_parameter_table)),
+      texture_wrap_mode(valid_texture_wrap_mode_table,
+                        arraysize(valid_texture_wrap_mode_table)),
+      gfx_buffer_usage(valid_gfx_buffer_usage_table,
+                       arraysize(valid_gfx_buffer_usage_table)) {}
+
+#endif  // GPU_COMMAND_BUFFER_SERVICE_RASTER_CMD_VALIDATION_IMPLEMENTATION_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index f69fc63..979849d 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -25,8 +25,8 @@
 #include "gpu/command_buffer/service/decoder_client.h"
 #include "gpu/command_buffer/service/error_state.h"
 #include "gpu/command_buffer/service/feature_info.h"
-#include "gpu/command_buffer/service/gles2_cmd_validation.h"
 #include "gpu/command_buffer/service/logger.h"
+#include "gpu/command_buffer/service/raster_cmd_validation.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_surface.h"
 #include "ui/gl/gl_version_info.h"
@@ -36,7 +36,7 @@
   ERRORSTATE_SET_GL_ERROR(state_.GetErrorState(), error, function_name, msg)
 #define LOCAL_SET_GL_ERROR_INVALID_ENUM(function_name, value, label)          \
   ERRORSTATE_SET_GL_ERROR_INVALID_ENUM(state_.GetErrorState(), function_name, \
-                                       value, label)
+                                       static_cast<uint32_t>(value), label)
 #define LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(function_name)         \
   ERRORSTATE_COPY_REAL_GL_ERRORS_TO_WRAPPER(state_.GetErrorState(), \
                                             function_name)
@@ -182,10 +182,26 @@
   // false if pname is unknown.
   bool GetNumValuesReturnedForGLGet(GLenum pname, GLsizei* num_values);
 
-  void DoActiveTexture(GLenum texture_unit) { NOTIMPLEMENTED(); }
-  void DoBindTexture(GLenum target, GLuint texture);
+  GLuint DoCreateTexture(bool use_buffer,
+                         gfx::BufferUsage buffer_usage,
+                         viz::ResourceFormat resource_format) {
+    // Stubbed out enough for unittests. Need to take params into account.
+    NOTIMPLEMENTED();
+    GLuint service_id;
+    api()->glGenTexturesFn(1, &service_id);
+    return service_id;
+  }
+  void CreateTexture(GLuint client_id,
+                     GLuint service_id,
+                     bool use_buffer,
+                     gfx::BufferUsage buffer_usage,
+                     viz::ResourceFormat resource_format) {
+    // Stubbed out enough for unittests. Need to take params into account.
+    NOTIMPLEMENTED();
+    CreateTexture(client_id, service_id);
+  }
+
   void DeleteTexturesHelper(GLsizei n, const volatile GLuint* client_ids);
-  bool GenTexturesHelper(GLsizei n, const GLuint* client_ids);
   bool GenQueriesEXTHelper(GLsizei n, const GLuint* client_ids) {
     NOTIMPLEMENTED();
     return true;
@@ -196,35 +212,30 @@
   void DoFinish();
   void DoFlush();
   void DoGetIntegerv(GLenum pname, GLint* params, GLsizei params_size);
-  void DoTexParameteri(GLenum target, GLenum pname, GLint param);
-  void DoTexStorage2DEXT(GLenum target,
-                         GLsizei levels,
-                         GLenum internal_format,
-                         GLsizei width,
-                         GLsizei height) {
+  void DoTexParameteri(GLuint texture_id, GLenum pname, GLint param);
+  void DoBindTexImage2DCHROMIUM(GLuint texture_id, GLint image_id) {
     NOTIMPLEMENTED();
   }
-  void DoTexStorage2DImageCHROMIUM(GLenum target,
-                                   GLenum internal_format,
-                                   GLenum buffer_usage,
-                                   GLsizei width,
-                                   GLsizei height) {
+  void DoProduceTextureDirect(GLuint texture, const volatile GLbyte* mailbox) {
     NOTIMPLEMENTED();
   }
-  void DoCopySubTextureCHROMIUM(GLuint source_id,
-                                GLint source_level,
-                                GLenum dest_target,
-                                GLuint dest_id,
-                                GLint dest_level,
-                                GLint xoffset,
-                                GLint yoffset,
-                                GLint x,
-                                GLint y,
-                                GLsizei width,
-                                GLsizei height,
-                                GLboolean unpack_flip_y,
-                                GLboolean unpack_premultiply_alpha,
-                                GLboolean unpack_unmultiply_alpha) {
+  void DoReleaseTexImage2DCHROMIUM(GLuint texture_id, GLint image_id) {
+    NOTIMPLEMENTED();
+  }
+  void DoTexStorage2D(GLuint texture_id,
+                      GLint levels,
+                      GLsizei width,
+                      GLsizei height) {
+    NOTIMPLEMENTED();
+  }
+  void DoCopySubTexture(GLuint source_id,
+                        GLuint dest_id,
+                        GLint xoffset,
+                        GLint yoffset,
+                        GLint x,
+                        GLint y,
+                        GLsizei width,
+                        GLsizei height) {
     NOTIMPLEMENTED();
   }
   void DoCompressedCopyTextureCHROMIUM(GLuint source_id, GLuint dest_id) {
@@ -234,13 +245,6 @@
                                       const volatile GLbyte* key) {
     NOTIMPLEMENTED();
   }
-  void DoBindTexImage2DCHROMIUM(GLenum target, GLint image_id) {
-    NOTIMPLEMENTED();
-  }
-  void DoReleaseTexImage2DCHROMIUM(GLenum target, GLint image_id) {
-    NOTIMPLEMENTED();
-  }
-  void DoTraceEndCHROMIUM();
   void DoLoseContextCHROMIUM(GLenum current, GLenum other) { NOTIMPLEMENTED(); }
   void DoBeginRasterCHROMIUM(GLuint texture_id,
                              GLuint sk_color,
@@ -345,7 +349,7 @@
 
   // The ContextGroup for this decoder uses to track resources.
   scoped_refptr<gles2::ContextGroup> group_;
-  const gles2::Validators* validators_;
+  std::unique_ptr<Validators> validators_;
   scoped_refptr<gles2::FeatureInfo> feature_info_;
 
   // All the state for this context.
@@ -419,7 +423,7 @@
       client_(client),
       logger_(&debug_marker_manager_, client),
       group_(group),
-      validators_(group_->feature_info()->validators()),
+      validators_(new Validators),
       feature_info_(group_->feature_info()),
       state_(group_->feature_info(), this, &logger_),
       service_logging_(
@@ -864,6 +868,13 @@
                                               : error::kNoError;
 }
 
+error::Error RasterDecoderImpl::HandleSetColorSpaceMetadata(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  NOTIMPLEMENTED();
+  return error::kNoError;
+}
+
 error::Error RasterDecoderImpl::HandleBeginQueryEXT(
     uint32_t immediate_data_size,
     const volatile void* cmd_data) {
@@ -942,89 +953,46 @@
   }
 }
 
-bool RasterDecoderImpl::GenTexturesHelper(GLsizei n, const GLuint* client_ids) {
-  for (GLsizei ii = 0; ii < n; ++ii) {
-    if (GetTexture(client_ids[ii])) {
-      return false;
-    }
-  }
-  std::unique_ptr<GLuint[]> service_ids(new GLuint[n]);
-  api()->glGenTexturesFn(n, service_ids.get());
-  for (GLsizei ii = 0; ii < n; ++ii) {
-    CreateTexture(client_ids[ii], service_ids[ii]);
-  }
-  return true;
-}
-
-void RasterDecoderImpl::DoTexParameteri(GLenum target,
+void RasterDecoderImpl::DoTexParameteri(GLuint texture_id,
                                         GLenum pname,
                                         GLint param) {
-  TextureRef* texture =
-      texture_manager()->GetTextureInfoForTarget(&state_, target);
+  TextureRef* texture = texture_manager()->GetTexture(texture_id);
   if (!texture) {
     LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexParameteri", "unknown texture");
     return;
   }
 
+  // TextureManager uses validators from the share group, which may include
+  // GLES2. Perform stronger validation here.
+  bool valid_param = true;
+  bool valid_pname = true;
+  switch (pname) {
+    case GL_TEXTURE_MIN_FILTER:
+      valid_param = validators_->texture_min_filter_mode.IsValid(param);
+      break;
+    case GL_TEXTURE_MAG_FILTER:
+      valid_param = validators_->texture_mag_filter_mode.IsValid(param);
+      break;
+    case GL_TEXTURE_WRAP_S:
+    case GL_TEXTURE_WRAP_T:
+      valid_param = validators_->texture_wrap_mode.IsValid(param);
+      break;
+    default:
+      valid_pname = false;
+  }
+  if (!valid_pname) {
+    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexParameteri", pname, "pname");
+    return;
+  }
+  if (!valid_param) {
+    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexParameteri", param, "pname");
+    return;
+  }
+
   texture_manager()->SetParameteri("glTexParameteri", GetErrorState(), texture,
                                    pname, param);
 }
 
-void RasterDecoderImpl::DoBindTexture(GLenum target, GLuint client_id) {
-  TextureRef* texture_ref = NULL;
-  GLuint service_id = 0;
-  if (client_id != 0) {
-    texture_ref = GetTexture(client_id);
-    if (!texture_ref) {
-      if (!group_->bind_generates_resource()) {
-        LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBindTexture",
-                           "id not generated by glGenTextures");
-        return;
-      }
-
-      // It's a new id so make a texture texture for it.
-      api()->glGenTexturesFn(1, &service_id);
-      DCHECK_NE(0u, service_id);
-      CreateTexture(client_id, service_id);
-      texture_ref = GetTexture(client_id);
-    }
-  } else {
-    texture_ref = texture_manager()->GetDefaultTextureInfo(target);
-  }
-
-  // Check the texture exists
-  if (texture_ref) {
-    Texture* texture = texture_ref->texture();
-    // Check that we are not trying to bind it to a different target.
-    if (texture->target() != 0 && texture->target() != target) {
-      LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBindTexture",
-                         "texture bound to more than 1 target.");
-      return;
-    }
-    LogClientServiceForInfo(texture, client_id, "glBindTexture");
-    api()->glBindTextureFn(target, texture->service_id());
-    if (texture->target() == 0) {
-      texture_manager()->SetTarget(texture_ref, target);
-      if (!gl_version_info().BehavesLikeGLES() &&
-          gl_version_info().IsAtLeastGL(3, 2)) {
-        // In Desktop GL core profile and GL ES, depth textures are always
-        // sampled to the RED channel, whereas on Desktop GL compatibility
-        // proifle, they are sampled to RED, LUMINANCE, INTENSITY, or ALPHA
-        // channel, depending on the DEPTH_TEXTURE_MODE value.
-        // In theory we only need to apply this for depth textures, but it is
-        // simpler to apply to all textures.
-        api()->glTexParameteriFn(target, GL_DEPTH_TEXTURE_MODE, GL_RED);
-      }
-    }
-  } else {
-    api()->glBindTextureFn(target, 0);
-  }
-
-  TextureUnit& unit = state_.texture_units[state_.active_texture_unit];
-  unit.bind_target = target;
-  unit.SetInfoForTarget(target, texture_ref);
-}
-
 // Include the auto-generated part of this file. We split this because it means
 // we can easily edit the non-auto generated parts right here in this file
 // instead of having to edit some template or the code generator.
diff --git a/gpu/command_buffer/service/raster_decoder_autogen.h b/gpu/command_buffer/service/raster_decoder_autogen.h
index 425e749..2dbfe30 100644
--- a/gpu/command_buffer/service/raster_decoder_autogen.h
+++ b/gpu/command_buffer/service/raster_decoder_autogen.h
@@ -12,21 +12,6 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_RASTER_DECODER_AUTOGEN_H_
 #define GPU_COMMAND_BUFFER_SERVICE_RASTER_DECODER_AUTOGEN_H_
 
-error::Error RasterDecoderImpl::HandleBindTexture(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::BindTexture& c =
-      *static_cast<const volatile raster::cmds::BindTexture*>(cmd_data);
-  GLenum target = static_cast<GLenum>(c.target);
-  GLuint texture = c.texture;
-  if (!validators_->texture_bind_target.IsValid(target)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glBindTexture", target, "target");
-    return error::kNoError;
-  }
-  DoBindTexture(target, texture);
-  return error::kNoError;
-}
-
 error::Error RasterDecoderImpl::HandleDeleteTexturesImmediate(
     uint32_t immediate_data_size,
     const volatile void* cmd_data) {
@@ -59,32 +44,6 @@
   return error::kNoError;
 }
 
-error::Error RasterDecoderImpl::HandleGenTexturesImmediate(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::GenTexturesImmediate& c =
-      *static_cast<const volatile raster::cmds::GenTexturesImmediate*>(
-          cmd_data);
-  GLsizei n = static_cast<GLsizei>(c.n);
-  uint32_t data_size;
-  if (!SafeMultiplyUint32(n, sizeof(GLuint), &data_size)) {
-    return error::kOutOfBounds;
-  }
-  volatile GLuint* textures =
-      GetImmediateDataAs<volatile GLuint*>(c, data_size, immediate_data_size);
-  if (textures == NULL) {
-    return error::kOutOfBounds;
-  }
-  auto textures_copy = std::make_unique<GLuint[]>(n);
-  GLuint* textures_safe = textures_copy.get();
-  std::copy(textures, textures + n, textures_safe);
-  if (!CheckUniqueAndNonNullIds(n, textures_safe) ||
-      !GenTexturesHelper(n, textures_safe)) {
-    return error::kInvalidArguments;
-  }
-  return error::kNoError;
-}
-
 error::Error RasterDecoderImpl::HandleGetError(uint32_t immediate_data_size,
                                                const volatile void* cmd_data) {
   const volatile raster::cmds::GetError& c =
@@ -134,26 +93,6 @@
   return error::kNoError;
 }
 
-error::Error RasterDecoderImpl::HandleTexParameteri(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::TexParameteri& c =
-      *static_cast<const volatile raster::cmds::TexParameteri*>(cmd_data);
-  GLenum target = static_cast<GLenum>(c.target);
-  GLenum pname = static_cast<GLenum>(c.pname);
-  GLint param = static_cast<GLint>(c.param);
-  if (!validators_->texture_bind_target.IsValid(target)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexParameteri", target, "target");
-    return error::kNoError;
-  }
-  if (!validators_->texture_parameter.IsValid(pname)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexParameteri", pname, "pname");
-    return error::kNoError;
-  }
-  DoTexParameteri(target, pname, param);
-  return error::kNoError;
-}
-
 error::Error RasterDecoderImpl::HandleGenQueriesEXTImmediate(
     uint32_t immediate_data_size,
     const volatile void* cmd_data) {
@@ -239,10 +178,6 @@
       *static_cast<
           const volatile raster::cmds::UnpremultiplyAndDitherCopyCHROMIUM*>(
           cmd_data);
-  if (!features().unpremultiply_and_dither_copy) {
-    return error::kUnknownCommand;
-  }
-
   GLuint source_id = static_cast<GLuint>(c.source_id);
   GLuint dest_id = static_cast<GLuint>(c.dest_id);
   GLint x = static_cast<GLint>(c.x);
@@ -268,10 +203,6 @@
     const volatile void* cmd_data) {
   const volatile raster::cmds::BeginRasterCHROMIUM& c =
       *static_cast<const volatile raster::cmds::BeginRasterCHROMIUM*>(cmd_data);
-  if (!features().chromium_raster_transport) {
-    return error::kUnknownCommand;
-  }
-
   GLuint texture_id = static_cast<GLuint>(c.texture_id);
   GLuint sk_color = static_cast<GLuint>(c.sk_color);
   GLuint msaa_sample_count = static_cast<GLuint>(c.msaa_sample_count);
@@ -289,10 +220,6 @@
     const volatile void* cmd_data) {
   const volatile raster::cmds::RasterCHROMIUM& c =
       *static_cast<const volatile raster::cmds::RasterCHROMIUM*>(cmd_data);
-  if (!features().chromium_raster_transport) {
-    return error::kUnknownCommand;
-  }
-
   GLsizeiptr size = static_cast<GLsizeiptr>(c.size);
   uint32_t data_size = size;
   const void* list = GetSharedMemoryAs<const void*>(
@@ -311,10 +238,6 @@
 error::Error RasterDecoderImpl::HandleEndRasterCHROMIUM(
     uint32_t immediate_data_size,
     const volatile void* cmd_data) {
-  if (!features().chromium_raster_transport) {
-    return error::kUnknownCommand;
-  }
-
   DoEndRasterCHROMIUM();
   return error::kNoError;
 }
@@ -365,4 +288,145 @@
   return error::kNoError;
 }
 
+error::Error RasterDecoderImpl::HandleCreateTexture(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  const volatile raster::cmds::CreateTexture& c =
+      *static_cast<const volatile raster::cmds::CreateTexture*>(cmd_data);
+  bool use_buffer = static_cast<bool>(c.use_buffer);
+  gfx::BufferUsage buffer_usage = static_cast<gfx::BufferUsage>(c.buffer_usage);
+  viz::ResourceFormat format = static_cast<viz::ResourceFormat>(c.format);
+  if (!validators_->gfx_buffer_usage.IsValid(buffer_usage)) {
+    LOCAL_SET_GL_ERROR_INVALID_ENUM("glCreateTexture", buffer_usage,
+                                    "buffer_usage");
+    return error::kNoError;
+  }
+  if (!validators_->viz_resource_format.IsValid(format)) {
+    LOCAL_SET_GL_ERROR_INVALID_ENUM("glCreateTexture", format, "format");
+    return error::kNoError;
+  }
+  uint32_t client_id = c.client_id;
+  if (GetTexture(client_id)) {
+    return error::kInvalidArguments;
+  }
+  GLuint service_id = DoCreateTexture(use_buffer, buffer_usage, format);
+  if (service_id) {
+    CreateTexture(client_id, service_id, use_buffer, buffer_usage, format);
+  }
+  return error::kNoError;
+}
+
+error::Error RasterDecoderImpl::HandleProduceTextureDirectImmediate(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  const volatile raster::cmds::ProduceTextureDirectImmediate& c =
+      *static_cast<const volatile raster::cmds::ProduceTextureDirectImmediate*>(
+          cmd_data);
+  GLuint texture = static_cast<GLuint>(c.texture);
+  uint32_t data_size;
+  if (!GLES2Util::ComputeDataSize<GLbyte, 16>(1, &data_size)) {
+    return error::kOutOfBounds;
+  }
+  if (data_size > immediate_data_size) {
+    return error::kOutOfBounds;
+  }
+  volatile const GLbyte* mailbox = GetImmediateDataAs<volatile const GLbyte*>(
+      c, data_size, immediate_data_size);
+  if (mailbox == NULL) {
+    return error::kOutOfBounds;
+  }
+  DoProduceTextureDirect(texture, mailbox);
+  return error::kNoError;
+}
+
+error::Error RasterDecoderImpl::HandleTexParameteri(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  const volatile raster::cmds::TexParameteri& c =
+      *static_cast<const volatile raster::cmds::TexParameteri*>(cmd_data);
+  GLuint texture_id = static_cast<GLuint>(c.texture_id);
+  GLenum pname = static_cast<GLenum>(c.pname);
+  GLint param = static_cast<GLint>(c.param);
+  if (!validators_->texture_parameter.IsValid(pname)) {
+    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexParameteri", pname, "pname");
+    return error::kNoError;
+  }
+  DoTexParameteri(texture_id, pname, param);
+  return error::kNoError;
+}
+
+error::Error RasterDecoderImpl::HandleBindTexImage2DCHROMIUM(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  const volatile raster::cmds::BindTexImage2DCHROMIUM& c =
+      *static_cast<const volatile raster::cmds::BindTexImage2DCHROMIUM*>(
+          cmd_data);
+  GLuint texture_id = static_cast<GLuint>(c.texture_id);
+  GLint image_id = static_cast<GLint>(c.image_id);
+  DoBindTexImage2DCHROMIUM(texture_id, image_id);
+  return error::kNoError;
+}
+
+error::Error RasterDecoderImpl::HandleReleaseTexImage2DCHROMIUM(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  const volatile raster::cmds::ReleaseTexImage2DCHROMIUM& c =
+      *static_cast<const volatile raster::cmds::ReleaseTexImage2DCHROMIUM*>(
+          cmd_data);
+  GLuint texture_id = static_cast<GLuint>(c.texture_id);
+  GLint image_id = static_cast<GLint>(c.image_id);
+  DoReleaseTexImage2DCHROMIUM(texture_id, image_id);
+  return error::kNoError;
+}
+
+error::Error RasterDecoderImpl::HandleTexStorage2D(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  const volatile raster::cmds::TexStorage2D& c =
+      *static_cast<const volatile raster::cmds::TexStorage2D*>(cmd_data);
+  GLuint texture_id = static_cast<GLuint>(c.texture_id);
+  GLsizei levels = static_cast<GLsizei>(c.levels);
+  GLsizei width = static_cast<GLsizei>(c.width);
+  GLsizei height = static_cast<GLsizei>(c.height);
+  if (levels < 0) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage2D", "levels < 0");
+    return error::kNoError;
+  }
+  if (width < 0) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage2D", "width < 0");
+    return error::kNoError;
+  }
+  if (height < 0) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage2D", "height < 0");
+    return error::kNoError;
+  }
+  DoTexStorage2D(texture_id, levels, width, height);
+  return error::kNoError;
+}
+
+error::Error RasterDecoderImpl::HandleCopySubTexture(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  const volatile raster::cmds::CopySubTexture& c =
+      *static_cast<const volatile raster::cmds::CopySubTexture*>(cmd_data);
+  GLuint source_id = static_cast<GLuint>(c.source_id);
+  GLuint dest_id = static_cast<GLuint>(c.dest_id);
+  GLint xoffset = static_cast<GLint>(c.xoffset);
+  GLint yoffset = static_cast<GLint>(c.yoffset);
+  GLint x = static_cast<GLint>(c.x);
+  GLint y = static_cast<GLint>(c.y);
+  GLsizei width = static_cast<GLsizei>(c.width);
+  GLsizei height = static_cast<GLsizei>(c.height);
+  if (width < 0) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture", "width < 0");
+    return error::kNoError;
+  }
+  if (height < 0) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture", "height < 0");
+    return error::kNoError;
+  }
+  DoCopySubTexture(source_id, dest_id, xoffset, yoffset, x, y, width, height);
+  return error::kNoError;
+}
+
 #endif  // GPU_COMMAND_BUFFER_SERVICE_RASTER_DECODER_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_1.cc b/gpu/command_buffer/service/raster_decoder_unittest_1.cc
index 6c68be0..26ab9cb 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_1.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest_1.cc
@@ -35,13 +35,71 @@
 
 INSTANTIATE_TEST_CASE_P(Service, RasterDecoderTest1, ::testing::Bool());
 
-template <>
-void RasterDecoderTestBase::SpecializedSetup<cmds::TexParameteri, 0>(
-    bool /* valid */) {
-  DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
-};
-
 #include "gpu/command_buffer/service/raster_decoder_unittest_1_autogen.h"
 
+TEST_P(RasterDecoderTest1, TexParameteriValidArgs) {
+  EXPECT_CALL(*gl_, TexParameteri(_, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
+  cmds::TexParameteri cmd;
+  cmd.Init(client_texture_id_, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+  EXPECT_CALL(*gl_, TexParameteri(_, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
+  cmd.Init(client_texture_id_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+  EXPECT_CALL(*gl_, TexParameteri(_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+  cmd.Init(client_texture_id_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+  EXPECT_CALL(*gl_, TexParameteri(_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+  cmd.Init(client_texture_id_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(RasterDecoderTest1, TexParameteriInvalidArgsMipMap) {
+  EXPECT_CALL(*gl_, TexParameteri(_, _, _)).Times(0);
+  cmds::TexParameteri cmd;
+  cmd.Init(client_texture_id_, GL_GENERATE_MIPMAP, GL_NEAREST);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
+}
+
+TEST_P(RasterDecoderTest1, TexParameteriInvalidArgsMagFilter) {
+  EXPECT_CALL(*gl_, TexParameteri(_, _, _)).Times(0);
+  cmds::TexParameteri cmd;
+  cmd.Init(client_texture_id_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
+}
+
+TEST_P(RasterDecoderTest1, TexParameteriInvalidArgsMinFilter) {
+  EXPECT_CALL(*gl_, TexParameteri(_, _, _)).Times(0);
+  cmds::TexParameteri cmd;
+  cmd.Init(client_texture_id_, GL_TEXTURE_MIN_FILTER,
+           GL_NEAREST_MIPMAP_NEAREST);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
+}
+
+TEST_P(RasterDecoderTest1, TexParameteriInvalidArgsWrapS) {
+  EXPECT_CALL(*gl_, TexParameteri(_, _, _)).Times(0);
+  cmds::TexParameteri cmd;
+  cmd.Init(client_texture_id_, GL_TEXTURE_WRAP_S, GL_REPEAT);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
+}
+
+TEST_P(RasterDecoderTest1, TexParameteriInvalidArgsWrapT) {
+  EXPECT_CALL(*gl_, TexParameteri(_, _, _)).Times(0);
+  cmds::TexParameteri cmd;
+  cmd.Init(client_texture_id_, GL_TEXTURE_WRAP_T, GL_REPEAT);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
+}
+
 }  // namespace raster
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_1_autogen.h b/gpu/command_buffer/service/raster_decoder_unittest_1_autogen.h
index 8349f94..67c9fa0 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_1_autogen.h
+++ b/gpu/command_buffer/service/raster_decoder_unittest_1_autogen.h
@@ -51,45 +51,6 @@
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
 }
 
-TEST_P(RasterDecoderTest1, GenTexturesImmediateValidArgs) {
-  EXPECT_CALL(*gl_, GenTextures(1, _))
-      .WillOnce(SetArgPointee<1>(kNewServiceId));
-  cmds::GenTexturesImmediate* cmd =
-      GetImmediateAs<cmds::GenTexturesImmediate>();
-  GLuint temp = kNewClientId;
-  SpecializedSetup<cmds::GenTexturesImmediate, 0>(true);
-  cmd->Init(1, &temp);
-  EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(*cmd, sizeof(temp)));
-  EXPECT_EQ(GL_NO_ERROR, GetGLError());
-  EXPECT_TRUE(GetTexture(kNewClientId) != NULL);
-}
-
-TEST_P(RasterDecoderTest1, GenTexturesImmediateDuplicateOrNullIds) {
-  EXPECT_CALL(*gl_, GenTextures(_, _)).Times(0);
-  cmds::GenTexturesImmediate* cmd =
-      GetImmediateAs<cmds::GenTexturesImmediate>();
-  GLuint temp[3] = {kNewClientId, kNewClientId + 1, kNewClientId};
-  SpecializedSetup<cmds::GenTexturesImmediate, 1>(true);
-  cmd->Init(3, temp);
-  EXPECT_EQ(error::kInvalidArguments, ExecuteImmediateCmd(*cmd, sizeof(temp)));
-  EXPECT_TRUE(GetTexture(kNewClientId) == NULL);
-  EXPECT_TRUE(GetTexture(kNewClientId + 1) == NULL);
-  GLuint null_id[2] = {kNewClientId, 0};
-  cmd->Init(2, null_id);
-  EXPECT_EQ(error::kInvalidArguments, ExecuteImmediateCmd(*cmd, sizeof(temp)));
-  EXPECT_TRUE(GetTexture(kNewClientId) == NULL);
-}
-
-TEST_P(RasterDecoderTest1, GenTexturesImmediateInvalidArgs) {
-  EXPECT_CALL(*gl_, GenTextures(_, _)).Times(0);
-  cmds::GenTexturesImmediate* cmd =
-      GetImmediateAs<cmds::GenTexturesImmediate>();
-  SpecializedSetup<cmds::GenTexturesImmediate, 0>(false);
-  cmd->Init(1, &client_texture_id_);
-  EXPECT_EQ(error::kInvalidArguments,
-            ExecuteImmediateCmd(*cmd, sizeof(&client_texture_id_)));
-}
-
 TEST_P(RasterDecoderTest1, GetErrorValidArgs) {
   EXPECT_CALL(*gl_, GetError());
   SpecializedSetup<cmds::GetError, 0>(true);
@@ -160,41 +121,4 @@
   EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
   EXPECT_EQ(0u, result->size);
 }
-
-TEST_P(RasterDecoderTest1, TexParameteriValidArgs) {
-  EXPECT_CALL(*gl_,
-              TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
-  SpecializedSetup<cmds::TexParameteri, 0>(true);
-  cmds::TexParameteri cmd;
-  cmd.Init(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-  EXPECT_EQ(GL_NO_ERROR, GetGLError());
-}
-
-TEST_P(RasterDecoderTest1, TexParameteriInvalidArgs0_0) {
-  EXPECT_CALL(*gl_, TexParameteri(_, _, _)).Times(0);
-  SpecializedSetup<cmds::TexParameteri, 0>(false);
-  cmds::TexParameteri cmd;
-  cmd.Init(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
-}
-
-TEST_P(RasterDecoderTest1, TexParameteriInvalidArgs0_1) {
-  EXPECT_CALL(*gl_, TexParameteri(_, _, _)).Times(0);
-  SpecializedSetup<cmds::TexParameteri, 0>(false);
-  cmds::TexParameteri cmd;
-  cmd.Init(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
-}
-
-TEST_P(RasterDecoderTest1, TexParameteriInvalidArgs1_0) {
-  EXPECT_CALL(*gl_, TexParameteri(_, _, _)).Times(0);
-  SpecializedSetup<cmds::TexParameteri, 0>(false);
-  cmds::TexParameteri cmd;
-  cmd.Init(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_NEAREST);
-  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
-}
 #endif  // GPU_COMMAND_BUFFER_SERVICE_RASTER_DECODER_UNITTEST_1_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.cc b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
index 98123ab..8c6b7f0 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
@@ -173,7 +173,10 @@
   EXPECT_CALL(*gl_, GenTextures(_, _))
       .WillOnce(SetArgPointee<1>(kServiceTextureId))
       .RetiresOnSaturation();
-  GenHelper<cmds::GenTexturesImmediate>(client_texture_id_);
+  cmds::CreateTexture cmd;
+  cmd.Init(false /* use_buffer */, gfx::BufferUsage::GPU_READ,
+           viz::ResourceFormat::RGBA_8888, client_texture_id_);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
 
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
 }
@@ -264,22 +267,6 @@
   ClearSharedMemory();
 }
 
-void RasterDecoderTestBase::DoBindTexture(GLenum target,
-                                          GLuint client_id,
-                                          GLuint service_id) {
-  EXPECT_CALL(*gl_, BindTexture(target, service_id))
-      .Times(1)
-      .RetiresOnSaturation();
-  if (!group_->feature_info()->gl_version_info().BehavesLikeGLES() &&
-      group_->feature_info()->gl_version_info().IsAtLeastGL(3, 2)) {
-    EXPECT_CALL(*gl_, TexParameteri(target, GL_DEPTH_TEXTURE_MODE, GL_RED))
-        .Times(AtMost(1));
-  }
-  cmds::BindTexture cmd;
-  cmd.Init(target, client_id);
-  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-}
-
 void RasterDecoderTestBase::DoDeleteTexture(GLuint client_id,
                                             GLuint service_id) {
   {
diff --git a/infra/config/branch/cq.cfg b/infra/config/branch/cq.cfg
index ce03fb4..b5e6ba5 100644
--- a/infra/config/branch/cq.cfg
+++ b/infra/config/branch/cq.cfg
@@ -79,7 +79,7 @@
       name: "master.tryserver.chromium.linux"
       builders {
         name: "cast_shell_linux"
-        equivalent_to { bucket: "luci.chromium.try" percentage: 10 }
+        equivalent_to { bucket: "luci.chromium.try" percentage: 100 }
       }
       builders {
         name: "chromium_presubmit"
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index e2db0ef..aee20d2 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -249,6 +249,12 @@
     }
 
     builders {
+      name: "linux-blink-heap-verification"
+      mixins: "fyi-ci"
+      mixins: "linux"
+    }
+
+    builders {
       name: "Cast Linux"
       mixins: "linux-ci"
       recipe {
diff --git a/infra/config/global/luci-milo-dev.cfg b/infra/config/global/luci-milo-dev.cfg
index 9613275e..d743c473 100644
--- a/infra/config/global/luci-milo-dev.cfg
+++ b/infra/config/global/luci-milo-dev.cfg
@@ -743,6 +743,11 @@
     short_name: "mvc"
   }
   builders: {
+    name: "buildbot/chromium.win/Win10 Tests x64"
+    category: "release|tester"
+    short_name: "w10"
+  }
+  builders: {
     name: "buildbot/chromium.win/Win x64 Builder (dbg)"
     category: "debug|builder"
     short_name: "64"
@@ -760,11 +765,6 @@
     short_name: "7"
   }
   builders: {
-    name: "buildbot/chromium.win/Win10 Tests x64"
-    category: "debug|tester"
-    short_name: "w10"
-  }
-  builders: {
     name: "buildbot/chromium.win/WinMSVC64 (dbg)"
     category: "debug|tester"
     short_name: "mvc"
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 92486b5..24de48d 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -747,6 +747,11 @@
     short_name: "mvc"
   }
   builders: {
+    name: "buildbot/chromium.win/Win10 Tests x64"
+    category: "release|tester"
+    short_name: "w10"
+  }
+  builders: {
     name: "buildbot/chromium.win/Win x64 Builder (dbg)"
     category: "debug|builder"
     short_name: "64"
@@ -764,11 +769,6 @@
     short_name: "7"
   }
   builders: {
-    name: "buildbot/chromium.win/Win10 Tests x64"
-    category: "debug|tester"
-    short_name: "w10"
-  }
-  builders: {
     name: "buildbot/chromium.win/WinMSVC64 (dbg)"
     category: "debug|tester"
     short_name: "mvc"
@@ -2459,6 +2459,11 @@
     short_name: "IM"
   }
   builders: {
+    name: "buildbucket/luci.chromium.ci/linux-blink-heap-verification"
+    category: "linux|blink"
+    short_name: "IM"
+  }
+  builders: {
     name: "buildbot/chromium.fyi/Linux ARM"
     category: "linux"
   }
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 816af71..17e471c 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -54,6 +54,7 @@
   triggers: "Cast Linux"
   triggers: "GPU Linux Builder"
   triggers: "linux-blink-heap-incremental-marking"
+  triggers: "linux-blink-heap-verificaton"
   triggers: "Linux Builder (dbg)"
   triggers: "Linux Builder"
 
@@ -206,6 +207,16 @@
 }
 
 job {
+  id: "linux-blink-heap-verification"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "linux-blink-heap-verification"
+  }
+}
+
+job {
   id: "Linux Builder (dbg)"
   acl_sets: "default"
   buildbucket: {
diff --git a/ios/chrome/browser/ui/download/BUILD.gn b/ios/chrome/browser/ui/download/BUILD.gn
index 303a970..fa0f462 100644
--- a/ios/chrome/browser/ui/download/BUILD.gn
+++ b/ios/chrome/browser/ui/download/BUILD.gn
@@ -16,6 +16,8 @@
     "legacy_download_manager_controller.mm",
     "pass_kit_coordinator.h",
     "pass_kit_coordinator.mm",
+    "radial_progress_view.h",
+    "radial_progress_view.mm",
   ]
   deps = [
     "resources:download_done",
diff --git a/ios/chrome/browser/ui/download/download_manager_consumer.h b/ios/chrome/browser/ui/download/download_manager_consumer.h
index a6e5e97..d7f956a 100644
--- a/ios/chrome/browser/ui/download/download_manager_consumer.h
+++ b/ios/chrome/browser/ui/download/download_manager_consumer.h
@@ -27,9 +27,12 @@
 // Sets the received size of the file being downloaded in bytes.
 - (void)setCountOfBytesReceived:(int64_t)value;
 
-// Sets the expected size of the file being downloaded in bytes.
+// Sets the expected size of the file being downloaded in bytes. -1 if unknown.
 - (void)setCountOfBytesExpectedToReceive:(int64_t)value;
 
+// Sets the download progress. 1.0 if the download is complete.
+- (void)setProgress:(float)progress;
+
 // Sets the state of the download task. Default is
 // kDownloadManagerStateNotStarted.
 - (void)setState:(DownloadManagerState)state;
diff --git a/ios/chrome/browser/ui/download/download_manager_mediator.h b/ios/chrome/browser/ui/download/download_manager_mediator.h
index 95cc606..f68b538 100644
--- a/ios/chrome/browser/ui/download/download_manager_mediator.h
+++ b/ios/chrome/browser/ui/download/download_manager_mediator.h
@@ -55,7 +55,10 @@
   void UpdateConsumer();
 
   // Converts web::DownloadTask::State to DownloadManagerState.
-  DownloadManagerState GetDownloadManagerState();
+  DownloadManagerState GetDownloadManagerState() const;
+
+  // Converts DownloadTask progress [0;100] to float progress [0.0f;1.0f].
+  float GetDownloadManagerProgress() const;
 
   // web::DownloadTaskObserver overrides:
   void OnDownloadUpdated(web::DownloadTask* task) override;
diff --git a/ios/chrome/browser/ui/download/download_manager_mediator.mm b/ios/chrome/browser/ui/download/download_manager_mediator.mm
index 015ab6e4..6ab8290 100644
--- a/ios/chrome/browser/ui/download/download_manager_mediator.mm
+++ b/ios/chrome/browser/ui/download/download_manager_mediator.mm
@@ -99,11 +99,12 @@
   [consumer_ setState:state];
   [consumer_ setCountOfBytesReceived:task_->GetReceivedBytes()];
   [consumer_ setCountOfBytesExpectedToReceive:task_->GetTotalBytes()];
+  [consumer_ setProgress:GetDownloadManagerProgress()];
   [consumer_
       setFileName:base::SysUTF16ToNSString(task_->GetSuggestedFilename())];
 }
 
-DownloadManagerState DownloadManagerMediator::GetDownloadManagerState() {
+DownloadManagerState DownloadManagerMediator::GetDownloadManagerState() const {
   switch (task_->GetState()) {
     case web::DownloadTask::State::kNotStarted:
       return kDownloadManagerStateNotStarted;
@@ -117,3 +118,9 @@
       return kDownloadManagerStateNotStarted;
   }
 }
+
+float DownloadManagerMediator::GetDownloadManagerProgress() const {
+  if (task_->GetPercentComplete() == -1)
+    return 0.0f;
+  return static_cast<float>(task_->GetPercentComplete()) / 100.0f;
+}
diff --git a/ios/chrome/browser/ui/download/download_manager_mediator_unittest.mm b/ios/chrome/browser/ui/download/download_manager_mediator_unittest.mm
index 21af01f..51441ed 100644
--- a/ios/chrome/browser/ui/download/download_manager_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/download/download_manager_mediator_unittest.mm
@@ -113,6 +113,7 @@
       base::SysNSStringToUTF16(kTestSuggestedFileName));
   task()->SetTotalBytes(kTestTotalBytes);
   task()->SetReceivedBytes(kTestReceivedBytes);
+  task()->SetPercentComplete(80);
 
   mediator_.SetDownloadTask(task());
   mediator_.SetConsumer(consumer_);
@@ -122,6 +123,7 @@
   EXPECT_NSEQ(kTestSuggestedFileName, consumer_.fileName);
   EXPECT_EQ(kTestTotalBytes, consumer_.countOfBytesExpectedToReceive);
   EXPECT_EQ(kTestReceivedBytes, consumer_.countOfBytesReceived);
+  EXPECT_FLOAT_EQ(0.8f, consumer_.progress);
 
   mediator_.SetDownloadTask(nullptr);
 }
@@ -180,6 +182,7 @@
   task()->Start(std::make_unique<net::URLFetcherStringWriter>());
   EXPECT_EQ(kDownloadManagerStateInProgress, consumer_.state);
   EXPECT_FALSE(consumer_.installDriveButtonVisible);
+  EXPECT_EQ(0.0, consumer_.progress);
 
   mediator_.SetDownloadTask(nullptr);
 }
diff --git a/ios/chrome/browser/ui/download/download_manager_view_controller.h b/ios/chrome/browser/ui/download/download_manager_view_controller.h
index 7cd12911e..b9db9cc 100644
--- a/ios/chrome/browser/ui/download/download_manager_view_controller.h
+++ b/ios/chrome/browser/ui/download/download_manager_view_controller.h
@@ -16,6 +16,7 @@
 extern NSString* const kDownloadManagerFailedImage;
 
 @class DownloadManagerViewController;
+@class RadialProgressView;
 
 @protocol DownloadManagerViewControllerDelegate<NSObject>
 @optional
@@ -67,6 +68,9 @@
 // setInstallGoogleDriveButtonVisible:animated: was called with YES.
 @property(nonatomic, readonly) UIButton* installDriveButton;
 
+// View that represents download progress.
+@property(nonatomic, readonly) RadialProgressView* progressView;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_DOWNLOAD_DOWNLOAD_MANAGER_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/download/download_manager_view_controller.mm b/ios/chrome/browser/ui/download/download_manager_view_controller.mm
index a9e3990..d22e7295 100644
--- a/ios/chrome/browser/ui/download/download_manager_view_controller.mm
+++ b/ios/chrome/browser/ui/download/download_manager_view_controller.mm
@@ -7,6 +7,7 @@
 #include "base/logging.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/strings/grit/components_strings.h"
+#import "ios/chrome/browser/ui/download/radial_progress_view.h"
 #import "ios/chrome/browser/ui/util/named_guide.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
@@ -44,10 +45,12 @@
   UILabel* _statusLabel;
   UIButton* _actionButton;
   UIButton* _installDriveButton;
+  RadialProgressView* _progressView;
 
   NSString* _fileName;
   int64_t _countOfBytesReceived;
   int64_t _countOfBytesExpectedToReceive;
+  float _progress;
   DownloadManagerState _state;
   BOOL _installDriveButtonVisible;
 }
@@ -101,6 +104,7 @@
   [self.downloadControlsRow addSubview:self.closeButton];
   [self.downloadControlsRow addSubview:self.statusIcon];
   [self.downloadControlsRow addSubview:self.statusLabel];
+  [self.downloadControlsRow addSubview:self.progressView];
   [self.downloadControlsRow addSubview:self.actionButton];
   [self.installDriveControlsRow addSubview:self.installDriveButton];
   [self.installDriveControlsRow addSubview:self.horizontalLine];
@@ -177,6 +181,17 @@
         constraintEqualToAnchor:downloadRow.layoutMarginsGuide.leadingAnchor],
   ]];
 
+  // progress view constraints.
+  RadialProgressView* progressView = self.progressView;
+  [NSLayoutConstraint activateConstraints:@[
+    [progressView.leadingAnchor
+        constraintEqualToAnchor:statusIcon.leadingAnchor],
+    [progressView.trailingAnchor
+        constraintEqualToAnchor:statusIcon.trailingAnchor],
+    [progressView.topAnchor constraintEqualToAnchor:statusIcon.topAnchor],
+    [progressView.bottomAnchor constraintEqualToAnchor:statusIcon.bottomAnchor],
+  ]];
+
   // status label constraints.
   UILabel* statusLabel = self.statusLabel;
   UIButton* actionButton = self.actionButton;
@@ -244,12 +259,20 @@
   }
 }
 
+- (void)setProgress:(float)value {
+  if (_progress != value) {
+    _progress = value;
+    [self updateProgressView];
+  }
+}
+
 - (void)setState:(DownloadManagerState)state {
   if (_state != state) {
     _state = state;
     [self updateStatusIcon];
     [self updateStatusLabel];
     [self updateActionButton];
+    [self updateProgressView];
   }
 }
 
@@ -376,6 +399,17 @@
   return _installDriveButton;
 }
 
+- (RadialProgressView*)progressView {
+  if (!_progressView) {
+    _progressView = [[RadialProgressView alloc] initWithFrame:CGRectZero];
+    _progressView.translatesAutoresizingMaskIntoConstraints = NO;
+    _progressView.lineWidth = 2;
+    _progressView.tintColor = [MDCPalette bluePalette].tint500;
+    [self updateProgressView];
+  }
+  return _progressView;
+}
+
 - (UIView*)horizontalLine {
   if (!_horizontalLine) {
     _horizontalLine = [[UIView alloc] init];
@@ -536,6 +570,11 @@
   self.actionButton.hidden = _state == kDownloadManagerStateInProgress;
 }
 
+- (void)updateProgressView {
+  self.progressView.hidden = _state != kDownloadManagerStateInProgress;
+  self.progressView.progress = _progress;
+}
+
 // Updates alpha value for install google drive controls row.
 // Makes whole installDriveControlsRow opaque if
 // _installDriveButtonVisible is set to YES, otherwise makes the row
diff --git a/ios/chrome/browser/ui/download/download_manager_view_controller_unittest.mm b/ios/chrome/browser/ui/download/download_manager_view_controller_unittest.mm
index 7f764fc..ffed636f 100644
--- a/ios/chrome/browser/ui/download/download_manager_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/download/download_manager_view_controller_unittest.mm
@@ -4,6 +4,9 @@
 
 #import "ios/chrome/browser/ui/download/download_manager_view_controller.h"
 
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/download/radial_progress_view.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
@@ -34,6 +37,7 @@
                                titleForState:UIControlStateNormal]);
   EXPECT_NSEQ([UIImage imageNamed:kDownloadManagerNotStartedImage],
               view_controller_.statusIcon.image);
+  EXPECT_TRUE(view_controller_.progressView.hidden);
 }
 
 // Tests label and button titles with kDownloadManagerStateNotStarted state
@@ -49,6 +53,7 @@
                                titleForState:UIControlStateNormal]);
   EXPECT_NSEQ([UIImage imageNamed:kDownloadManagerNotStartedImage],
               view_controller_.statusIcon.image);
+  EXPECT_TRUE(view_controller_.progressView.hidden);
 }
 
 // Tests label and button hidden state with kDownloadManagerStateInProgress
@@ -57,11 +62,14 @@
   view_controller_.state = kDownloadManagerStateInProgress;
   view_controller_.fileName = @"longfilenamesolongthatitbarelyfitwidthlimit";
   view_controller_.countOfBytesExpectedToReceive = 10 * 1024;
+  view_controller_.progress = 0.0f;
 
   EXPECT_NSEQ(@"Downloading… Zero KB/10 KB", view_controller_.statusLabel.text);
   EXPECT_TRUE(view_controller_.actionButton.hidden);
   EXPECT_NSEQ([UIImage imageNamed:kDownloadManagerInProgressImage],
               view_controller_.statusIcon.image);
+  EXPECT_FALSE(view_controller_.progressView.hidden);
+  EXPECT_EQ(0.0f, view_controller_.progressView.progress);
 }
 
 // Tests label and button hidden state with kDownloadManagerStateInProgress
@@ -72,11 +80,14 @@
   view_controller_.fileName = @"file.zip";
   view_controller_.countOfBytesReceived = 900;
   view_controller_.countOfBytesExpectedToReceive = -1;
+  view_controller_.progress = 0.9f;
 
   EXPECT_NSEQ(@"Downloading… 900 bytes", view_controller_.statusLabel.text);
   EXPECT_TRUE(view_controller_.actionButton.hidden);
   EXPECT_NSEQ([UIImage imageNamed:kDownloadManagerInProgressImage],
               view_controller_.statusIcon.image);
+  EXPECT_FALSE(view_controller_.progressView.hidden);
+  EXPECT_EQ(0.9f, view_controller_.progressView.progress);
 }
 
 // Tests label and button titles with kDownloadManagerStateSucceeded state.
@@ -90,6 +101,7 @@
                                titleForState:UIControlStateNormal]);
   EXPECT_NSEQ([UIImage imageNamed:kDownloadManagerSucceededImage],
               view_controller_.statusIcon.image);
+  EXPECT_TRUE(view_controller_.progressView.hidden);
 }
 
 // Tests label and button titles with kDownloadManagerStateFailed state.
@@ -103,6 +115,7 @@
                                 titleForState:UIControlStateNormal]);
   EXPECT_NSEQ([UIImage imageNamed:kDownloadManagerFailedImage],
               view_controller_.statusIcon.image);
+  EXPECT_TRUE(view_controller_.progressView.hidden);
 }
 
 // Tests that tapping close button calls downloadManagerViewControllerDidClose:.
diff --git a/ios/chrome/browser/ui/download/radial_progress_view.h b/ios/chrome/browser/ui/download/radial_progress_view.h
new file mode 100644
index 0000000..c143895
--- /dev/null
+++ b/ios/chrome/browser/ui/download/radial_progress_view.h
@@ -0,0 +1,22 @@
+// 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 IOS_CHROME_BROWSER_UI_DOWNLOAD_RADIAL_PROGRESS_VIEW_H_
+#define IOS_CHROME_BROWSER_UI_DOWNLOAD_RADIAL_PROGRESS_VIEW_H_
+
+#import <UIKit/UIKit.h>
+
+// View that draws progress as arc. Arc starts at 12 o'clock and drawn
+// clockwise. The arc color is tintColor.
+@interface RadialProgressView : UIView
+
+// Progress in [0.0f, 1.0f] range.
+@property(nonatomic) float progress;
+
+// The line width used when stroking the progress arc.
+@property(nonatomic) CGFloat lineWidth;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_DOWNLOAD_RADIAL_PROGRESS_VIEW_H_
diff --git a/ios/chrome/browser/ui/download/radial_progress_view.mm b/ios/chrome/browser/ui/download/radial_progress_view.mm
new file mode 100644
index 0000000..dc169c8
--- /dev/null
+++ b/ios/chrome/browser/ui/download/radial_progress_view.mm
@@ -0,0 +1,64 @@
+// 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.
+
+#import "ios/chrome/browser/ui/download/radial_progress_view.h"
+
+#import <QuartzCore/QuartzCore.h>
+
+#include "base/mac/foundation_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface RadialProgressView ()
+// CALayer that backs this view up.
+@property(nonatomic, readonly) CAShapeLayer* shapeLayer;
+@end
+
+@implementation RadialProgressView
+
+@synthesize progress = _progress;
+@synthesize lineWidth = _lineWidth;
+
+#pragma mark - UIView overrides
+
++ (Class)layerClass {
+  return [CAShapeLayer class];
+}
+
+- (void)willMoveToSuperview:(UIView*)newSuperview {
+  if (newSuperview) {
+    self.shapeLayer.fillColor = [UIColor clearColor].CGColor;
+    self.shapeLayer.strokeColor = self.tintColor.CGColor;
+    self.shapeLayer.lineWidth = self.lineWidth;
+  }
+}
+
+#pragma mark - Public
+
+- (void)setProgress:(float)progress {
+  if (_progress == progress)
+    return;
+
+  _progress = progress;
+  CGPoint center =
+      CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
+  CGFloat radius = CGRectGetWidth(self.bounds) / 2 - 1;
+  UIBezierPath* path =
+      [UIBezierPath bezierPathWithArcCenter:center
+                                     radius:radius
+                                 startAngle:0 - M_PI_2  // 12 o'clock
+                                   endAngle:M_PI * 2 * progress - M_PI_2
+                                  clockwise:YES];
+  self.shapeLayer.path = path.CGPath;
+}
+
+#pragma mark - Private
+
+- (CAShapeLayer*)shapeLayer {
+  return base::mac::ObjCCastStrict<CAShapeLayer>(self.layer);
+}
+
+@end
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm
index 5a2242f7..1c36bb45 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm
@@ -7,6 +7,7 @@
 #import <Foundation/Foundation.h>
 #include <memory>
 
+#include "base/mac/foundation_util.h"
 #include "base/test/scoped_task_environment.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/ui/tab_grid/grid_commands.h"
@@ -63,7 +64,8 @@
 // mediator.
 TEST_F(TabGridMediatorTest, ConsumerPopulateItems) {
   [[consumer_ verify] populateItems:[OCMArg checkWithBlock:^BOOL(id value) {
-                        NSArray* items = static_cast<NSArray*>(value);
+                        NSArray* items =
+                            base::mac::ObjCCastStrict<NSArray>(value);
                         EXPECT_EQ(3UL, items.count);
                         return YES;
                       }]
@@ -80,7 +82,8 @@
                                   WebStateList::INSERT_FORCE_INDEX,
                                   WebStateOpener());
   [[consumer_ verify] insertItem:[OCMArg checkWithBlock:^BOOL(id value) {
-                        GridItem* item = static_cast<GridItem*>(value);
+                        GridItem* item =
+                            base::mac::ObjCCastStrict<GridItem>(value);
                         EXPECT_NSEQ(item_identifier, item.identifier);
                         return YES;
                       }]
@@ -111,7 +114,7 @@
   [[consumer_ verify]
       replaceItemAtIndex:1
                 withItem:[OCMArg checkWithBlock:^BOOL(id value) {
-                  GridItem* item = static_cast<GridItem*>(value);
+                  GridItem* item = base::mac::ObjCCastStrict<GridItem>(value);
                   EXPECT_NSEQ(new_item_identifier, item.identifier);
                   return YES;
                 }]];
diff --git a/ios/chrome/test/fakes/fake_download_manager_consumer.h b/ios/chrome/test/fakes/fake_download_manager_consumer.h
index bd941ac..1ae7e169 100644
--- a/ios/chrome/test/fakes/fake_download_manager_consumer.h
+++ b/ios/chrome/test/fakes/fake_download_manager_consumer.h
@@ -21,6 +21,9 @@
 // The expected size of the file being downloaded in bytes.
 @property(nonatomic) int64_t countOfBytesExpectedToReceive;
 
+// Download progress. 1.0 if the download is complete.
+@property(nonatomic) float progress;
+
 // State of the download task. Default is kDownloadManagerStateNotStarted.
 @property(nonatomic) DownloadManagerState state;
 
diff --git a/ios/chrome/test/fakes/fake_download_manager_consumer.mm b/ios/chrome/test/fakes/fake_download_manager_consumer.mm
index acc420d..f6d3aba 100644
--- a/ios/chrome/test/fakes/fake_download_manager_consumer.mm
+++ b/ios/chrome/test/fakes/fake_download_manager_consumer.mm
@@ -12,6 +12,7 @@
 @synthesize fileName = _fileName;
 @synthesize countOfBytesReceived = _countOfBytesReceived;
 @synthesize countOfBytesExpectedToReceive = _countOfBytesExpectedToReceive;
+@synthesize progress = _progress;
 @synthesize state = _state;
 @synthesize installDriveButtonVisible = _installDriveButtonVisible;
 
diff --git a/ios/third_party/motion_animator_objc/README.chromium b/ios/third_party/motion_animator_objc/README.chromium
index cd967df..bd5e7c42c 100644
--- a/ios/third_party/motion_animator_objc/README.chromium
+++ b/ios/third_party/motion_animator_objc/README.chromium
@@ -1,7 +1,7 @@
 Name: Motion Animator for Objective-C
 URL: https://github.com/material-motion/motion-animator-objc
 Version: 0
-Revision: ff39ecc69fdee46d388cc9f882201d54c3d5039c
+Revision: 5df831026445004b2fc0f6a42f8b8f33af46512b
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/media/base/key_systems.cc b/media/base/key_systems.cc
index 27b632c..eb6174d 100644
--- a/media/base/key_systems.cc
+++ b/media/base/key_systems.cc
@@ -356,9 +356,9 @@
   if (properties.UseAesDecryptor())
     return true;
 
-  // For External Clear Key, it is either implemented as a pepper CDM (Clear Key
-  // CDM), which is covered above, or by using AesDecryptor remotely, e.g. via
-  // MojoCdm. In both cases, we can block. This is only used for testing.
+  // For External Clear Key, it is either implemented as a library CDM (Clear
+  // Key CDM), which is covered above, or by using AesDecryptor remotely, e.g.
+  // via MojoCdm. In both cases, we can block. This is only used for testing.
   if (base::FeatureList::IsEnabled(media::kExternalClearKeyForTesting) &&
       IsExternalClearKey(properties.GetKeySystemName()))
     return true;
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 94b9cc3..b8e7136 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -235,9 +235,6 @@
 const base::Feature kMemoryPressureBasedSourceBufferGC{
     "MemoryPressureBasedSourceBufferGC", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// On systems where pepper CDMs are enabled, use mojo CDM instead of PPAPI CDM.
-const base::Feature kMojoCdm{"MojoCdm", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enable MojoVideoDecoder.  Has no effect except on Android currently.
 const base::Feature kMojoVideoDecoder{"MojoVideoDecoder",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index 2827fd0..cba9e8202 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -116,7 +116,6 @@
 MEDIA_EXPORT extern const base::Feature kRecordMediaEngagementScores;
 MEDIA_EXPORT extern const base::Feature kMediaEngagementBypassAutoplayPolicies;
 MEDIA_EXPORT extern const base::Feature kMemoryPressureBasedSourceBufferGC;
-MEDIA_EXPORT extern const base::Feature kMojoCdm;
 MEDIA_EXPORT extern const base::Feature kMojoVideoDecoder;
 MEDIA_EXPORT extern const base::Feature kMseBufferByPts;
 MEDIA_EXPORT extern const base::Feature kMseFlacInIsobmff;
diff --git a/media/mojo/README.md b/media/mojo/README.md
index 3824154..bbc83ea 100644
--- a/media/mojo/README.md
+++ b/media/mojo/README.md
@@ -66,7 +66,8 @@
 mojo_media_services = ["renderer", "cdm"]
 ```
 Note that you must set `enable_mojo_media` first.  Also, some remote media
-components are also controlled by run time features, e.g. `media::kMojoCdm`.
+components are also controlled by run time features, e.g.
+`media::kMojoVideoDecoder`.
 
 ### Media Mojo Interface Factory
 
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index e4f8457..35ba345e 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -10769,7 +10769,6 @@
     { "name": "setphaserstostun.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "setuid.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sevenmatches.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "shadoom.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "shadowsocks.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "shadowsworldonline.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "shagi29.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -20131,7 +20130,6 @@
     { "name": "sokietech.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "skynetz.tk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "smartviewing.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "sinnovate.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "silentexplosion.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "shopherbal.co.za", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "slashand.co", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -24092,7 +24090,6 @@
     { "name": "painefamily.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "pantallasled.mx", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "paradiesgirls.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "paradisenazarene.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "paratxt.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "parkrocker.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "payboy.rocks", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -26266,7 +26263,6 @@
     { "name": "jaan.su", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "jability.ovh", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "jacobdevans.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "jakeguild.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "jamanji.com.ng", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "james-bell.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "jamesaimonetti.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -27460,7 +27456,6 @@
     { "name": "riversideradio.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "rj-onderneemt.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "rkc-hygrotherm.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "rlsnet.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "rm-it.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "rnag.ie", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "robertabittle.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -45972,6 +45967,810 @@
     { "name": "zimmer-voss.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zmscable.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zylai.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "1231212.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "123123q.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "123123qq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "123bearing.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "123bearing.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "123bearing.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "123roulement.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "123roulement.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24timeravis.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2au.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3555500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3lot.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500103.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500108.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500a500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500b500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500c500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500d500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500e500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500f500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500g500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500h500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500i500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500j500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500k500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500l500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500m500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500n500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500o500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500p500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500pingtai.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500q500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500r500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500s500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500t500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500u500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500y500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "500z500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "5364.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "5364d.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "56877.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "918yy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "91966.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aanbieders.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abos.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "activecare-monitor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adf-safetytools.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "advanceworx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "affordablehealthquotesforyou.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "affvps.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "agechecker.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "agenziaimmobiliarezeta.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "airikai.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alexanderb.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alistairholland.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "allprorisk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "altered.network", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "altphotos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "amello.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "andalusierondreizen.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "andycloud.dynu.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "angehardy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "anglesgirl.eu.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "animojis.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "annmariewaltsphotography.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ansgar.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "appify.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "applicationmanager.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "appshuttle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "arno-klein.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "arno-klein.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "arno-klein.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "arschkrebs.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aspirateur-anti-pollution.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "atgroup.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "autorando.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "autospurgo.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "awxg.eu.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "b72.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "baches-piscines.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "badgesenpatches.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "balia.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "balticnetworks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "banketbesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "baopublishing.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "baraxolka.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bart-f.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "baseconvert.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bauer.network", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bck-koethen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "beckon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "beginatzero.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "benc.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "benmorecentre.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bergfreunde.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "berz.one", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bestautoinsurance.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bettingsider.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bfgcdn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bflix.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "binarydream.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "birdslabel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bitcalt.eu.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bitcalt.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bitmarket.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bitmarket.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bloemenbesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blueoakart.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blueskycoverage.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blundell.wedding", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bmwcolors.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "borisenko.by", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bowlcake.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bozdoz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "br7.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brainsik.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brightonzhang.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brimspark.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brimspark.systems", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "broodbesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bsd.com.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "buddhismus.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "buena.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bueroplus.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "buffaloturf.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "buyritefairview.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bwl-earth.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cadre.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "caibi.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "callanbryant.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "canadian-nurse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cannahealth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "carbontv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cardexchangesolutions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "carlobiagi.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "caryefurd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "catalogobiblioteca.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cb-crochet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cbmusa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cd5k.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cdasenegal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cedricmartineau.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "celebrityhealthcritic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "celluliteorangeskin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "celluliteremovaldiet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "centralegedimat.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chargify.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chasetrails.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "checkyourreps.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chimerity.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cinemarxism.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cio-cisointerchange.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "citybeat.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "clase3.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cldfile.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "clicandfioul.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cliniquecomplementaire.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cloaked.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cloudfiles.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cmftech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cna-aiic.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cnaprograms.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cnatraining.network", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "codigo-bonus-bet.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "codigodelbonusbet365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "coins2001.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "colpacpackaging.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "combatircelulitis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "combattrecellulite.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "comodosslstore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "conju.cat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "constares.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "consultingroupitaly.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cooljs.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "coolpickz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "coussinsky.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cpd-education.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "craftinghand.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "craigsimpson.scot", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "croeder.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "croncron.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "crownpoint.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cryptocaseproject.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cursosgratuitos.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cyberatlantis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cybercareers.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "d.rip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "daniel-milnes.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dannycairns.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "danwolff.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dataregister.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "davidhanle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dawoud.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ddnsweb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "debbyefurd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dedge.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "deechtebakkers.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dekeurslagers.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dekulk.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "delf.co.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "der-lan.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "despachomartinyasociados.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "deutsche-seniorenbetreuung.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dharamkot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dictionaryofnumbers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dietaanticelulitica.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dietaanticelulitis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dietacelulitis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dietafeliz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "disarc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "divi-experte.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "djsk.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dko-steiermark.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dmailshop.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dmmultionderhoud.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dmschilderwerken.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "domster.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "doooooops.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dorfzittig.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "douglasstafford.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "drachenleder.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "drakensberg-tourism.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "drmayakato.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dubbingkursus.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "duckduck.horse", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dupree.pe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dyn-dnhensel.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dzsibi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "e-baraxolka.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "easydumpsterrental.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "easyreal.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "easyschools.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ebest.co.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "echo.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "echoteam.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "echoteen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "econverter.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "edgevelder.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "edinburghsportsandoutdoorlearning.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "edlinger.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "edlinger.mobi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "educationfutures.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "edwinyrkuniversity.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eenekorea.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eengoedenotaris.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eldapoint.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "electrotainment.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "elektronische-post.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "elerizoentintado.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eliminercellulite.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "elisabethrene.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "emkrivoy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "emmababy420.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "employeeexpress.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "enbecom.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "encrypt.org.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ent-london.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "entercenter.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "epo32.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ervaarjapan.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "espacecuisine.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "essayshark.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "estedafah.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "euwid.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "evangelicalmagazine.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ex-deli.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "exoscale.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "experienceoutdoors.org.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eztvtorrent.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fabrica360.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "facebydrh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "facucosta.com.ar", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "failoverplan.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fairssl.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "faisalshuvo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "faithleaks.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "familyworld.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "faraonplay5.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fegli.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "final-expense-quotes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "financepark.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "finnwea.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fir3net.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "flashbaggie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "flaviu.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fliio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fnbnokomis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "followthedog.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "forodieta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "foundchurch.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "frankinteriordesign.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "freddieonfire.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "freitasul.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "freitasul.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "frequentflyerapp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "friederloch.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "frietbesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "funkner.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "funtime.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fxmarketing.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fxmarketing.net.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fxseo.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fxwebsites.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fxwebsites.net.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fyksen.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "garyrh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "geekystudios.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gesundheitmassage.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "getbreadcrumbs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "getpanelapp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "getthefriendsyouwant.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "give2charity.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "give2charityapp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "glenavy.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "glenberviegolfclub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "glotech.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "goflo.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gogonano.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "got-tty.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gpga.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "grafmag.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "greatlengthshairextensionssalon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "greatlifeinsurancegroup.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "greener.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "greenhats.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "groentebesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "guides-et-admin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "guildbase.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hakaru.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hamburgerbesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hamcram.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "happydietplan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hardez.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "healthstar-dev.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "healthstar.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "helicaldash.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hendrik.li", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hestervanderheijden.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "higgsboson.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hilaryhutler.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hitandhealth.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hoevenstein.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hofauer.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hookany.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "horizonlawncare.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hostcoz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hsn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hsulei.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "i-aloks.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ianmooreis.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "icloudlogin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "icowhitepapers.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "idealvenir.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "idlethoughtsandramblings.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ilemonrain.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "illustrate.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ilove.fish", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "imask.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "imppac-schmuck.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "indianaberry.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "inf-fusion.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "infirmiere-canadienne.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "insideofgaming.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "insolved.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "intal.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "intelliance.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "internationaltalento.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "internet-aukcion.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "intita.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "invadelabs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ipresent.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "itsense.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jahmusic.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "janvari.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "janvaribalint.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jkyuan.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "johnsonho.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jolokia.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "juef.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "juliekproperties.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jurijbuga.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "justbelieverecoverypa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kaasbesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kaitol.click", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kamui.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "karlloch.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kebabbesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "keithlomax.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kempo-sissach.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kenbillionsyuan.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kennynet.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kenx5.eu.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kevinmoreland.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kids-ok.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kimdumaine.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kinderopvangzevenbergen.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kisma.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "klaxon.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "klcreations.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kleim.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "knightsbridgewine.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "komelin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "koolitee.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kouki-food.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kuchenfeelisa.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kuhnelautorepair.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kulickovy-pojezd.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kuttler.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kx197.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lakehavasuhomes.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lamp.re", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "landrovermerriamparts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "laparoscopia.com.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "laranjada.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "larbertbaptist.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lavasing.eu.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "laylo.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lemonparty.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lemouillour.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lespret.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "letraba.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "leulu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lietaer.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lifeinsurancepro.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lighthouseinstruments.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "limap.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lincolnfinewines.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linearmap.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "literarymachin.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "littlebestfriend.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "littleredsbakeshop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lmrcouncil.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "location-fichier-email.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "locationvoiturecorse.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "loposchokk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lotl.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lyukaacom.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lzqii.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mack.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mackeysack.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "madandpissedoff.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "maddistonevangelical.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "maddistonparentcouncil.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "maddistonpsa.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "madweb.design", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "magical-secrets.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "magicalcircuslv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "maisondoree.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "maitriser-son-stress.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "makersatwork.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "manoro.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "manylots.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "marechal-company.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "marelijah.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "markantoffice.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "marketlinks.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "martasibaja.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "matt-royal.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mauran.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "maxp.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mcfx.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mcivor.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mcprocdn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mechok.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "medicare-providers.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "medicarecoveragefinder.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "medicareinfo.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "medigap-quote.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mediter-simplement.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "megauction.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "meine-reise-gut-versichert.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "menudieta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mercedespartscenter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "meshok.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "metafurquest.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mfacko.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mgtbaas.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "michiganstateuniversityonline.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mickusit.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mieuxvivreadarvoy.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "millibitcoin.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "minimal-apps.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "monakasatmasr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "moneypark.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "morepablo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mormonleaks.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "morningcurve.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "movie1000.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mtgsuomi.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "muckrack.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myadpost.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myphotoshopbrushes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myproxy.eu.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myrealestateschool.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mytfg.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "naomiheji.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "narenderchopra.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nationalhomequotes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nationwiderealtyinvestors.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nbib.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nbrain.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "neil-barrett.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "neil-barrett.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nejenpneu.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "netbrewventures.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "netfog.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "netsafeid.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nettegeschenke.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "newchance.store", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "niallator.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nicholasperkins.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nirjharstudio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nllboard.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nlleisure.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nobledust.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "norys-escape.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nostosh.eu.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nugdev.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "numbermunchers.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nurseone.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nursingschool.network", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nwbc.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nzdmo.govt.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "octarineparrot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ohne-name.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "olltechjob.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "omdesign.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "online-health-insurance.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "oscillation-services.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "osielnava.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ourworldindata.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "overrustle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "owid.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "owl-hakkei.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "owl-square.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "paass.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pamsorel.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "paris-store.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "parkhillsbaptist.church", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "partypearl.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "partyspecialists.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "patatbesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pdomo.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "peaudorange.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pens.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "penser-electronique.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pestkill.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "peterbruceharvey.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "philipp-trulson.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "phosphene.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "photosoftware.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pieldenaranja.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pietermaene.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pioneer-car.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pioneer-rus.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pircher.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pixabay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pizzabesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "placedaffiliate.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "planetknauer.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "play-charades.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "plumbingcentral.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pmaene.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "policesromandesrecrutement.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "policyreporter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "policyreporter.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "polit-it.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pomockypredeti.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "poopr.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "portofala.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "posyperfume.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "precision.st", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "prijsvergelijken.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "primalbase.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pristinegreenlandscaping.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "producepromotions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "progenitor.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "programmaticmagic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "psdreams.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pst.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "psychicsource.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "psytrance-pro.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "punte-juwelier.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "q123123.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "quant-labs.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "quarkdose.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "questions-admin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "quiet-waters.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "qwertyatom100.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "r33.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rabica.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rainstormsinjuly.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "raipet.no-ip.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "randomcode.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "raoul-kieffer.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rawr.sexy", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rcsolutions.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "re-engines.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "reformatreality.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "regime-anticellulite.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "regimebonheur.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "regimecellulite.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "reginagroffy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "removalcellulite.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "remrol.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "renaissanceplasticsurgery.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "renewpfc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rent-a-c.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "reseponline.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rideyourdamn.bike", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rightnetworks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ristoviitanen.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rittis.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "robottip.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rodafe.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "routercncperu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rpus.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rucksack-rauf-und-weg.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rusmolotok.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ryancarter.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "safeui.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "salexy.kz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "salvagedfurnitureparlour.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sanipousse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sarahwikeley.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sasanika.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "science-anatomie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "scootaloo.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "scottishcu.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "scrod.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sdg-tracker.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "segaretro.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "semrush.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "seriouss.am", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sgutranscripts.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shaamrelief.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shadwe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shellgame.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shermantank.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shivammaheshwari.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shopstart.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shorehamfort.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "short-term-plans.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shouldihookupwithmybarista.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "showroom113.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shredriteservices.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shyuka.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sigmapramuka.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "silvobeat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "simplegreen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sirchuk.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sivale.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "skatesins.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "skid.church", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "skomski.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "smartservices.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "snackbesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sodomojo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "somaliaonline.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "songluck.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spacedots.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sphido.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spittank.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sportvereine.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spotteredu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spreadsheetgear.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "srun.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ssh.nu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "star-clean.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "startergen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stephaniedeady.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sterlinx.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sternadel.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sternenbund.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stilecop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "strajnar.si", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "strongsalpinesucculents.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stroomacties.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "structure.systems", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stuartmorris.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "studentse.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "summershomes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sun-leo.co.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "supermercadosdia.com.ar", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sushibesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "svinformatica.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "taartbesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tagabrand.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tagpay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "taimane.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "talkingmoose.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "taxi-puck.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "team2fou.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "teddyss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "teenerotic.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "telamon.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "telecharger-itunes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "telework.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "teltru.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tempflix.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tetrafinancial-manufacturing-industrial-equipment-financing.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tetrafinancial-news.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tetrafinancial-technology-equipment-software-financing.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "teufel.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thatdarkplace.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "theanticellulitediet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thecellulitediet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "themaster.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "themefoxx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "themonkeytrail.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thestudyla.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "theuucc.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "think-positive-watches.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thomasscholz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tildesnyder.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "timbrust.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "time2choose.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "timoso.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tirlins.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tjampoer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tleng.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tnl.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tobyalden.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tokyobarbershop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "toonsburgh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "torg-room.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trashwagon.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "travelshack.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trezy.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trezy.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "truentumvet.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trustserv.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tslcontractors.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tss.am", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tsung.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tty1.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "u-metals.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uc.ac.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uitslagensoftware.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "unatco.noip.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "unedouleur.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "unstamps.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "usage.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "usastaffing.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uwsoftware.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uwvloereruit.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vapecrunch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "venenum.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "verry.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "versbesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "versolslapeyre.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vestingbar.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "via-shire-krug.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "visionnissancanandaiguaparts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vjpatel.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vleesbesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vlzbazar.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vocalsynth.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vulns.sexy", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vykup-car.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wallacehigh.org.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wallacequinn.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "waterdrop.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webgaff.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webhostingzzp.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "weedcircles.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "weien.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wella-download-center.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "welpo.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wendlberger.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wg-steubenstrasse.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wiebel.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wijnbesteld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wjr.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "womensalespros.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wonghome.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wpcdn.bid", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wpmu-tutorials.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wpoptimalizace.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wpsec.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wristreview.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wrp-timber-mouldings.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wtwk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wyo.cam", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "x-one.co.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xia.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn-----8kcgbo2bmdgkdacthvjf.xn--p1ai", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--4pv80kkz8auzf.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--qfun83b.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xpoc.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xzoneadventure.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yahan.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yazaral.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ylde.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yourskin.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yuanjiazhao.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yukari.cafe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zeelynk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zfg.li", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zhl123.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zorig.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zula.africa", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zumazar.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zyger.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     // END OF 1-YEAR BULK HSTS ENTRIES
 
     // Only eTLD+1 domains can be submitted automatically to hstspreload.org,
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index d8d6ab6c..77a6d78 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -242,12 +242,12 @@
 // ------------------------------------------------------------------------
 
 // The start/end of a proxy resolve request.
-EVENT_TYPE(PROXY_SERVICE)
+EVENT_TYPE(PROXY_RESOLUTION_SERVICE)
 
 // The time while a request is waiting on InitProxyResolver to configure
 // against either WPAD or custom PAC URL. The specifics on this time
 // are found from ProxyResolutionService::init_proxy_resolver_log().
-EVENT_TYPE(PROXY_SERVICE_WAITING_FOR_INIT_PAC)
+EVENT_TYPE(PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC)
 
 // This event is emitted to show what the PAC script returned. It can contain
 // extra parameters that are either:
@@ -259,7 +259,7 @@
 //   {
 //      "net_error": <Net error code that resolver failed with>,
 //   }
-EVENT_TYPE(PROXY_SERVICE_RESOLVED_PROXY_LIST)
+EVENT_TYPE(PROXY_RESOLUTION_SERVICE_RESOLVED_PROXY_LIST)
 
 // This event is emitted after proxies marked as bad have been deprioritized.
 //
@@ -267,7 +267,7 @@
 //   {
 //      "pac_string": <List of valid proxy servers, in PAC format>,
 //   }
-EVENT_TYPE(PROXY_SERVICE_DEPRIORITIZED_BAD_PROXIES)
+EVENT_TYPE(PROXY_RESOLUTION_SERVICE_DEPRIORITIZED_BAD_PROXIES)
 
 // This event is emitted whenever the proxy settings used by
 // ProxyResolutionService change.
diff --git a/net/proxy_resolution/proxy_service.cc b/net/proxy_resolution/proxy_service.cc
index 4efd88a50..388cd9c 100644
--- a/net/proxy_resolution/proxy_service.cc
+++ b/net/proxy_resolution/proxy_service.cc
@@ -848,7 +848,7 @@
     user_callback_.Reset();
     results_ = NULL;
 
-    net_log_.EndEvent(NetLogEventType::PROXY_SERVICE);
+    net_log_.EndEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE);
   }
 
   // Returns true if Cancel() has been called.
@@ -1039,7 +1039,7 @@
     const NetLogWithSource& net_log) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  net_log.BeginEvent(NetLogEventType::PROXY_SERVICE);
+  net_log.BeginEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE);
 
   // Notify our polling-based dependencies that a resolve is taking place.
   // This way they can schedule their polls in response to network activity.
@@ -1078,7 +1078,7 @@
       return req->QueryDidComplete(rv);
   } else {
     req->net_log()->BeginEvent(
-        NetLogEventType::PROXY_SERVICE_WAITING_FOR_INIT_PAC);
+        NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC);
   }
 
   DCHECK_EQ(ERR_IO_PENDING, rv);
@@ -1152,7 +1152,7 @@
       req->CancelResolveJob();
 
       req->net_log()->BeginEvent(
-          NetLogEventType::PROXY_SERVICE_WAITING_FOR_INIT_PAC);
+          NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC);
     }
   }
 }
@@ -1172,7 +1172,7 @@
     Request* req = it->get();
     if (!req->is_started() && !req->was_cancelled()) {
       req->net_log()->EndEvent(
-          NetLogEventType::PROXY_SERVICE_WAITING_FOR_INIT_PAC);
+          NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC);
 
       // Note that we re-check for synchronous completion, in case we are
       // no longer using a ProxyResolver (can happen if we fell-back to manual).
@@ -1328,20 +1328,22 @@
     if (proxy_delegate)
       proxy_delegate->OnResolveProxy(url, method, proxy_retry_info_, result);
 
-    net_log.AddEvent(NetLogEventType::PROXY_SERVICE_RESOLVED_PROXY_LIST,
-                     base::Bind(&NetLogFinishedResolvingProxyCallback, result));
+    net_log.AddEvent(
+        NetLogEventType::PROXY_RESOLUTION_SERVICE_RESOLVED_PROXY_LIST,
+        base::Bind(&NetLogFinishedResolvingProxyCallback, result));
 
     // This check is done to only log the NetLog event when necessary, it's
     // not a performance optimization.
     if (!proxy_retry_info_.empty()) {
       result->DeprioritizeBadProxies(proxy_retry_info_);
       net_log.AddEvent(
-          NetLogEventType::PROXY_SERVICE_DEPRIORITIZED_BAD_PROXIES,
+          NetLogEventType::PROXY_RESOLUTION_SERVICE_DEPRIORITIZED_BAD_PROXIES,
           base::Bind(&NetLogFinishedResolvingProxyCallback, result));
     }
   } else {
     net_log.AddEventWithNetErrorCode(
-        NetLogEventType::PROXY_SERVICE_RESOLVED_PROXY_LIST, result_code);
+        NetLogEventType::PROXY_RESOLUTION_SERVICE_RESOLVED_PROXY_LIST,
+        result_code);
 
     bool reset_config = result_code == ERR_PAC_SCRIPT_TERMINATED;
     if (!config_->pac_mandatory()) {
@@ -1372,7 +1374,7 @@
     }
   }
 
-  net_log.EndEvent(NetLogEventType::PROXY_SERVICE);
+  net_log.EndEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE);
   return result_code;
 }
 
diff --git a/net/proxy_resolution/proxy_service_unittest.cc b/net/proxy_resolution/proxy_service_unittest.cc
index 5efa61aa..53bc1b7 100644
--- a/net/proxy_resolution/proxy_service_unittest.cc
+++ b/net/proxy_resolution/proxy_service_unittest.cc
@@ -358,12 +358,13 @@
   log.GetEntries(&entries);
 
   EXPECT_EQ(3u, entries.size());
-  EXPECT_TRUE(
-      LogContainsBeginEvent(entries, 0, NetLogEventType::PROXY_SERVICE));
+  EXPECT_TRUE(LogContainsBeginEvent(entries, 0,
+                                    NetLogEventType::PROXY_RESOLUTION_SERVICE));
   EXPECT_TRUE(LogContainsEvent(
-      entries, 1, NetLogEventType::PROXY_SERVICE_RESOLVED_PROXY_LIST,
+      entries, 1, NetLogEventType::PROXY_RESOLUTION_SERVICE_RESOLVED_PROXY_LIST,
       NetLogEventPhase::NONE));
-  EXPECT_TRUE(LogContainsEndEvent(entries, 2, NetLogEventType::PROXY_SERVICE));
+  EXPECT_TRUE(LogContainsEndEvent(entries, 2,
+                                  NetLogEventType::PROXY_RESOLUTION_SERVICE));
 }
 
 TEST_F(ProxyServiceTest, OnResolveProxyCallbackAddProxy) {
@@ -521,13 +522,16 @@
   log.GetEntries(&entries);
 
   EXPECT_EQ(5u, entries.size());
-  EXPECT_TRUE(
-      LogContainsBeginEvent(entries, 0, NetLogEventType::PROXY_SERVICE));
+  EXPECT_TRUE(LogContainsBeginEvent(entries, 0,
+                                    NetLogEventType::PROXY_RESOLUTION_SERVICE));
   EXPECT_TRUE(LogContainsBeginEvent(
-      entries, 1, NetLogEventType::PROXY_SERVICE_WAITING_FOR_INIT_PAC));
+      entries, 1,
+      NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC));
   EXPECT_TRUE(LogContainsEndEvent(
-      entries, 2, NetLogEventType::PROXY_SERVICE_WAITING_FOR_INIT_PAC));
-  EXPECT_TRUE(LogContainsEndEvent(entries, 4, NetLogEventType::PROXY_SERVICE));
+      entries, 2,
+      NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC));
+  EXPECT_TRUE(LogContainsEndEvent(entries, 4,
+                                  NetLogEventType::PROXY_RESOLUTION_SERVICE));
 }
 
 // Test that the proxy resolver does not see the URL's username/password
@@ -2072,15 +2076,17 @@
 
   // Check the NetLog for request 1 (which was cancelled) got filled properly.
   EXPECT_EQ(4u, entries1.size());
-  EXPECT_TRUE(
-      LogContainsBeginEvent(entries1, 0, NetLogEventType::PROXY_SERVICE));
+  EXPECT_TRUE(LogContainsBeginEvent(entries1, 0,
+                                    NetLogEventType::PROXY_RESOLUTION_SERVICE));
   EXPECT_TRUE(LogContainsBeginEvent(
-      entries1, 1, NetLogEventType::PROXY_SERVICE_WAITING_FOR_INIT_PAC));
-  // Note that PROXY_SERVICE_WAITING_FOR_INIT_PAC is never completed before
-  // the cancellation occured.
+      entries1, 1,
+      NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC));
+  // Note that PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC is never completed
+  // before the cancellation occured.
   EXPECT_TRUE(LogContainsEvent(entries1, 2, NetLogEventType::CANCELLED,
                                NetLogEventPhase::NONE));
-  EXPECT_TRUE(LogContainsEndEvent(entries1, 3, NetLogEventType::PROXY_SERVICE));
+  EXPECT_TRUE(LogContainsEndEvent(entries1, 3,
+                                  NetLogEventType::PROXY_RESOLUTION_SERVICE));
 }
 
 // Test that if auto-detect fails, we fall-back to the custom pac.
diff --git a/net/quic/chromium/quic_http_stream.cc b/net/quic/chromium/quic_http_stream.cc
index 68f82ed..307604da 100644
--- a/net/quic/chromium/quic_http_stream.cc
+++ b/net/quic/chromium/quic_http_stream.cc
@@ -468,6 +468,9 @@
 int QuicHttpStream::DoLoop(int rv) {
   CHECK(!in_loop_);
   base::AutoReset<bool> auto_reset_in_loop(&in_loop_, true);
+  std::unique_ptr<QuicConnection::ScopedPacketFlusher> packet_flusher =
+      quic_session()->CreatePacketBundler(
+          QuicConnection::AckBundling::SEND_ACK_IF_QUEUED);
   do {
     State state = next_state_;
     next_state_ = STATE_NONE;
diff --git a/net/quic/chromium/quic_http_stream_test.cc b/net/quic/chromium/quic_http_stream_test.cc
index b0b026be..b1a0f683 100644
--- a/net/quic/chromium/quic_http_stream_test.cc
+++ b/net/quic/chromium/quic_http_stream_test.cc
@@ -420,6 +420,45 @@
         spdy_headers_frame_length, offset);
   }
 
+  std::unique_ptr<QuicReceivedPacket>
+  ConstructRequestHeadersAndDataFramesPacket(
+      QuicPacketNumber packet_number,
+      QuicStreamId stream_id,
+      bool should_include_version,
+      bool fin,
+      RequestPriority request_priority,
+      QuicStreamId parent_stream_id,
+      QuicStreamOffset* offset,
+      size_t* spdy_headers_frame_length,
+      const std::vector<std::string>& data_writes) {
+    SpdyPriority priority =
+        ConvertRequestPriorityToQuicPriority(request_priority);
+    return client_maker_.MakeRequestHeadersAndMultipleDataFramesPacket(
+        packet_number, stream_id, should_include_version, fin, priority,
+        std::move(request_headers_), parent_stream_id, offset,
+        spdy_headers_frame_length, data_writes);
+  }
+
+  std::unique_ptr<QuicReceivedPacket> ConstructRequestAndRstPacket(
+      QuicPacketNumber packet_number,
+      QuicStreamId stream_id,
+      bool should_include_version,
+      bool fin,
+      RequestPriority request_priority,
+      QuicStreamId parent_stream_id,
+      size_t* spdy_headers_frame_length,
+      QuicStreamOffset* header_stream_offset,
+      QuicRstStreamErrorCode error_code,
+      size_t bytes_written) {
+    SpdyPriority priority =
+        ConvertRequestPriorityToQuicPriority(request_priority);
+    return client_maker_.MakeRequestHeadersAndRstPacket(
+        packet_number, stream_id, should_include_version, fin, priority,
+        std::move(request_headers_), parent_stream_id,
+        spdy_headers_frame_length, header_stream_offset, error_code,
+        bytes_written);
+  }
+
   std::unique_ptr<QuicReceivedPacket> ConstructRequestHeadersPacket(
       QuicPacketNumber packet_number,
       bool fin,
@@ -1141,12 +1180,13 @@
   size_t spdy_request_headers_frame_length;
   QuicStreamOffset header_stream_offset = 0;
   AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
-  AddWrite(InnerConstructRequestHeadersPacket(
-      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
-      DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
-      &header_stream_offset));
-  AddWrite(ConstructClientDataPacket(3, kIncludeVersion, kFin, 0, kUploadData));
-  AddWrite(ConstructClientAckPacket(4, 3, 1, 1));
+
+  AddWrite(ConstructRequestHeadersAndDataFramesPacket(
+      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, kFin,
+      DEFAULT_PRIORITY, 0, &header_stream_offset,
+      &spdy_request_headers_frame_length, {kUploadData}));
+
+  AddWrite(ConstructClientAckPacket(3, 3, 1, 1));
 
   Initialize();
 
@@ -1212,12 +1252,11 @@
   size_t spdy_request_headers_frame_length;
   QuicStreamOffset header_stream_offset = 0;
   AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
-  AddWrite(InnerConstructRequestHeadersPacket(
-      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
-      DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
-      &header_stream_offset));
-  AddWrite(ConstructClientDataPacket(3, kIncludeVersion, kFin, 0, kUploadData));
-  AddWrite(ConstructClientAckPacket(4, 3, 1, 1));
+  AddWrite(ConstructRequestHeadersAndDataFramesPacket(
+      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, kFin,
+      DEFAULT_PRIORITY, 0, &header_stream_offset,
+      &spdy_request_headers_frame_length, {kUploadData}));
+  AddWrite(ConstructClientAckPacket(3, 3, 1, 1));
 
   Initialize();
 
@@ -1286,15 +1325,13 @@
   size_t spdy_request_headers_frame_length;
   QuicStreamOffset header_stream_offset = 0;
   AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
-  AddWrite(InnerConstructRequestHeadersPacket(
+  AddWrite(ConstructRequestHeadersAndDataFramesPacket(
       2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
-      DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
-      &header_stream_offset));
-  AddWrite(
-      ConstructClientDataPacket(3, kIncludeVersion, !kFin, 0, kUploadData));
-  AddWrite(ConstructClientDataPacket(4, kIncludeVersion, kFin, chunk_size,
+      DEFAULT_PRIORITY, 0, &header_stream_offset,
+      &spdy_request_headers_frame_length, {kUploadData}));
+  AddWrite(ConstructClientDataPacket(3, kIncludeVersion, kFin, chunk_size,
                                      kUploadData));
-  AddWrite(ConstructClientAckPacket(5, 3, 1, 1));
+  AddWrite(ConstructClientAckPacket(4, 3, 1, 1));
   Initialize();
 
   upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
@@ -1361,14 +1398,12 @@
   size_t spdy_request_headers_frame_length;
   QuicStreamOffset header_stream_offset = 0;
   AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
-  AddWrite(InnerConstructRequestHeadersPacket(
+  AddWrite(ConstructRequestHeadersAndDataFramesPacket(
       2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
-      DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
-      &header_stream_offset));
-  AddWrite(
-      ConstructClientDataPacket(3, kIncludeVersion, !kFin, 0, kUploadData));
-  AddWrite(ConstructClientDataPacket(4, kIncludeVersion, kFin, chunk_size, ""));
-  AddWrite(ConstructClientAckPacket(5, 3, 1, 1));
+      DEFAULT_PRIORITY, 0, &header_stream_offset,
+      &spdy_request_headers_frame_length, {kUploadData}));
+  AddWrite(ConstructClientDataPacket(3, kIncludeVersion, kFin, chunk_size, ""));
+  AddWrite(ConstructClientAckPacket(4, 3, 1, 1));
   Initialize();
 
   upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
@@ -1589,12 +1624,10 @@
   size_t spdy_request_headers_frame_length;
   QuicStreamOffset header_stream_offset = 0;
   AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
-  AddWrite(InnerConstructRequestHeadersPacket(
+  AddWrite(ConstructRequestHeadersAndDataFramesPacket(
       2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
-      DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
-      &header_stream_offset));
-  AddWrite(
-      ConstructClientDataPacket(3, kIncludeVersion, !kFin, 0, kUploadData));
+      DEFAULT_PRIORITY, 0, &header_stream_offset,
+      &spdy_request_headers_frame_length, {kUploadData}));
   // Second data write will result in a synchronous failure which will close
   // the session.
   AddWrite(SYNCHRONOUS, ERR_FAILED);
@@ -1618,11 +1651,16 @@
   QuicHttpStream* stream = stream_.get();
   DeleteStreamCallback delete_stream_callback(std::move(stream_));
   // SendRequest() completes asynchronously after the final chunk is added.
+  // Error does not surface yet since packet write is triggered by a packet
+  // flusher that tries to bundle request body writes.
   ASSERT_EQ(ERR_IO_PENDING,
             stream->SendRequest(headers_, &response_, callback_.callback()));
   chunked_upload_stream->AppendData(kUploadData, chunk_size, true);
   int rv = callback_.WaitForResult();
-  EXPECT_EQ(ERR_QUIC_PROTOCOL_ERROR, rv);
+  EXPECT_EQ(OK, rv);
+  // Error will be surfaced once an attempt to read the response occurs.
+  ASSERT_EQ(ERR_QUIC_PROTOCOL_ERROR,
+            stream->ReadResponseHeaders(callback_.callback()));
 }
 
 TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendHeadersComplete) {
@@ -1633,6 +1671,8 @@
   Initialize();
 
   upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
+  auto* chunked_upload_stream =
+      static_cast<ChunkedUploadDataStream*>(upload_data_stream_.get());
 
   request_.method = "POST";
   request_.url = GURL("https://www.example.org/");
@@ -1643,9 +1683,49 @@
   ASSERT_EQ(OK,
             stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                       net_log_.bound(), callback_.callback()));
-  ASSERT_EQ(ERR_QUIC_PROTOCOL_ERROR,
+  ASSERT_EQ(ERR_IO_PENDING,
             stream_->SendRequest(headers_, &response_, callback_.callback()));
 
+  // Error will be surfaced once |upload_data_stream| triggers the next write.
+  size_t chunk_size = strlen(kUploadData);
+  chunked_upload_stream->AppendData(kUploadData, chunk_size, true);
+  ASSERT_EQ(ERR_QUIC_PROTOCOL_ERROR, callback_.WaitForResult());
+
+  EXPECT_LE(0, stream_->GetTotalSentBytes());
+  EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
+}
+
+TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendHeadersCompleteReadResponse) {
+  SetRequest("POST", "/", DEFAULT_PRIORITY);
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
+  AddWrite(SYNCHRONOUS, ERR_FAILED);
+  Initialize();
+
+  upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
+  auto* chunked_upload_stream =
+      static_cast<ChunkedUploadDataStream*>(upload_data_stream_.get());
+
+  request_.method = "POST";
+  request_.url = GURL("https://www.example.org/");
+  request_.upload_data_stream = upload_data_stream_.get();
+
+  size_t chunk_size = strlen(kUploadData);
+  chunked_upload_stream->AppendData(kUploadData, chunk_size, true);
+
+  ASSERT_EQ(OK, request_.upload_data_stream->Init(
+                    TestCompletionCallback().callback(), NetLogWithSource()));
+
+  ASSERT_EQ(OK,
+            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
+                                      net_log_.bound(), callback_.callback()));
+  ASSERT_EQ(OK,
+            stream_->SendRequest(headers_, &response_, callback_.callback()));
+
+  // Error will be surfaced once an attempt to read the response occurs.
+  ASSERT_EQ(ERR_QUIC_PROTOCOL_ERROR,
+            stream_->ReadResponseHeaders(callback_.callback()));
+
   EXPECT_LE(0, stream_->GetTotalSentBytes());
   EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
 }
@@ -1665,8 +1745,6 @@
   upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
   auto* chunked_upload_stream =
       static_cast<ChunkedUploadDataStream*>(upload_data_stream_.get());
-  size_t chunk_size = strlen(kUploadData);
-  chunked_upload_stream->AppendData(kUploadData, chunk_size, false);
 
   request_.method = "POST";
   request_.url = GURL("https://www.example.org/");
@@ -1677,8 +1755,65 @@
   ASSERT_EQ(OK,
             stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                       net_log_.bound(), callback_.callback()));
-  ASSERT_EQ(ERR_QUIC_PROTOCOL_ERROR,
+  ASSERT_EQ(ERR_IO_PENDING,
             stream_->SendRequest(headers_, &response_, callback_.callback()));
+
+  size_t chunk_size = strlen(kUploadData);
+  chunked_upload_stream->AppendData(kUploadData, chunk_size, true);
+  // Error does not surface yet since packet write is triggered by a packet
+  // flusher that tries to bundle request body writes.
+  ASSERT_EQ(OK, callback_.WaitForResult());
+  // Error will be surfaced once an attempt to read the response occurs.
+  ASSERT_EQ(ERR_QUIC_PROTOCOL_ERROR,
+            stream_->ReadResponseHeaders(callback_.callback()));
+
+  EXPECT_LE(0, stream_->GetTotalSentBytes());
+  EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
+}
+
+TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendBundledBodyComplete) {
+  SetRequest("POST", "/", DEFAULT_PRIORITY);
+  size_t spdy_request_headers_frame_length;
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
+  AddWrite(ConstructRequestHeadersAndDataFramesPacket(
+      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
+      DEFAULT_PRIORITY, 0, &header_stream_offset,
+      &spdy_request_headers_frame_length, {kUploadData}));
+  AddWrite(SYNCHRONOUS, ERR_FAILED);
+  Initialize();
+
+  upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
+  auto* chunked_upload_stream =
+      static_cast<ChunkedUploadDataStream*>(upload_data_stream_.get());
+
+  request_.method = "POST";
+  request_.url = GURL("https://www.example.org/");
+  request_.upload_data_stream = upload_data_stream_.get();
+
+  size_t chunk_size = strlen(kUploadData);
+  chunked_upload_stream->AppendData(kUploadData, chunk_size, false);
+
+  ASSERT_EQ(OK, request_.upload_data_stream->Init(
+                    TestCompletionCallback().callback(), NetLogWithSource()));
+
+  ASSERT_EQ(OK,
+            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
+                                      net_log_.bound(), callback_.callback()));
+  ASSERT_EQ(ERR_IO_PENDING,
+            stream_->SendRequest(headers_, &response_, callback_.callback()));
+
+  chunked_upload_stream->AppendData(kUploadData, chunk_size, true);
+
+  // Error does not surface yet since packet write is triggered by a packet
+  // flusher that tries to bundle request body writes.
+  ASSERT_EQ(OK, callback_.WaitForResult());
+  // Error will be surfaced once an attempt to read the response occurs.
+  ASSERT_EQ(ERR_QUIC_PROTOCOL_ERROR,
+            stream_->ReadResponseHeaders(callback_.callback()));
+
+  EXPECT_LE(0, stream_->GetTotalSentBytes());
+  EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
 }
 
 TEST_P(QuicHttpStreamTest, ServerPushGetRequest) {
@@ -2164,11 +2299,10 @@
   size_t spdy_request_headers_frame_length;
   QuicStreamOffset header_stream_offset = 0;
   AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
-  AddWrite(InnerConstructRequestHeadersPacket(
+  AddWrite(ConstructRequestAndRstPacket(
       2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
-      DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
-      &header_stream_offset));
-  AddWrite(ConstructClientRstStreamErrorPacket(3, kIncludeVersion));
+      DEFAULT_PRIORITY, 0, &spdy_request_headers_frame_length,
+      &header_stream_offset, QUIC_ERROR_PROCESSING_STREAM, 0));
 
   Initialize();
 
diff --git a/net/quic/chromium/quic_network_transaction_unittest.cc b/net/quic/chromium/quic_network_transaction_unittest.cc
index ca11773b..9bcf030e 100644
--- a/net/quic/chromium/quic_network_transaction_unittest.cc
+++ b/net/quic/chromium/quic_network_transaction_unittest.cc
@@ -423,20 +423,19 @@
         ConvertRequestPriorityToQuicPriority(request_priority), offset);
   }
 
-  std::unique_ptr<QuicEncryptedPacket> ConstructClientAckAndPriorityPacket(
+  std::unique_ptr<QuicEncryptedPacket>
+  ConstructClientAckAndPriorityFramesPacket(
       QuicPacketNumber packet_number,
       bool should_include_version,
       QuicPacketNumber largest_received,
       QuicPacketNumber smallest_received,
       QuicPacketNumber least_unacked,
-      QuicStreamId stream_id,
-      QuicStreamId parent_stream_id,
-      RequestPriority request_priority,
+      const std::vector<QuicTestPacketMaker::Http2StreamDependency>&
+          priority_frames,
       QuicStreamOffset* offset) {
-    return client_maker_.MakeAckAndPriorityPacket(
+    return client_maker_.MakeAckAndMultiplePriorityFramesPacket(
         packet_number, should_include_version, largest_received,
-        smallest_received, least_unacked, stream_id, parent_stream_id,
-        ConvertRequestPriorityToQuicPriority(request_priority), offset);
+        smallest_received, least_unacked, priority_frames, offset);
   }
 
   // Uses default QuicTestPacketMaker.
@@ -568,6 +567,26 @@
         std::move(headers), parent_stream_id, offset);
   }
 
+  std::unique_ptr<QuicReceivedPacket>
+  ConstructClientRequestHeadersAndDataFramesPacket(
+      QuicPacketNumber packet_number,
+      QuicStreamId stream_id,
+      bool should_include_version,
+      bool fin,
+      RequestPriority request_priority,
+      SpdyHeaderBlock headers,
+      QuicStreamId parent_stream_id,
+      QuicStreamOffset* offset,
+      size_t* spdy_headers_frame_length,
+      const std::vector<std::string>& data_writes) {
+    SpdyPriority priority =
+        ConvertRequestPriorityToQuicPriority(request_priority);
+    return client_maker_.MakeRequestHeadersAndMultipleDataFramesPacket(
+        packet_number, stream_id, should_include_version, fin, priority,
+        std::move(headers), parent_stream_id, offset, spdy_headers_frame_length,
+        data_writes);
+  }
+
   std::unique_ptr<QuicEncryptedPacket> ConstructClientMultipleDataFramesPacket(
       QuicPacketNumber packet_number,
       QuicStreamId stream_id,
@@ -5278,14 +5297,10 @@
 
   QuicStreamOffset offset = 0;
   mock_quic_data.AddWrite(ConstructInitialSettingsPacket(1, &offset));
-  mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      2, GetNthClientInitiatedStreamId(0), true, false,
-      GetRequestHeaders("POST", "https", "/"), &offset));
 
-  std::unique_ptr<QuicEncryptedPacket> packet;
-  packet = ConstructClientDataPacket(3, GetNthClientInitiatedStreamId(0), true,
-                                     true, 0, "1");
-  mock_quic_data.AddWrite(std::move(packet));
+  mock_quic_data.AddWrite(ConstructClientRequestHeadersAndDataFramesPacket(
+      2, GetNthClientInitiatedStreamId(0), true, true, DEFAULT_PRIORITY,
+      GetRequestHeaders("POST", "https", "/"), 0, &offset, nullptr, {"1"}));
 
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, GetNthClientInitiatedStreamId(0), false, false,
@@ -5294,7 +5309,7 @@
   mock_quic_data.AddRead(ConstructServerDataPacket(
       2, GetNthClientInitiatedStreamId(0), false, true, 0, "hello!"));
 
-  mock_quic_data.AddWrite(ConstructClientAckPacket(4, 2, 1, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1, 1));
 
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
@@ -6068,13 +6083,11 @@
       client_packet_number++, GetNthServerInitiatedStreamId(0),
       QUIC_STREAM_CANCELLED, 5, 5, 1));
   const char kBody[] = "1";
-  mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      client_packet_number++, GetNthClientInitiatedStreamId(1), false, false,
-      GetRequestHeaders("GET", "https", "/pushed.jpg"),
-      GetNthServerInitiatedStreamId(0), &header_stream_offset));
-  mock_quic_data.AddWrite(ConstructClientMultipleDataFramesPacket(
+  mock_quic_data.AddWrite(ConstructClientRequestHeadersAndDataFramesPacket(
       client_packet_number++, GetNthClientInitiatedStreamId(1), false, true,
-      {kBody}, 0));
+      DEFAULT_PRIORITY, GetRequestHeaders("GET", "https", "/pushed.jpg"),
+      GetNthServerInitiatedStreamId(0), &header_stream_offset, nullptr,
+      {kBody}));
 
   // We see the same response as for the earlier pushed and cancelled
   // stream.
@@ -7004,8 +7017,10 @@
       4, client_stream_0, push_stream_0, false,
       GetRequestHeaders("GET", "https", "/pushed_0.jpg"), &server_header_offset,
       &server_maker_));
-  mock_quic_data.AddWrite(ConstructClientAckAndPriorityPacket(
-      6, false, 4, 3, 1, push_stream_0, client_stream_2, DEFAULT_PRIORITY,
+  mock_quic_data.AddWrite(ConstructClientAckAndPriorityFramesPacket(
+      6, false, 4, 3, 1,
+      {{push_stream_0, client_stream_2,
+        ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY)}},
       &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerPushPromisePacket(
       5, client_stream_0, push_stream_1, false,
@@ -7027,29 +7042,30 @@
   // Request for "pushed_0.jpg" matches |push_stream_0|. |push_stream_0|'s
   // priority updates to match the request's priority. Client sends PRIORITY
   // frames to inform server of new HTTP/2 stream dependencies.
-  mock_quic_data.AddWrite(ConstructClientAckAndPriorityPacket(
-      9, false, 7, 7, 1, push_stream_1, client_stream_2, DEFAULT_PRIORITY,
+  mock_quic_data.AddWrite(ConstructClientAckAndPriorityFramesPacket(
+      9, false, 7, 7, 1,
+      {{push_stream_1, client_stream_2,
+        ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY)},
+       {push_stream_0, client_stream_0,
+        ConvertRequestPriorityToQuicPriority(HIGHEST)}},
       &header_stream_offset));
-  mock_quic_data.AddWrite(
-      ConstructClientPriorityPacket(10, false, push_stream_0, client_stream_0,
-                                    HIGHEST, &header_stream_offset));
 
   // Server sends data for the three requests and the two push promises.
   mock_quic_data.AddRead(ConstructServerDataPacket(8, client_stream_0, false,
                                                    true, 0, "hello 0!"));
   mock_quic_data.AddSynchronousRead(ConstructServerDataPacket(
       9, client_stream_1, false, true, 0, "hello 1!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(11, 9, 8, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(10, 9, 8, 1));
   mock_quic_data.AddRead(ConstructServerDataPacket(10, client_stream_2, false,
                                                    true, 0, "hello 2!"));
   mock_quic_data.AddSynchronousRead(ConstructServerDataPacket(
       11, push_stream_0, false, true, 0, "and hello 0!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(12, 11, 10, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(11, 11, 10, 1));
   mock_quic_data.AddRead(ConstructServerDataPacket(12, push_stream_1, false,
                                                    true, 0, "and hello 1!"));
 
   mock_quic_data.AddWrite(ConstructClientAckAndRstPacket(
-      13, push_stream_0, QUIC_RST_ACKNOWLEDGEMENT, 12, 12, 1));
+      12, push_stream_0, QUIC_RST_ACKNOWLEDGEMENT, 12, 12, 1));
 
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
diff --git a/net/quic/chromium/quic_test_packet_maker.cc b/net/quic/chromium/quic_test_packet_maker.cc
index b50f826..70d83bb 100644
--- a/net/quic/chromium/quic_test_packet_maker.cc
+++ b/net/quic/chromium/quic_test_packet_maker.cc
@@ -452,6 +452,7 @@
   QuicStreamFrame frame(kHeadersStreamId, false, header_offset,
                         QuicStringPiece(spdy_frame.data(), spdy_frame.size()));
   frames.push_back(QuicFrame(&frame));
+  DVLOG(1) << "Adding frame: " << frames.back();
   if (header_stream_offset != nullptr) {
     *header_stream_offset += spdy_frame.size();
   }
@@ -543,6 +544,45 @@
   }
 }
 
+std::unique_ptr<QuicReceivedPacket>
+QuicTestPacketMaker::MakeRequestHeadersAndRstPacket(
+    QuicPacketNumber packet_number,
+    QuicStreamId stream_id,
+    bool should_include_version,
+    bool fin,
+    SpdyPriority priority,
+    SpdyHeaderBlock headers,
+    QuicStreamId parent_stream_id,
+    size_t* spdy_headers_frame_length,
+    QuicStreamOffset* header_stream_offset,
+    QuicRstStreamErrorCode error_code,
+    size_t bytes_written) {
+  SpdySerializedFrame spdy_frame = MakeSpdyHeadersFrame(
+      stream_id, fin, priority, std::move(headers), parent_stream_id);
+  if (spdy_headers_frame_length) {
+    *spdy_headers_frame_length = spdy_frame.size();
+  }
+  QuicStreamOffset header_offset = 0;
+  if (header_stream_offset != nullptr) {
+    header_offset = *header_stream_offset;
+    *header_stream_offset += spdy_frame.size();
+  }
+  QuicStreamFrame headers_frame(
+      kHeadersStreamId, false, header_offset,
+      QuicStringPiece(spdy_frame.data(), spdy_frame.size()));
+
+  QuicRstStreamFrame rst_frame(1, stream_id, error_code, bytes_written);
+
+  QuicFrames frames;
+  frames.push_back(QuicFrame(&headers_frame));
+  DVLOG(1) << "Adding frame: " << frames.back();
+  frames.push_back(QuicFrame(&rst_frame));
+  DVLOG(1) << "Adding frame: " << frames.back();
+
+  InitializeHeader(packet_number, should_include_version);
+  return MakeMultipleFramesPacket(header_, frames);
+}
+
 SpdySerializedFrame QuicTestPacketMaker::MakeSpdyHeadersFrame(
     QuicStreamId stream_id,
     bool fin,
@@ -831,15 +871,13 @@
 }
 
 std::unique_ptr<QuicReceivedPacket>
-QuicTestPacketMaker::MakeAckAndPriorityPacket(
+QuicTestPacketMaker::MakeAckAndMultiplePriorityFramesPacket(
     QuicPacketNumber packet_number,
     bool should_include_version,
     QuicPacketNumber largest_received,
     QuicPacketNumber smallest_received,
     QuicPacketNumber least_unacked,
-    QuicStreamId stream_id,
-    QuicStreamId parent_stream_id,
-    SpdyPriority spdy_priority,
+    const std::vector<Http2StreamDependency>& priority_frames,
     QuicStreamOffset* offset) {
   QuicAckFrame ack(MakeAckFrame(largest_received));
   ack.ack_delay_time = QuicTime::Delta::Zero();
@@ -858,22 +896,33 @@
   frames.push_back(QuicFrame(&stop_waiting));
   DVLOG(1) << "Adding frame: " << frames[1];
 
-  bool exclusive = client_headers_include_h2_stream_dependency_;
-  SpdyPriorityIR priority_frame(stream_id, parent_stream_id,
-                                Spdy3PriorityToHttp2Weight(spdy_priority),
-                                exclusive);
-  SpdySerializedFrame spdy_frame =
-      spdy_request_framer_.SerializeFrame(priority_frame);
+  const bool exclusive = client_headers_include_h2_stream_dependency_;
   QuicStreamOffset header_offset = 0;
-  if (offset != nullptr) {
-    header_offset = *offset;
-    *offset += spdy_frame.size();
+  if (offset == nullptr) {
+    offset = &header_offset;
   }
-  QuicStreamFrame priority(
-      kHeadersStreamId, false, header_offset,
-      QuicStringPiece(spdy_frame.data(), spdy_frame.size()));
-  frames.push_back(QuicFrame(&priority));
-  DVLOG(1) << "Adding frame: " << frames[2];
+  // Keep SpdySerializedFrames alive until MakeMultipleFramesPacket is done.
+  // Keep StreamFrames alive until MakeMultipleFramesPacket is done.
+  std::vector<std::unique_ptr<SpdySerializedFrame>> spdy_frames;
+  std::vector<std::unique_ptr<QuicStreamFrame>> stream_frames;
+  for (const Http2StreamDependency& info : priority_frames) {
+    SpdyPriorityIR priority_frame(
+        info.stream_id, info.parent_stream_id,
+        Spdy3PriorityToHttp2Weight(info.spdy_priority), exclusive);
+
+    spdy_frames.push_back(std::make_unique<SpdySerializedFrame>(
+        spdy_request_framer_.SerializeFrame(priority_frame)));
+
+    SpdySerializedFrame* spdy_frame = spdy_frames.back().get();
+    stream_frames.push_back(std::make_unique<QuicStreamFrame>(
+        kHeadersStreamId, false, *offset,
+        QuicStringPiece(spdy_frame->data(), spdy_frame->size())));
+    *offset += spdy_frame->size();
+
+    frames.push_back(QuicFrame(stream_frames.back().get()));
+    DVLOG(1) << "Adding frame: " << frames.back();
+    ;
+  }
 
   InitializeHeader(packet_number, should_include_version);
   return MakeMultipleFramesPacket(header_, frames);
diff --git a/net/quic/chromium/quic_test_packet_maker.h b/net/quic/chromium/quic_test_packet_maker.h
index 925596d..c5a4a2b 100644
--- a/net/quic/chromium/quic_test_packet_maker.h
+++ b/net/quic/chromium/quic_test_packet_maker.h
@@ -27,6 +27,12 @@
 
 class QuicTestPacketMaker {
  public:
+  struct Http2StreamDependency {
+    QuicStreamId stream_id;
+    QuicStreamId parent_stream_id;
+    SpdyPriority spdy_priority;
+  };
+
   // |client_headers_include_h2_stream_dependency| affects the output of
   // the MakeRequestHeaders...() methods. If its value is true, then request
   // headers are constructed with the exclusive flag set to true and the parent
@@ -194,6 +200,19 @@
       QuicStreamOffset* offset,
       std::string* stream_data);
 
+  std::unique_ptr<QuicReceivedPacket> MakeRequestHeadersAndRstPacket(
+      QuicPacketNumber packet_number,
+      QuicStreamId stream_id,
+      bool should_include_version,
+      bool fin,
+      SpdyPriority priority,
+      SpdyHeaderBlock headers,
+      QuicStreamId parent_stream_id,
+      size_t* spdy_headers_frame_length,
+      QuicStreamOffset* header_stream_offset,
+      QuicRstStreamErrorCode error_code,
+      size_t bytes_written);
+
   // Convenience method for calling MakeRequestHeadersPacket with nullptr for
   // |spdy_headers_frame_length|.
   std::unique_ptr<QuicReceivedPacket>
@@ -268,15 +287,13 @@
       SpdyPriority priority,
       QuicStreamOffset* offset);
 
-  std::unique_ptr<QuicReceivedPacket> MakeAckAndPriorityPacket(
+  std::unique_ptr<QuicReceivedPacket> MakeAckAndMultiplePriorityFramesPacket(
       QuicPacketNumber packet_number,
       bool should_include_version,
       QuicPacketNumber largest_received,
       QuicPacketNumber smallest_received,
       QuicPacketNumber least_unacked,
-      QuicStreamId stream_id,
-      QuicStreamId parent_stream_id,
-      SpdyPriority spdy_priority,
+      const std::vector<Http2StreamDependency>& priority_frames,
       QuicStreamOffset* offset);
 
   SpdyHeaderBlock GetRequestHeaders(const std::string& method,
diff --git a/services/network/resource_scheduler.cc b/services/network/resource_scheduler.cc
index dbc2d13a..1d465ac 100644
--- a/services/network/resource_scheduler.cc
+++ b/services/network/resource_scheduler.cc
@@ -173,8 +173,8 @@
 
 class ResourceScheduler::RequestQueue {
  public:
-  typedef std::multiset<ScheduledResourceRequestImpl*, ScheduledResourceSorter>
-      NetQueue;
+  using NetQueue =
+      std::multiset<ScheduledResourceRequestImpl*, ScheduledResourceSorter>;
 
   RequestQueue() : fifo_ordering_ids_(0) {}
   ~RequestQueue() {}
@@ -200,11 +200,11 @@
   }
 
   // Returns true if no requests are queued.
-  bool IsEmpty() const { return queue_.size() == 0; }
+  bool IsEmpty() const { return queue_.empty(); }
 
  private:
-  typedef std::map<ScheduledResourceRequestImpl*, NetQueue::iterator>
-      PointerMap;
+  using PointerMap =
+      std::map<ScheduledResourceRequestImpl*, NetQueue::iterator>;
 
   uint32_t MakeFifoOrderingId() {
     fifo_ordering_ids_ += 1;
@@ -1052,7 +1052,7 @@
     return std::move(request);
   }
 
-  Client* client = it->second;
+  Client* client = it->second.get();
   client->ScheduleRequest(*url_request, request.get());
   return std::move(request);
 }
@@ -1065,11 +1065,10 @@
   }
 
   ClientMap::iterator client_it = client_map_.find(request->client_id());
-  if (client_it == client_map_.end()) {
+  if (client_it == client_map_.end())
     return;
-  }
 
-  Client* client = client_it->second;
+  Client* client = client_it->second.get();
   client->RemoveRequest(request);
 }
 
@@ -1081,8 +1080,8 @@
   ClientId client_id = MakeClientId(child_id, route_id);
   DCHECK(!base::ContainsKey(client_map_, client_id));
 
-  Client* client = new Client(network_quality_estimator, this);
-  client_map_[client_id] = client;
+  client_map_[client_id] =
+      std::make_unique<Client>(network_quality_estimator, this);
 }
 
 void ResourceScheduler::OnClientDeleted(int child_id, int route_id) {
@@ -1091,7 +1090,7 @@
   ClientMap::iterator it = client_map_.find(client_id);
   DCHECK(it != client_map_.end());
 
-  Client* client = it->second;
+  Client* client = it->second.get();
   // ResourceDispatcherHost cancels all requests except for cross-renderer
   // navigations, async revalidations and detachable requests after
   // OnClientDeleted() returns.
@@ -1101,7 +1100,6 @@
     unowned_requests_.insert(*request_it);
   }
 
-  delete client;
   client_map_.erase(it);
 }
 
@@ -1123,7 +1121,7 @@
     return;
   }
 
-  Client* client = it->second;
+  Client* client = it->second.get();
   client->DeprecatedOnNavigate();
 }
 
@@ -1137,7 +1135,7 @@
     return;
   }
 
-  Client* client = it->second;
+  Client* client = it->second.get();
   client->DeprecatedOnWillInsertBody();
 }
 
@@ -1147,11 +1145,10 @@
   ClientId client_id = MakeClientId(child_id, route_id);
 
   ClientMap::iterator client_it = client_map_.find(client_id);
-  if (client_it == client_map_.end()) {
+  if (client_it == client_map_.end())
     return;
-  }
 
-  Client* client = client_it->second;
+  Client* client = client_it->second.get();
   client->OnReceivedSpdyProxiedHttpResponse();
 }
 
@@ -1167,10 +1164,9 @@
                                                         int route_id) {
   ClientId client_id = MakeClientId(child_id, route_id);
   ClientMap::iterator client_it = client_map_.find(client_id);
-  if (client_it == client_map_.end()) {
+  if (client_it == client_map_.end())
     return nullptr;
-  }
-  return client_it->second;
+  return client_it->second.get();
 }
 
 void ResourceScheduler::ReprioritizeRequest(net::URLRequest* request,
@@ -1208,7 +1204,7 @@
     return;
   }
 
-  Client* client = client_it->second;
+  Client* client = client_it->second.get();
   client->ReprioritizeRequest(scheduled_resource_request, old_priority_params,
                               new_priority_params);
 }
diff --git a/services/network/resource_scheduler.h b/services/network/resource_scheduler.h
index af91cba..e198b45 100644
--- a/services/network/resource_scheduler.h
+++ b/services/network/resource_scheduler.h
@@ -92,7 +92,7 @@
     // of non-delayable requests in-flight.
     double non_delayable_weight;
   };
-  typedef std::vector<ParamsForNetworkQuality> ParamsForNetworkQualityContainer;
+  using ParamsForNetworkQualityContainer = std::vector<ParamsForNetworkQuality>;
 
   explicit ResourceScheduler(bool enabled);
   ~ResourceScheduler();
@@ -250,9 +250,9 @@
         params_for_network_quality_container_;
   };
 
-  typedef int64_t ClientId;
-  typedef std::map<ClientId, Client*> ClientMap;
-  typedef std::set<ScheduledResourceRequestImpl*> RequestSet;
+  using ClientId = int64_t;
+  using ClientMap = std::map<ClientId, std::unique_ptr<Client>>;
+  using RequestSet = std::set<ScheduledResourceRequestImpl*>;
 
   // Called when a ScheduledResourceRequest is destroyed.
   void RemoveRequest(ScheduledResourceRequestImpl* request);
diff --git a/services/ui/public/interfaces/BUILD.gn b/services/ui/public/interfaces/BUILD.gn
index 09bc30a..eac70afb 100644
--- a/services/ui/public/interfaces/BUILD.gn
+++ b/services/ui/public/interfaces/BUILD.gn
@@ -15,14 +15,21 @@
   }
 
   sources = [
-    # TODO: if adding a new mojom add it to interfaces_tmp below.
+    "accessibility_manager.mojom",
+    "clipboard.mojom",
+    "display_manager.mojom",
+    "event_matcher.mojom",
+    "gpu.mojom",
+    "mus_constants.mojom",
     "remote_event_dispatcher.mojom",
     "user_activity_monitor.mojom",
     "video_detector.mojom",
     "window_manager.mojom",
+    "window_manager_constants.mojom",
     "window_manager_window_tree_factory.mojom",
     "window_server_test.mojom",
     "window_tree.mojom",
+    "window_tree_constants.mojom",
     "window_tree_host.mojom",
     "window_tree_host_factory.mojom",
   ]
@@ -34,7 +41,6 @@
 
   public_deps = [
     ":constants",
-    ":interfaces_tmp",
     "//gpu/ipc/common:interfaces",
     "//media/mojo/interfaces",
     "//mojo/common:common_custom_types",
@@ -55,49 +61,6 @@
     sources += [ "arc.mojom" ]
     public_deps += [ "//components/arc/common:media" ]
   }
-
-  # TODO(crbug.com/714018): Convert the implementation to use OnceCallback.
-  use_once_callback = false
-}
-
-# TODO(sky): temporary while migrating to once_callback. Eventually this will
-# become "interfaces".
-mojom("interfaces_tmp") {
-  visibility = [ ":*" ]
-
-  sources = [
-    "accessibility_manager.mojom",
-    "clipboard.mojom",
-    "display_manager.mojom",
-    "event_matcher.mojom",
-    "gpu.mojom",
-    "mus_constants.mojom",
-    "window_manager_constants.mojom",
-    "window_tree_constants.mojom",
-  ]
-
-  import_dirs = [
-    get_path_info("../../../..", "abspath"),
-    "//mojo/services",
-  ]
-
-  public_deps = [
-    ":constants",
-    "//gpu/ipc/common:interfaces",
-    "//media/mojo/interfaces",
-    "//mojo/common:common_custom_types",
-    "//services/ui/public/interfaces/cursor",
-    "//services/ui/public/interfaces/display",
-    "//services/ui/public/interfaces/ime",
-    "//services/viz/public/interfaces",
-    "//skia/public/interfaces",
-    "//ui/base/mojo:mojo_bindings",
-    "//ui/display/mojo:interfaces",
-    "//ui/events/mojo:interfaces",
-    "//ui/gfx/geometry/mojo",
-    "//ui/gfx/mojo",
-    "//ui/platform_window/mojo:interfaces",
-  ]
 }
 
 mojom("constants") {
diff --git a/services/ui/ws/remote_event_dispatcher.cc b/services/ui/ws/remote_event_dispatcher.cc
index c3f8641..d5842585 100644
--- a/services/ui/ws/remote_event_dispatcher.cc
+++ b/services/ui/ws/remote_event_dispatcher.cc
@@ -18,18 +18,18 @@
 
 void RemoteEventDispatcherImpl::DispatchEvent(int64_t display_id,
                                               std::unique_ptr<ui::Event> event,
-                                              const DispatchEventCallback& cb) {
+                                              DispatchEventCallback cb) {
   DisplayManager* manager = window_server_->display_manager();
   if (!manager) {
     DVLOG(1) << "No display manager in DispatchEvent.";
-    cb.Run(false);
+    std::move(cb).Run(false);
     return;
   }
 
   Display* display = manager->GetDisplayById(display_id);
   if (!display) {
     DVLOG(1) << "Invalid display_id in DispatchEvent.";
-    cb.Run(false);
+    std::move(cb).Run(false);
     return;
   }
 
@@ -37,7 +37,7 @@
     LocatedEvent* located_event = event->AsLocatedEvent();
     if (located_event->root_location_f() != located_event->location_f()) {
       DVLOG(1) << "RemoteEventDispatcher::DispatchEvent locations must match";
-      cb.Run(false);
+      std::move(cb).Run(false);
       return;
     }
 
@@ -46,7 +46,7 @@
     if (event->IsMousePointerEvent())
       display->platform_display()->MoveCursorTo(located_event->location());
   }
-  display->ProcessEvent(event.get(), base::BindRepeating(cb, true));
+  display->ProcessEvent(event.get(), base::BindOnce(std::move(cb), true));
 }
 
 }  // namespace ws
diff --git a/services/ui/ws/remote_event_dispatcher.h b/services/ui/ws/remote_event_dispatcher.h
index 528ed037..38c0c7a 100644
--- a/services/ui/ws/remote_event_dispatcher.h
+++ b/services/ui/ws/remote_event_dispatcher.h
@@ -21,7 +21,7 @@
   // mojom::RemoteEventDispatcher:
   void DispatchEvent(int64_t display_id,
                      std::unique_ptr<ui::Event> event,
-                     const DispatchEventCallback& cb) override;
+                     DispatchEventCallback cb) override;
 
   WindowServer* window_server_;
 
diff --git a/services/ui/ws/test_utils.cc b/services/ui/ws/test_utils.cc
index 0302189..b4ec8b1 100644
--- a/services/ui/ws/test_utils.cc
+++ b/services/ui/ws/test_utils.cc
@@ -253,10 +253,9 @@
                                          const gfx::Vector2d& drag_image_offset,
                                          ui::mojom::PointerKind source) {}
 
-void TestWindowManager::WmMoveDragImage(
-    const gfx::Point& screen_location,
-    const WmMoveDragImageCallback& callback) {
-  callback.Run();
+void TestWindowManager::WmMoveDragImage(const gfx::Point& screen_location,
+                                        WmMoveDragImageCallback callback) {
+  std::move(callback).Run();
 }
 
 void TestWindowManager::WmDestroyDragImage() {}
@@ -450,22 +449,21 @@
                                        uint32_t key_state,
                                        const gfx::Point& position,
                                        uint32_t effect_bitmask,
-                                       const OnDragEnterCallback& callback) {}
+                                       OnDragEnterCallback callback) {}
 
 void TestWindowTreeClient::OnDragOver(Id window,
                                       uint32_t key_state,
                                       const gfx::Point& position,
                                       uint32_t effect_bitmask,
-                                      const OnDragOverCallback& callback) {}
+                                      OnDragOverCallback callback) {}
 
 void TestWindowTreeClient::OnDragLeave(Id window) {}
 
-void TestWindowTreeClient::OnCompleteDrop(
-    Id window,
-    uint32_t key_state,
-    const gfx::Point& position,
-    uint32_t effect_bitmask,
-    const OnCompleteDropCallback& callback) {}
+void TestWindowTreeClient::OnCompleteDrop(Id window,
+                                          uint32_t key_state,
+                                          const gfx::Point& position,
+                                          uint32_t effect_bitmask,
+                                          OnCompleteDropCallback callback) {}
 
 void TestWindowTreeClient::OnPerformDragDropCompleted(uint32_t change_id,
                                                       bool success,
diff --git a/services/ui/ws/test_utils.h b/services/ui/ws/test_utils.h
index 3eadd639..2477d94 100644
--- a/services/ui/ws/test_utils.h
+++ b/services/ui/ws/test_utils.h
@@ -411,7 +411,7 @@
                         const gfx::Vector2d& drag_image_offset,
                         ui::mojom::PointerKind source) override;
   void WmMoveDragImage(const gfx::Point& screen_location,
-                       const WmMoveDragImageCallback& callback) override;
+                       WmMoveDragImageCallback callback) override;
   void WmDestroyDragImage() override;
   void WmPerformMoveLoop(uint32_t change_id,
                          Id window_id,
@@ -539,18 +539,18 @@
                    uint32_t key_state,
                    const gfx::Point& position,
                    uint32_t effect_bitmask,
-                   const OnDragEnterCallback& callback) override;
+                   OnDragEnterCallback callback) override;
   void OnDragOver(Id window,
                   uint32_t key_state,
                   const gfx::Point& position,
                   uint32_t effect_bitmask,
-                  const OnDragOverCallback& callback) override;
+                  OnDragOverCallback callback) override;
   void OnDragLeave(Id window) override;
   void OnCompleteDrop(Id window,
                       uint32_t key_state,
                       const gfx::Point& position,
                       uint32_t effect_bitmask,
-                      const OnCompleteDropCallback& callback) override;
+                      OnCompleteDropCallback callback) override;
   void OnPerformDragDropCompleted(uint32_t change_id,
                                   bool success,
                                   uint32_t action_taken) override;
diff --git a/services/ui/ws/window_server.cc b/services/ui/ws/window_server.cc
index 7125ab4..5c553bf 100644
--- a/services/ui/ws/window_server.cc
+++ b/services/ui/ws/window_server.cc
@@ -549,12 +549,12 @@
   }
 }
 
-void WindowServer::SetPaintCallback(
-    const base::Callback<void(ServerWindow*)>& callback) {
-  DCHECK(delegate_->IsTestConfig()) << "Paint callbacks are expensive, and "
-                                    << "allowed only in tests.";
-  DCHECK(window_paint_callback_.is_null() || callback.is_null());
-  window_paint_callback_ = callback;
+void WindowServer::SetSurfaceActivationCallback(
+    base::OnceCallback<void(ServerWindow*)> callback) {
+  DCHECK(delegate_->IsTestConfig()) << "Surface activation callbacks are "
+                                    << "expensive, and allowed only in tests.";
+  DCHECK(surface_activation_callback_.is_null() || callback.is_null());
+  surface_activation_callback_ = std::move(callback);
 }
 
 void WindowServer::StartMoveLoop(uint32_t change_id,
@@ -668,8 +668,8 @@
   DCHECK(host_frame_sink_manager_);
   // This is only used for testing to observe that a window has a
   // CompositorFrame.
-  if (!window_paint_callback_.is_null())
-    window_paint_callback_.Run(window);
+  if (surface_activation_callback_)
+    std::move(surface_activation_callback_).Run(window);
 
   Display* display = display_manager_->GetDisplayContaining(window);
   if (IsWindowConsideredWindowManagerRoot(display, window)) {
diff --git a/services/ui/ws/window_server.h b/services/ui/ws/window_server.h
index 5004b7e..4925a42 100644
--- a/services/ui/ws/window_server.h
+++ b/services/ui/ws/window_server.h
@@ -212,9 +212,11 @@
                              WindowTree* ignore_tree,
                              int64_t display_id);
 
-  // Sets a callback to be called whenever a ServerWindow is scheduled for
-  // a [re]paint. This should only be called in a test configuration.
-  void SetPaintCallback(const base::Callback<void(ServerWindow*)>& callback);
+  // Sets a callback to be called whenever a surface is activated. This
+  // corresponds a client submitting a new CompositorFrame for a Window. This
+  // should only be called in a test configuration.
+  void SetSurfaceActivationCallback(
+      base::OnceCallback<void(ServerWindow*)> callback);
 
   void StartMoveLoop(uint32_t change_id,
                      ServerWindow* window,
@@ -384,7 +386,7 @@
   uint32_t next_wm_change_id_;
 
   std::unique_ptr<GpuHost> gpu_host_;
-  base::Callback<void(ServerWindow*)> window_paint_callback_;
+  base::OnceCallback<void(ServerWindow*)> surface_activation_callback_;
 
   std::unique_ptr<UserActivityMonitor> user_activity_monitor_;
 
diff --git a/services/ui/ws/window_server_test_impl.cc b/services/ui/ws/window_server_test_impl.cc
index 93863340..56028334 100644
--- a/services/ui/ws/window_server_test_impl.cc
+++ b/services/ui/ws/window_server_test_impl.cc
@@ -17,35 +17,44 @@
 
 WindowServerTestImpl::~WindowServerTestImpl() {}
 
-void WindowServerTestImpl::OnWindowPaint(
+void WindowServerTestImpl::OnSurfaceActivated(
     const std::string& name,
-    const EnsureClientHasDrawnWindowCallback& cb,
+    EnsureClientHasDrawnWindowCallback cb,
     ServerWindow* window) {
+  // This api is used to detect when a client has painted once, which is
+  // dictated by whether there is a CompositorFrameSink.
   WindowTree* tree = window_server_->GetTreeWithClientName(name);
-  if (!tree)
-    return;
-  if (tree->HasRoot(window) && window->has_created_compositor_frame_sink()) {
-    cb.Run(true);
-    window_server_->SetPaintCallback(base::Callback<void(ServerWindow*)>());
+  if (tree && tree->HasRoot(window) &&
+      window->has_created_compositor_frame_sink()) {
+    std::move(cb).Run(true);
+  } else {
+    // No tree with the given name, or it hasn't painted yet. Install a callback
+    // for the next time a client creates a CompositorFramesink.
+    InstallCallback(name, std::move(cb));
   }
 }
 
+void WindowServerTestImpl::InstallCallback(
+    const std::string& client_name,
+    EnsureClientHasDrawnWindowCallback cb) {
+  window_server_->SetSurfaceActivationCallback(
+      base::BindOnce(&WindowServerTestImpl::OnSurfaceActivated,
+                     base::Unretained(this), client_name, std::move(cb)));
+}
+
 void WindowServerTestImpl::EnsureClientHasDrawnWindow(
     const std::string& client_name,
-    const EnsureClientHasDrawnWindowCallback& callback) {
+    EnsureClientHasDrawnWindowCallback callback) {
   WindowTree* tree = window_server_->GetTreeWithClientName(client_name);
   if (tree) {
     for (const ServerWindow* window : tree->roots()) {
       if (window->has_created_compositor_frame_sink()) {
-        callback.Run(true);
+        std::move(callback).Run(true);
         return;
       }
     }
   }
-
-  window_server_->SetPaintCallback(
-      base::Bind(&WindowServerTestImpl::OnWindowPaint, base::Unretained(this),
-                 client_name, std::move(callback)));
+  InstallCallback(client_name, std::move(callback));
 }
 
 }  // namespace ws
diff --git a/services/ui/ws/window_server_test_impl.h b/services/ui/ws/window_server_test_impl.h
index 9a6dc78..43c4d361 100644
--- a/services/ui/ws/window_server_test_impl.h
+++ b/services/ui/ws/window_server_test_impl.h
@@ -13,20 +13,27 @@
 class ServerWindow;
 class WindowServer;
 
+// Used to detect when a client (as identified by a name) has drawn at least
+// once to screen.
 class WindowServerTestImpl : public mojom::WindowServerTest {
  public:
   explicit WindowServerTestImpl(WindowServer* server);
   ~WindowServerTestImpl() override;
 
  private:
-  void OnWindowPaint(const std::string& name,
-                     const EnsureClientHasDrawnWindowCallback& cb,
-                     ServerWindow* window);
+  void OnSurfaceActivated(const std::string& name,
+                          EnsureClientHasDrawnWindowCallback cb,
+                          ServerWindow* window);
+
+  // Installs a callback that calls OnSurfaceActivated() the next time a client
+  // creates a compositor frame.
+  void InstallCallback(const std::string& name,
+                       EnsureClientHasDrawnWindowCallback cb);
 
   // mojom::WindowServerTest:
   void EnsureClientHasDrawnWindow(
       const std::string& client_name,
-      const EnsureClientHasDrawnWindowCallback& callback) override;
+      EnsureClientHasDrawnWindowCallback callback) override;
 
   WindowServer* window_server_;
 
diff --git a/services/ui/ws/window_tree.cc b/services/ui/ws/window_tree.cc
index 9639db66..7f6a13ba 100644
--- a/services/ui/ws/window_tree.cc
+++ b/services/ui/ws/window_tree.cc
@@ -1694,12 +1694,10 @@
   client()->OnChangeCompleted(change_id, success);
 }
 
-void WindowTree::GetWindowTree(
-    Id window_id,
-    const base::Callback<void(std::vector<mojom::WindowDataPtr>)>& callback) {
+void WindowTree::GetWindowTree(Id window_id, GetWindowTreeCallback callback) {
   std::vector<const ServerWindow*> windows(
       GetWindowTree(MakeClientWindowId(window_id)));
-  callback.Run(WindowsToWindowDatas(windows));
+  std::move(callback).Run(WindowsToWindowDatas(windows));
 }
 
 void WindowTree::SetCapture(uint32_t change_id, Id window_id) {
@@ -1994,31 +1992,31 @@
 void WindowTree::Embed(Id transport_window_id,
                        mojom::WindowTreeClientPtr client,
                        uint32_t flags,
-                       const EmbedCallback& callback) {
-  callback.Run(
+                       EmbedCallback callback) {
+  std::move(callback).Run(
       Embed(MakeClientWindowId(transport_window_id), std::move(client), flags));
 }
 
 void WindowTree::ScheduleEmbed(mojom::WindowTreeClientPtr client,
-                               const ScheduleEmbedCallback& callback) {
+                               ScheduleEmbedCallback callback) {
   const base::UnguessableToken token = base::UnguessableToken::Create();
   scheduled_embeds_[token] = std::move(client);
-  callback.Run(token);
+  std::move(callback).Run(token);
 }
 
 void WindowTree::EmbedUsingToken(Id transport_window_id,
                                  const base::UnguessableToken& token,
                                  uint32_t flags,
-                                 const EmbedUsingTokenCallback& callback) {
+                                 EmbedUsingTokenCallback callback) {
   mojom::WindowTreeClientPtr client =
       GetAndRemoveScheduledEmbedWindowTreeClient(token);
   if (!client) {
     DVLOG(1) << "EmbedUsingToken failed, no ScheduleEmbed(), token="
              << token.ToString();
-    callback.Run(false);
+    std::move(callback).Run(false);
     return;
   }
-  Embed(transport_window_id, std::move(client), flags, callback);
+  Embed(transport_window_id, std::move(client), flags, std::move(callback));
 }
 
 void WindowTree::SetFocus(uint32_t change_id, Id transport_window_id) {
@@ -2255,8 +2253,8 @@
 }
 
 void WindowTree::GetCursorLocationMemory(
-    const GetCursorLocationMemoryCallback& callback) {
-  callback.Run(
+    GetCursorLocationMemoryCallback callback) {
+  std::move(callback).Run(
       display_manager()->cursor_location_manager()->GetCursorLocationMemory());
 }
 
@@ -2424,7 +2422,7 @@
 
 void WindowTree::AddAccelerators(
     std::vector<mojom::WmAcceleratorPtr> accelerators,
-    const AddAcceleratorsCallback& callback) {
+    AddAcceleratorsCallback callback) {
   DCHECK(window_manager_state_);
 
   bool success = true;
@@ -2433,7 +2431,7 @@
             iter->get()->id, std::move(iter->get()->event_matcher)))
       success = false;
   }
-  callback.Run(success);
+  std::move(callback).Run(success);
 }
 
 void WindowTree::RemoveAccelerator(uint32_t id) {
@@ -2490,16 +2488,16 @@
                                 bool is_primary_display,
                                 Id window_id,
                                 const std::vector<display::Display>& mirrors,
-                                const SetDisplayRootCallback& callback) {
+                                SetDisplayRootCallback callback) {
   ServerWindow* display_root = ProcessSetDisplayRoot(
       display, TransportMetricsToDisplayMetrics(*viewport_metrics),
       is_primary_display, MakeClientWindowId(window_id), mirrors);
   if (!display_root) {
-    callback.Run(false);
+    std::move(callback).Run(false);
     return;
   }
   display_root->parent()->SetVisible(true);
-  callback.Run(true);
+  std::move(callback).Run(true);
 }
 
 void WindowTree::SetDisplayConfiguration(
@@ -2508,26 +2506,27 @@
     int64_t primary_display_id,
     int64_t internal_display_id,
     const std::vector<display::Display>& mirrors,
-    const SetDisplayConfigurationCallback& callback) {
+    SetDisplayConfigurationCallback callback) {
   std::vector<display::ViewportMetrics> metrics;
   for (auto& transport_ptr : transport_metrics)
     metrics.push_back(TransportMetricsToDisplayMetrics(*transport_ptr));
-  callback.Run(display_manager()->SetDisplayConfiguration(
+  std::move(callback).Run(display_manager()->SetDisplayConfiguration(
       displays, metrics, primary_display_id, internal_display_id, mirrors));
 }
 
 void WindowTree::SwapDisplayRoots(int64_t display_id1,
                                   int64_t display_id2,
-                                  const SwapDisplayRootsCallback& callback) {
+                                  SwapDisplayRootsCallback callback) {
   DCHECK(window_manager_state_);  // Only applicable to the window manager.
-  callback.Run(ProcessSwapDisplayRoots(display_id1, display_id2));
+  std::move(callback).Run(ProcessSwapDisplayRoots(display_id1, display_id2));
 }
 
 void WindowTree::SetBlockingContainers(
     std::vector<::ui::mojom::BlockingContainersPtr> blocking_containers,
-    const SetBlockingContainersCallback& callback) {
+    SetBlockingContainersCallback callback) {
   DCHECK(window_manager_state_);  // Only applicable to the window manager.
-  callback.Run(ProcessSetBlockingContainers(std::move(blocking_containers)));
+  std::move(callback).Run(
+      ProcessSetBlockingContainers(std::move(blocking_containers)));
 }
 
 void WindowTree::WmResponse(uint32_t change_id, bool response) {
diff --git a/services/ui/ws/window_tree.h b/services/ui/ws/window_tree.h
index 733ca35..bab6d2d 100644
--- a/services/ui/ws/window_tree.h
+++ b/services/ui/ws/window_tree.h
@@ -473,10 +473,7 @@
                      Id window_id,
                      Id relative_window_id,
                      mojom::OrderDirection direction) override;
-  void GetWindowTree(
-      Id window_id,
-      const base::Callback<void(std::vector<mojom::WindowDataPtr>)>& callback)
-      override;
+  void GetWindowTree(Id window_id, GetWindowTreeCallback callback) override;
   void SetCapture(uint32_t change_id, Id window_id) override;
   void ReleaseCapture(uint32_t change_id, Id window_id) override;
   void StartPointerWatcher(bool want_moves) override;
@@ -507,13 +504,13 @@
   void Embed(Id transport_window_id,
              mojom::WindowTreeClientPtr client,
              uint32_t flags,
-             const EmbedCallback& callback) override;
+             EmbedCallback callback) override;
   void ScheduleEmbed(mojom::WindowTreeClientPtr client,
-                     const ScheduleEmbedCallback& callback) override;
+                     ScheduleEmbedCallback callback) override;
   void EmbedUsingToken(Id transport_window_id,
                        const base::UnguessableToken& token,
                        uint32_t flags,
-                       const EmbedUsingTokenCallback& callback) override;
+                       EmbedUsingTokenCallback callback) override;
   void SetFocus(uint32_t change_id, Id transport_window_id) override;
   void SetCanFocus(Id transport_window_id, bool can_focus) override;
   void SetEventTargetingPolicy(Id transport_window_id,
@@ -542,8 +539,8 @@
   void GetWindowManagerClient(
       mojo::AssociatedInterfaceRequest<mojom::WindowManagerClient> internal)
       override;
-  void GetCursorLocationMemory(const GetCursorLocationMemoryCallback& callback)
-      override;
+  void GetCursorLocationMemory(
+      GetCursorLocationMemoryCallback callback) override;
   void PerformDragDrop(
       uint32_t change_id,
       Id source_window_id,
@@ -562,7 +559,7 @@
 
   // mojom::WindowManagerClient:
   void AddAccelerators(std::vector<mojom::WmAcceleratorPtr> accelerators,
-                       const AddAcceleratorsCallback& callback) override;
+                       AddAcceleratorsCallback callback) override;
   void RemoveAccelerator(uint32_t id) override;
   void AddActivationParent(Id transport_window_id) override;
   void RemoveActivationParent(Id transport_window_id) override;
@@ -577,20 +574,20 @@
                       bool is_primary_display,
                       Id window_id,
                       const std::vector<display::Display>& mirrors,
-                      const SetDisplayRootCallback& callback) override;
+                      SetDisplayRootCallback callback) override;
   void SetDisplayConfiguration(
       const std::vector<display::Display>& displays,
       std::vector<ui::mojom::WmViewportMetricsPtr> transport_metrics,
       int64_t primary_display_id,
       int64_t internal_display_id,
       const std::vector<display::Display>& mirrors,
-      const SetDisplayConfigurationCallback& callback) override;
+      SetDisplayConfigurationCallback callback) override;
   void SwapDisplayRoots(int64_t display_id1,
                         int64_t display_id2,
-                        const SwapDisplayRootsCallback& callback) override;
+                        SwapDisplayRootsCallback callback) override;
   void SetBlockingContainers(
       std::vector<mojom::BlockingContainersPtr> blocking_containers,
-      const SetBlockingContainersCallback& callback) override;
+      SetBlockingContainersCallback callback) override;
   void WmResponse(uint32_t change_id, bool response) override;
   void WmSetBoundsResponse(uint32_t change_id) override;
   void WmRequestClose(Id transport_window_id) override;
diff --git a/services/ui/ws/window_tree_client_unittest.cc b/services/ui/ws/window_tree_client_unittest.cc
index 7aa0794..59a8b999 100644
--- a/services/ui/ws/window_tree_client_unittest.cc
+++ b/services/ui/ws/window_tree_client_unittest.cc
@@ -433,14 +433,14 @@
                    uint32_t key_state,
                    const gfx::Point& position,
                    uint32_t effect_bitmask,
-                   const OnDragEnterCallback& callback) override {
+                   OnDragEnterCallback callback) override {
     NOTIMPLEMENTED();
   }
   void OnDragOver(Id window,
                   uint32_t key_state,
                   const gfx::Point& position,
                   uint32_t effect_bitmask,
-                  const OnDragOverCallback& callback) override {
+                  OnDragOverCallback callback) override {
     NOTIMPLEMENTED();
   }
   void OnDragLeave(Id window) override { NOTIMPLEMENTED(); }
@@ -448,7 +448,7 @@
                       uint32_t key_state,
                       const gfx::Point& position,
                       uint32_t effect_bitmask,
-                      const OnCompleteDropCallback& callback) override {
+                      OnCompleteDropCallback callback) override {
     NOTIMPLEMENTED();
   }
 
@@ -521,8 +521,8 @@
                         const gfx::Vector2d& drag_image_offset,
                         ui::mojom::PointerKind source) override {}
   void WmMoveDragImage(const gfx::Point& screen_location,
-                       const WmMoveDragImageCallback& callback) override {
-    callback.Run();
+                       WmMoveDragImageCallback callback) override {
+    std::move(callback).Run();
   }
   void WmDestroyDragImage() override {}
   void WmPerformMoveLoop(uint32_t change_id,
diff --git a/services/viz/privileged/interfaces/compositing/frame_sink_manager.mojom b/services/viz/privileged/interfaces/compositing/frame_sink_manager.mojom
index c5211901..b9af835 100644
--- a/services/viz/privileged/interfaces/compositing/frame_sink_manager.mojom
+++ b/services/viz/privileged/interfaces/compositing/frame_sink_manager.mojom
@@ -11,6 +11,7 @@
 import "services/viz/privileged/interfaces/compositing/frame_sink_video_capture.mojom";
 import "services/viz/privileged/interfaces/compositing/renderer_settings.mojom";
 import "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom";
+import "services/viz/public/interfaces/compositing/copy_output_request.mojom";
 import "services/viz/public/interfaces/compositing/frame_sink_id.mojom";
 import "services/viz/public/interfaces/compositing/local_surface_id.mojom";
 import "services/viz/public/interfaces/compositing/surface_id.mojom";
@@ -127,6 +128,11 @@
 
   // Marks the given SurfaceIds for destruction.
   EvictSurfaces(array<SurfaceId> surface_ids);
+
+  // Takes a snapshot of |frame_sink_id|. Next time a display frame is
+  // generated, the snapshot will be taken from the Surface belonging to
+  // |frame_sink_id| that is reachable from the root Surface.
+  RequestCopyOfOutput(FrameSinkId frame_sink_id, CopyOutputRequest request);
 };
 
 // The FrameSinkManagerClient interface is implemented by the Display
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 17590844..a3a726d 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -4690,6 +4690,21 @@
       },
       {
         "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "args": [
           "--site-per-process"
         ],
         "name": "site_per_process_components_browsertests",
@@ -4910,6 +4925,22 @@
       },
       {
         "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_extensions_browsertests.filter"
+        ],
+        "name": "network_service_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "args": [
           "--site-per-process"
         ],
         "name": "site_per_process_extensions_browsertests",
@@ -5073,6 +5104,22 @@
       },
       {
         "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_interactive_ui_tests.filter"
+        ],
+        "name": "network_service_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "args": [
           "--site-per-process"
         ],
         "name": "site_per_process_interactive_ui_tests",
@@ -6184,6 +6231,16 @@
       },
       {
         "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "args": [
           "--enable-features=NetworkService",
           "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
         ],
@@ -6192,6 +6249,28 @@
           "can_use_on_swarming_builders": true
         },
         "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_extensions_browsertests.filter"
+        ],
+        "name": "network_service_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_interactive_ui_tests.filter"
+        ],
+        "name": "network_service_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "interactive_ui_tests"
       }
     ],
     "isolated_scripts": [
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index c4ca742..8005c80e 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -749,6 +749,16 @@
       },
       {
         "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "args": [
           "--site-per-process"
         ],
         "name": "site_per_process_components_browsertests",
@@ -884,6 +894,17 @@
       },
       {
         "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_extensions_browsertests.filter"
+        ],
+        "name": "network_service_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "args": [
           "--site-per-process"
         ],
         "name": "site_per_process_extensions_browsertests",
@@ -976,6 +997,17 @@
       },
       {
         "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_interactive_ui_tests.filter"
+        ],
+        "name": "network_service_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "args": [
           "--site-per-process"
         ],
         "name": "site_per_process_interactive_ui_tests",
@@ -1506,6 +1538,16 @@
       },
       {
         "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "args": [
           "--site-per-process"
         ],
         "name": "site_per_process_components_browsertests",
@@ -1641,6 +1683,17 @@
       },
       {
         "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_extensions_browsertests.filter"
+        ],
+        "name": "network_service_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "args": [
           "--site-per-process"
         ],
         "name": "site_per_process_extensions_browsertests",
@@ -1734,6 +1787,17 @@
       },
       {
         "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_interactive_ui_tests.filter"
+        ],
+        "name": "network_service_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "args": [
           "--site-per-process"
         ],
         "name": "site_per_process_interactive_ui_tests",
@@ -2149,6 +2213,16 @@
         "test": "chromedriver_unittests"
       },
       {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_browsertests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -2237,6 +2311,17 @@
         "test": "extensions_browsertests"
       },
       {
+        "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_extensions_browsertests.filter"
+        ],
+        "name": "network_service_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_browsertests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -2280,6 +2365,17 @@
         "test": "interactive_ui_tests"
       },
       {
+        "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_interactive_ui_tests.filter"
+        ],
+        "name": "network_service_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 4b8350d..f4d0afa 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -56,10 +56,19 @@
   ]
 }
 
+source_set("extensions_browsertests_filters") {
+  testonly = true
+
+  data = [
+    "//testing/buildbot/filters/mojo.fyi.network_extensions_browsertests.filter",
+  ]
+}
+
 source_set("interactive_ui_tests_filters") {
   testonly = true
 
   data = [
+    "//testing/buildbot/filters/mojo.fyi.network_interactive_ui_tests.filter",
     "//testing/buildbot/filters/site-per-process.interactive_ui_tests.filter",
   ]
 }
diff --git a/testing/buildbot/filters/mash.browser_tests.filter b/testing/buildbot/filters/mash.browser_tests.filter
index 8a0961b..f65151d 100644
--- a/testing/buildbot/filters/mash.browser_tests.filter
+++ b/testing/buildbot/filters/mash.browser_tests.filter
@@ -175,9 +175,8 @@
 # OutputProtection problems:
 # interface_endpoint_client.cc(32) Check failed: !is_valid. The callback passed to OutputProtection::QueryStatus() was never run.
 # binder_registry.h(89) Failed to locate a binder for interface: display::mojom::OutputProtection
--Mojo/ECKEncryptedMediaTest.*
+-ECKEncryptedMediaTest.OutputProtectionTest
 -OutOfProcessPPAPITest.*
--Pepper/ECKEncryptedMediaTest.*
 
 # ash::FocusRingController::SetVisible() from LoginDisplayHostWebUI.
 -MultiAuthEnrollmentScreenTest.*
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
index 055ae80..ae47757c 100644
--- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -9,7 +9,6 @@
 -BackgroundXhrTest.TlsClientAuth
 -BrowsingDataRemoverBrowserTest.CookieDeletion
 -ChromeSecurityExploitBrowserTest.CreateFilesystemURLInExtensionOrigin
--ChromeSitePerProcessTest.LaunchExternalProtocolFromSubframe
 -DevToolsSanityTest.TestRawHeadersWithRedirectAndHSTS
 -DisabledSignInIsolationBrowserTest.SyntheticTrial
 -DomainReliabilityBrowserTest.Upload
@@ -82,13 +81,16 @@
 -WebViewTests/WebViewTest.DownloadCookieIsolation/1
 -WebViewTests/WebViewTest.DownloadCookieIsolation_CrossSession/0
 -WebViewTests/WebViewTest.DownloadCookieIsolation_CrossSession/1
--WebViewTests/WebViewTest.Shim_TestNavigationToExternalProtocol/0
--WebViewTests/WebViewTest.Shim_TestNavigationToExternalProtocol/1
 -WebViewTests/WebViewTest.StoragePersistence/0
 -WebViewTests/WebViewTest.StoragePersistence/1
 -WebViewTests/WebViewTest.WebViewInBackgroundPage/0
 -WebViewTests/WebViewTest.WebViewInBackgroundPage/1
 
+# http://crbug.com/706030 handle external protocols
+-ChromeSitePerProcessTest.LaunchExternalProtocolFromSubframe
+-WebViewTests/WebViewTest.Shim_TestNavigationToExternalProtocol/0
+-WebViewTests/WebViewTest.Shim_TestNavigationToExternalProtocol/1
+
 # Need support for DoNotTrack:
 # https://crbug.com/808948
 -DoNotTrackTest.Redirect
diff --git a/testing/buildbot/filters/mojo.fyi.network_extensions_browsertests.filter b/testing/buildbot/filters/mojo.fyi.network_extensions_browsertests.filter
new file mode 100644
index 0000000..ddc3d60
--- /dev/null
+++ b/testing/buildbot/filters/mojo.fyi.network_extensions_browsertests.filter
@@ -0,0 +1,5 @@
+# These tests currently fail when run with enablefeatures=NetworkService

+# See https://crbug.com/819248

+

+# http://crbug.com/706030

+-WebViewAPITest.TestNavigationToExternalProtocol

diff --git a/testing/buildbot/filters/mojo.fyi.network_interactive_ui_tests.filter b/testing/buildbot/filters/mojo.fyi.network_interactive_ui_tests.filter
new file mode 100644
index 0000000..84e8dc7
--- /dev/null
+++ b/testing/buildbot/filters/mojo.fyi.network_interactive_ui_tests.filter
@@ -0,0 +1,10 @@
+# These tests currently fail when run with enablefeatures=NetworkService

+# See https://crbug.com/819249

+

+-LocalNTPUITest.FakeboxRedirectsToOmnibox

+-PasswordGenerationInteractiveTest.GenerationTriggeredInIFrame

+-PasswordGenerationInteractiveTest.PopupShownAndDismissed

+-PasswordGenerationInteractiveTest.PopupShownAndDismissedByScrolling

+-PasswordGenerationInteractiveTest.PopupShownAndPasswordSelected

+-WebViewInteractiveTests/WebViewNewWindowInteractiveTest.NewWindow_WebRequestRemoveElement/0

+-WebViewInteractiveTests/WebViewNewWindowInteractiveTest.NewWindow_WebRequestRemoveElement/1

diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 305b448b..f927baa 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -1142,6 +1142,12 @@
       },
       'test': 'browser_tests',
     },
+    'network_service_components_browsertests': {
+      'args': [
+        '--enable-features=NetworkService',
+      ],
+      'test': 'components_browsertests',
+    },
     'network_service_content_browsertests': {
       'args': [
         '--enable-features=NetworkService',
@@ -1149,6 +1155,20 @@
       ],
       'test': 'content_browsertests',
     },
+    'network_service_extensions_browsertests': {
+      'args': [
+        '--enable-features=NetworkService',
+        '--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_extensions_browsertests.filter',
+      ],
+      'test': 'extensions_browsertests',
+    },
+    'network_service_interactive_ui_tests': {
+      'args': [
+        '--enable-features=NetworkService',
+        '--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_interactive_ui_tests.filter',
+      ],
+      'test': 'interactive_ui_tests',
+    },
   },
 
   'linux_chromeos_rel_specific_gtests': {
diff --git a/testing/libfuzzer/fuzzers/base_json_reader_fuzzer.cc b/testing/libfuzzer/fuzzers/base_json_reader_fuzzer.cc
index 7b3e448..81c865ed 100644
--- a/testing/libfuzzer/fuzzers/base_json_reader_fuzzer.cc
+++ b/testing/libfuzzer/fuzzers/base_json_reader_fuzzer.cc
@@ -10,6 +10,21 @@
 #include "base/json/json_reader.h"
 #include "base/values.h"
 
+#if defined(ADDRESS_SANITIZER)
+#include <sanitizer/asan_interface.h>
+#define POISON(address, size) __asan_poison_memory_region(address, size)
+#define UNPOISON(address, size) __asan_unpoison_memory_region(address, size)
+#elif defined(MEMORY_SANITIZER)
+#include <sanitizer/msan_interface.h>
+#define POISON(address, size) __msan_poison(address, size)
+#define UNPOISON(address, size) __msan_unpoison(address, size)
+#else
+#define POISON(address, size)
+#define UNPOISON(address, size)
+#endif
+
+constexpr size_t kPoisonSize = 1024;
+
 int error_code, error_line, error_column;
 std::string error_message;
 
@@ -18,10 +33,23 @@
   if (size < 1)
     return 0;
 
-  const std::string input_string(reinterpret_cast<const char*>(data), size - 1);
+  // Create a larger buffer than |size|, tell the sanitizer to poison
+  // around the edges, and copy the input into the middle. This will help
+  // detect buffer over-reads.
+  std::unique_ptr<uint8_t[]> input(new uint8_t[size + 2 * kPoisonSize]);
+  POISON(input.get(), kPoisonSize);
+  POISON(input.get() + kPoisonSize + size, kPoisonSize);
+  memcpy(input.get() + kPoisonSize, data, size);
+
+  base::StringPiece input_string(
+      reinterpret_cast<char*>(input.get() + kPoisonSize), size);
+
   const int options = data[size - 1];
   base::JSONReader::ReadAndReturnError(input_string, options, &error_code,
                                        &error_message, &error_line,
                                        &error_column);
+
+  UNPOISON(input.get(), size + 2 * kPoisonSize);
+
   return 0;
 }
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 35cc8b5..12422f0 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1970,24 +1970,6 @@
             ]
         }
     ],
-    "MojoCdm": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "win"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "MojoCdm"
-                    ]
-                }
-            ]
-        }
-    ],
     "MojoChannel": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index ac25d90..5df582a 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -714,7 +714,6 @@
 # Other extra layers.
 Bug(none) compositing/composite-scrollable-fixed-position-when-descendants-composite.html [ Failure ]
 Bug(none) paint/invalidation/position/abspos-shift-image-incorrect-repaint.html [ Failure ]
-Bug(none) paint/invalidation/image/animated-gif-transformed-offscreen.html [ Failure ]
 Bug(none) paint/invalidation/image/canvas-composite-repaint-by-all-imagesource.html [ Failure ]
 Bug(none) paint/invalidation/overflow/clipped-overflow-visible-subtree.html [ Failure ]
 Bug(none) paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll.html [ Failure ]
@@ -1149,12 +1148,6 @@
 crbug.com/738613 compositing/overflow/overflow-overlay-with-touch.html [ Failure ]
 crbug.com/738613 compositing/overflow/overflow-visible-with-touch.html [ Failure ]
 crbug.com/738613 compositing/squashing/invalidations-with-large-negative-margin-inline-content.html [ Failure ]
-crbug.com/738613 paint/invalidation/background/animated-gif-background.html [ Failure ]
-crbug.com/738613 paint/invalidation/image/animated-gif.html [ Failure ]
-crbug.com/738613 paint/invalidation/background/animated-png-background.html [ Failure ]
-crbug.com/738613 paint/invalidation/image/animated-png.html [ Failure ]
-crbug.com/738613 paint/invalidation/background/animated-webp-background.html [ Failure ]
-crbug.com/738613 paint/invalidation/image/animated-webp.html [ Failure ]
 crbug.com/738613 paint/invalidation/background/background-image-paint-invalidation.html [ Failure ]
 crbug.com/738613 paint/invalidation/compositing/text-color-change.html [ Failure ]
 crbug.com/738613 paint/invalidation/compositing/text-match-highlight.html [ Failure ]
@@ -1204,11 +1197,7 @@
 crbug.com/738613 paint/invalidation/scroll/overflow-scroll-in-overflow-scroll-scrolled.html [ Failure ]
 
 # "flattenInheritedTransform" of FrameView scroll translation.
-Bug(none) paint/invalidation/background/animated-gif-background-offscreen-firstline.html [ Failure ]
-Bug(none) paint/invalidation/background/animated-gif-background-offscreen.html [ Failure ]
-Bug(none) paint/invalidation/image/animated-gif-offscreen.html [ Failure ]
-Bug(none) paint/invalidation/image/animated-png-offscreen.html [ Failure ]
-Bug(none) paint/invalidation/image/animated-webp-offscreen.html [ Failure ]
+Bug(none) paint/invalidation/background/animated-svg-background-offscreen-firstline.html [ Failure ]
 Bug(none) paint/invalidation/svg/animated-svg-as-image-background-offscreen.html [ Failure ]
 Bug(none) paint/invalidation/svg/animated-svg-as-image-offscreen.html [ Failure ]
 
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
index 680f46a..d0ca9ce 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
@@ -61,8 +61,8 @@
 
 # https://crbug.com/601584 - No OOPIF support for UserGestureIndicator triggers
 #                            cross-origin-iframe.html layout test failure
-crbug.com/601584 external/wpt/http/tests/bluetooth/https/requestDevice/cross-origin-iframe.sub.https.html [ Skip ]
-crbug.com/601584 virtual/outofblink-cors/external/wpt/http/tests/bluetooth/https/requestDevice/cross-origin-iframe.sub.https.html [ Skip ]
+crbug.com/601584 external/wpt/bluetooth/requestDevice/cross-origin-iframe.sub.https.html [ Skip ]
+crbug.com/601584 virtual/outofblink-cors/external/wpt/bluetooth/requestDevice/cross-origin-iframe.sub.https.html [ Skip ]
 
 # https://crbug.com/606594 - UaF of delegate_ in WebFrameTestClient::willSendRequest
 # https://crbug.com/786510 - test tries to access cross-origin document body
diff --git a/third_party/WebKit/LayoutTests/SmokeTests b/third_party/WebKit/LayoutTests/SmokeTests
index cb2a5e67..c955df6 100644
--- a/third_party/WebKit/LayoutTests/SmokeTests
+++ b/third_party/WebKit/LayoutTests/SmokeTests
@@ -846,8 +846,6 @@
 netinfo/type-change-no-listener.html
 nfc/idl-NavigatorNFC.html
 paint/float/float-text-clip.html
-paint/invalidation/image/animated-gif-transformed-offscreen.html
-paint/invalidation/image/animated-png-offscreen.html
 paint/invalidation/button-inner-no-repaint.html
 paint/invalidation/compositing/should-not-repaint-composited-opacity.html
 paint/invalidation/table/display-table-row-crash.html
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 5c2bd039..43f6408 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1667,6 +1667,8 @@
 crbug.com/805463 external/wpt/acid/acid3/numbered-tests.html [ Skip ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Linux Win ] external/wpt/css/css-text/letter-spacing/letter-spacing-control-chars-001.html [ Failure ]
+crbug.com/626703 [ Linux Mac10.10 Mac10.12 Mac10.13 Retina Win ] external/wpt/payment-handler/can-make-payment-event-constructor.https.worker.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-writing-modes/available-size-019.html [ Failure ]
 crbug.com/626703 external/wpt/payment-request/change-shipping-option-select-last-manual.https.html [ Skip ]
 crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-text/word-break/word-break-break-all-004.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/accessibility/animation-policy.html b/third_party/WebKit/LayoutTests/accessibility/animation-policy.html
index 2d96c4ca..64c6f7e 100644
--- a/third_party/WebKit/LayoutTests/accessibility/animation-policy.html
+++ b/third_party/WebKit/LayoutTests/accessibility/animation-policy.html
@@ -20,11 +20,10 @@
 function imageLoaded() {
     if (!updated)
         return;
+    setTimeout(onTimeout, 0.5);
+}
 
-    var img = document.getElementById("target");
-    if (window.internals)
-        internals.advanceTimeForImage(img, 0.1);
-
+function onTimeout() {
     if (window.testRunner) {
         testRunner.layoutAndPaintAsyncThen(function () {
             window.requestAnimationFrame(finishTest);
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index c59f07b..5b83e85 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -54553,6 +54553,18 @@
      {}
     ]
    ],
+   "css/css-text/letter-spacing/letter-spacing-control-chars-001.html": [
+    [
+     "/css/css-text/letter-spacing/letter-spacing-control-chars-001.html",
+     [
+      [
+       "/css/css-text/letter-spacing/reference/letter-spacing-control-chars-001.ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/line-break/line-break-anywhere-001.html": [
     [
      "/css/css-text/line-break/line-break-anywhere-001.html",
@@ -121237,6 +121249,11 @@
      {}
     ]
    ],
+   "css/css-text/letter-spacing/reference/letter-spacing-control-chars-001.ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-text/line-break/OWNERS": [
     [
      {}
@@ -135437,21 +135454,6 @@
      {}
     ]
    ],
-   "fetch/api/basic/integrity-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "fetch/api/basic/integrity-sharedworker-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "fetch/api/basic/integrity-worker-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "fetch/api/basic/integrity.js": [
     [
      {}
@@ -151687,6 +151689,11 @@
      {}
     ]
    ],
+   "payment-handler/app-can-make-payment.js": [
+    [
+     {}
+    ]
+   ],
    "payment-handler/basic-card.js": [
     [
      {}
@@ -151697,6 +151704,11 @@
      {}
     ]
    ],
+   "payment-handler/can-make-payment-event.https-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "payment-handler/interfaces.https.any-expected.txt": [
     [
      {}
@@ -151707,6 +151719,11 @@
      {}
     ]
    ],
+   "payment-handler/manifest.json": [
+    [
+     {}
+    ]
+   ],
    "payment-handler/payment-app/payment.html": [
     [
      {}
@@ -214444,6 +214461,24 @@
      {}
     ]
    ],
+   "payment-handler/can-make-payment-event-constructor.https.html": [
+    [
+     "/payment-handler/can-make-payment-event-constructor.https.html",
+     {}
+    ]
+   ],
+   "payment-handler/can-make-payment-event-constructor.https.worker.js": [
+    [
+     "/payment-handler/can-make-payment-event-constructor.https.worker.html",
+     {}
+    ]
+   ],
+   "payment-handler/can-make-payment-event.https.html": [
+    [
+     "/payment-handler/can-make-payment-event.https.html",
+     {}
+    ]
+   ],
    "payment-handler/interfaces.https.any.js": [
     [
      "/payment-handler/interfaces.https.any.html",
@@ -291170,7 +291205,7 @@
    "reftest"
   ],
   "css/css-text/i18n/css3-text-line-break-opclns-004.html": [
-   "7c07a62b34871c9f840a980559449345735d238e",
+   "7d40237edd4a25e9cae1db394549f0ac540927a0",
    "reftest"
   ],
   "css/css-text/i18n/css3-text-line-break-opclns-005.html": [
@@ -291462,7 +291497,7 @@
    "reftest"
   ],
   "css/css-text/i18n/css3-text-line-break-opclns-111.html": [
-   "fd727b27858f521220e88b2e5b0a70238a9e2324",
+   "ebe515e578779fbd1ef3e692e6bb70077ddb046e",
    "reftest"
   ],
   "css/css-text/i18n/css3-text-line-break-opclns-112.html": [
@@ -292806,7 +292841,7 @@
    "support"
   ],
   "css/css-text/i18n/reference/css3-text-line-break-opclns-004-ref.html": [
-   "19cb202ab9ce2ec6c224a4d1eb44d9cdafe6126a",
+   "2ed590694cbcad7507ca78d5cb12f47e4ffafb55",
    "support"
   ],
   "css/css-text/i18n/reference/css3-text-line-break-opclns-005-ref.html": [
@@ -293098,7 +293133,7 @@
    "support"
   ],
   "css/css-text/i18n/reference/css3-text-line-break-opclns-111-ref.html": [
-   "cac6f5171c076ae5f5fae7f589061d46248c9e1f",
+   "c800f8b2c56f8bef95efb95bab9da4a167741b37",
    "support"
   ],
   "css/css-text/i18n/reference/css3-text-line-break-opclns-112-ref.html": [
@@ -293545,6 +293580,14 @@
    "708040c72a525e3ca122156c0212ca7ec14852bd",
    "support"
   ],
+  "css/css-text/letter-spacing/letter-spacing-control-chars-001.html": [
+   "f5e41752df238877b54acab9289a79fb9b59a9e5",
+   "reftest"
+  ],
+  "css/css-text/letter-spacing/reference/letter-spacing-control-chars-001.ref.html": [
+   "313d6e0924fde0eaa57265fafbe9923d4497f87c",
+   "support"
+  ],
   "css/css-text/line-break/OWNERS": [
    "5498c55b57270bb257281f8f2076a29a8af28fc4",
    "support"
@@ -322485,22 +322528,10 @@
    "54181cd222c7a5428f9468f4777c273ecc553f4f",
    "testharness"
   ],
-  "fetch/api/basic/integrity-expected.txt": [
-   "bc5d7efddbf3a7093955d2d5b950cfb10a2843d2",
-   "support"
-  ],
-  "fetch/api/basic/integrity-sharedworker-expected.txt": [
-   "bc5d7efddbf3a7093955d2d5b950cfb10a2843d2",
-   "support"
-  ],
   "fetch/api/basic/integrity-sharedworker.html": [
    "00d7eae5a324653caae19ab83bef76dd3503fb3b",
    "testharness"
   ],
-  "fetch/api/basic/integrity-worker-expected.txt": [
-   "bc5d7efddbf3a7093955d2d5b950cfb10a2843d2",
-   "support"
-  ],
   "fetch/api/basic/integrity-worker.html": [
    "1a0a6abd1c6bf8c4665a242d64f50bbbff4982f8",
    "testharness"
@@ -326390,7 +326421,7 @@
    "support"
   ],
   "html/browsers/origin/cross-origin-objects/cross-origin-objects-expected.txt": [
-   "90167ededd40cb5bd9da78d4c597ec14d01fd424",
+   "623830857628c2432bea02322006c78e04a90e5f",
    "support"
   ],
   "html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html": [
@@ -326398,7 +326429,7 @@
    "testharness"
   ],
   "html/browsers/origin/cross-origin-objects/cross-origin-objects.html": [
-   "5a5a86a7f989232a78a89e07e5678a1eeb8d4d6d",
+   "f28dabd90e50b23816512bbbf5b3392b40a3ec5a",
    "testharness"
   ],
   "html/browsers/origin/cross-origin-objects/frame-with-then.html": [
@@ -344170,7 +344201,7 @@
    "support"
   ],
   "interfaces/payment-handler.idl": [
-   "d1d50028218427ecb20ddd67061c9726fddcce3a",
+   "ddd283da0dadaffb3403652651c30f0f59007331",
    "support"
   ],
   "interfaces/payment-request.idl": [
@@ -344226,7 +344257,7 @@
    "support"
   ],
   "interfaces/xhr.idl": [
-   "5363c96002f96f796f4800b92f8748b1141f0821",
+   "00b3847513bf63b69f94a75662f31bc71f16b597",
    "support"
   ],
   "intersection-observer/OWNERS": [
@@ -353453,6 +353484,10 @@
    "75c6f2aca2a7226545ea81a5f6ea93cb9742ace4",
    "support"
   ],
+  "payment-handler/app-can-make-payment.js": [
+   "95d11a09c5468bcea9172e1511be5c8fa5b4d9d7",
+   "support"
+  ],
   "payment-handler/basic-card.js": [
    "40165fc47d8681622cde490a9a8ec3c8eaded43f",
    "support"
@@ -353461,16 +353496,36 @@
    "dbed6dd422b57b7c343b2171aeb9ad921eb3231c",
    "support"
   ],
+  "payment-handler/can-make-payment-event-constructor.https.html": [
+   "f48b21e7376a7a45b7e121a4331362ecd46f8dcd",
+   "testharness"
+  ],
+  "payment-handler/can-make-payment-event-constructor.https.worker.js": [
+   "39e90eca2f6cb7d4fa62655a1aa492acea30f436",
+   "testharness"
+  ],
+  "payment-handler/can-make-payment-event.https-expected.txt": [
+   "dc2102659a6597e232806c4957057462b7cdd0fd",
+   "support"
+  ],
+  "payment-handler/can-make-payment-event.https.html": [
+   "1433a0dcee409e808adc6ed5b65a532128a1d350",
+   "testharness"
+  ],
   "payment-handler/interfaces.https.any-expected.txt": [
-   "c53b5e4996a1870ee79ae921a12b0a548bc94f26",
+   "9cdc2a2ce98bf6b06ff7c4816f30d00c2add7b0f",
    "support"
   ],
   "payment-handler/interfaces.https.any.js": [
-   "a20a5909c9a94120049e45e543cc623b875ea7f5",
+   "7910e8fa96dabaf03f1806c6faad94a640e4726c",
    "testharness"
   ],
   "payment-handler/interfaces.https.any.worker-expected.txt": [
-   "a618c74910c6fabeb57cada573955adf7d77b558",
+   "b9567acc565acb84bb9ef84eb13b1f7f9671fcc8",
+   "support"
+  ],
+  "payment-handler/manifest.json": [
+   "1224f847a504794d499dba09ec7db82c5614146d",
    "support"
   ],
   "payment-handler/payment-app/payment.html": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/http/tests/bluetooth/https/requestDevice/cross-origin-iframe.sub.https.html b/third_party/WebKit/LayoutTests/external/wpt/bluetooth/requestDevice/cross-origin-iframe.sub.https.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/external/wpt/http/tests/bluetooth/https/requestDevice/cross-origin-iframe.sub.https.html
rename to third_party/WebKit/LayoutTests/external/wpt/bluetooth/requestDevice/cross-origin-iframe.sub.https.html
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-text/letter-spacing/letter-spacing-control-chars-001.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-text/letter-spacing/letter-spacing-control-chars-001.html
new file mode 100644
index 0000000..87d071a1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-text/letter-spacing/letter-spacing-control-chars-001.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>letter-spacing should not be applied to zero-width format controls</title>
+<link rel=match href="reference/letter-spacing-control-chars-001.ref.html">
+<link rel=help href="https://drafts.csswg.org/css-text-3/#letter-spacing-property">
+<style>
+.test {
+  font: 12px/2 monospace;
+  letter-spacing: 4px;
+}
+</style>
+</head>
+<body>
+The two lines below should match:<br>
+<div class=test>
+&#x200b;let&#x200b;ter&#x200b;spac&#x200b;ing&#x200b; should not be
+&#x200c;af&#x200c;fec&#x200c;ted&#x200c; by
+&#x200d;ze&#x200d;ro&#x200d;-&#x200d;width&#x200d;
+&#x2060;for&#x2060;mat&#x2060;
+&#xfeff;char&#xfeff;ac&#xfeff;ters&#xfeff;
+</div>
+<div class=test>
+letterspacing should not be
+affected by
+zero-width
+format
+characters
+</div>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-text/letter-spacing/reference/letter-spacing-control-chars-001.ref.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-text/letter-spacing/reference/letter-spacing-control-chars-001.ref.html
new file mode 100644
index 0000000..977d821
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-text/letter-spacing/reference/letter-spacing-control-chars-001.ref.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>letter-spacing should not be applied to zero-width format controls</title>
+<style>
+.test {
+  font: 12px/2 monospace;
+  letter-spacing: 4px;
+}
+</style>
+</head>
+<body>
+The two lines below should match:<br>
+<div class=test>
+letterspacing should not be
+affected by
+zero-width
+format
+characters
+</div>
+<div class=test>
+letterspacing should not be
+affected by
+zero-width
+format
+characters
+</div>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-objects/parse-invalid.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-objects/parse-invalid.html
index 6ff9218..ea1d564 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-objects/parse-invalid.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-objects/parse-invalid.html
@@ -6,7 +6,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <body>
-<div id="log">
+<div id="log"></div>
 <script>
 'use strict';
 
@@ -26,4 +26,8 @@
   assert_throws(new TypeError(), () => CSSStyleValue.parse('margin', '10deg'));
 }, 'CSSStyleValue.parse() with invalid value for shorthand property throws TypeError');
 
+test(() => {
+  assert_throws(new TypeError(), () => CSSStyleValue.parse('--foo', ''));
+}, 'CSSStyleValue.parse() with invalid value for custom property throws TypeError');
+
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-objects/parseAll-invalid.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-objects/parseAll-invalid.html
index 1ac1f89..c1809543 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-objects/parseAll-invalid.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-objects/parseAll-invalid.html
@@ -6,7 +6,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <body>
-<div id="log">
+<div id="log"></div>
 <script>
 'use strict';
 
@@ -23,7 +23,11 @@
 }, 'CSSStyleValue.parseAll() with invalid value for valid property throws TypeError');
 
 test(() => {
-  assert_throws(new TypeError(), () => CSSStyleValue.parse('margin', '10deg'));
+  assert_throws(new TypeError(), () => CSSStyleValue.parseAll('margin', '10deg'));
 }, 'CSSStyleValue.parseAll() with invalid value for shorthand property throws TypeError');
 
+test(() => {
+  assert_throws(new TypeError(), () => CSSStyleValue.parseAll('--foo', ''));
+}, 'CSSStyleValue.parseAll() with invalid value for custom property throws TypeError');
+
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects-expected.txt
index 5026c25..df4fa3d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects-expected.txt
@@ -18,8 +18,8 @@
 PASS A and B jointly observe the same identity for cross-origin Window and Location
 PASS Cross-origin functions get local Function.prototype
 FAIL Cross-origin Window accessors get local Function.prototype Cannot read property 'name' of undefined
-FAIL Same-origin observers get different functions for cross-origin objects assert_true: same-origin Window functions get their own object expected true got false
-FAIL Same-origin observers get different accessors for cross-origin Window assert_true: different Window accessors per-incumbent script settings object expected true got false
+FAIL Same-origin observers get different functions for cross-origin objects assert_not_equals: same-origin Window functions get their own object got disallowed value function "function () { [native code] }"
+FAIL Same-origin observers get different accessors for cross-origin Window assert_not_equals: different Window accessors per-incumbent script settings object got disallowed value undefined
 FAIL Same-origin observers get different accessors for cross-origin Location Blocked a frame with origin "http://web-platform.test:8001" from accessing a cross-origin frame.
 FAIL {}.toString.call() does the right thing on cross-origin objects assert_equals: expected "[object Object]" but got "[object Location]"
 PASS Resolving a promise with a cross-origin window without a 'then' subframe should work.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
index e0b4b0c9..caac56a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
@@ -127,11 +127,11 @@
  * [[GetPrototypeOf]]
  */
 addTest(function() {
-  assert_true(Object.getPrototypeOf(C) === null, "cross-origin Window proto is null");
-  assert_true(Object.getPrototypeOf(C.location) === null, "cross-origin Location proto is null (__proto__)");
+  assert_equals(Object.getPrototypeOf(C), null, "cross-origin Window proto is null");
+  assert_equals(Object.getPrototypeOf(C.location), null, "cross-origin Location proto is null (__proto__)");
   var protoGetter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').get;
-  assert_true(protoGetter.call(C) === null, "cross-origin Window proto is null");
-  assert_true(protoGetter.call(C.location) === null, "cross-origin Location proto is null (__proto__)");
+  assert_equals(protoGetter.call(C), null, "cross-origin Window proto is null");
+  assert_equals(protoGetter.call(C.location), null, "cross-origin Location proto is null (__proto__)");
   assert_throws("SecurityError", function() { C.__proto__; }, "__proto__ property not available cross-origin");
   assert_throws("SecurityError", function() { C.location.__proto__; }, "__proto__ property not available cross-origin");
 
@@ -349,8 +349,8 @@
 }, "[[OwnPropertyKeys]] should not reorder where 'then' appears if it's a named subframe, nor add another copy of 'then'");
 
 addTest(function() {
-  assert_true(B.eval('parent.C') === C, "A and B observe the same identity for C's Window");
-  assert_true(B.eval('parent.C.location') === C.location, "A and B observe the same identity for C's Location");
+  assert_equals(B.eval('parent.C'), C, "A and B observe the same identity for C's Window");
+  assert_equals(B.eval('parent.C.location'), C.location, "A and B observe the same identity for C's Location");
 }, "A and B jointly observe the same identity for cross-origin Window and Location");
 
 function checkFunction(f, proto) {
@@ -373,17 +373,17 @@
 
 addTest(function() {
   checkFunction(close, Function.prototype);
-  assert_true(close != B.close, 'same-origin Window functions get their own object');
-  assert_true(close != C.close, 'cross-origin Window functions get their own object');
+  assert_not_equals(close, B.close, 'same-origin Window functions get their own object');
+  assert_not_equals(close, C.close, 'cross-origin Window functions get their own object');
   var close_B = B.eval('parent.C.close');
-  assert_true(close != close_B, 'close_B is unique when viewed by the parent');
-  assert_true(close_B != C.close, 'different Window functions per-incumbent script settings object');
+  assert_not_equals(close, close_B, 'close_B is unique when viewed by the parent');
+  assert_not_equals(close_B, C.close, 'different Window functions per-incumbent script settings object');
   checkFunction(close_B, B.Function.prototype);
 
   checkFunction(location.replace, Function.prototype);
-  assert_true(location.replace != C.location.replace, "cross-origin Location functions get their own object");
+  assert_not_equals(location.replace, C.location.replace, "cross-origin Location functions get their own object");
   var replace_B = B.eval('parent.C.location.replace');
-  assert_true(replace_B != C.location.replace, 'different Location functions per-incumbent script settings object');
+  assert_not_equals(replace_B, C.location.replace, 'different Location functions per-incumbent script settings object');
   checkFunction(replace_B, B.Function.prototype);
 }, "Same-origin observers get different functions for cross-origin objects");
 
@@ -393,8 +393,8 @@
   var get_self_parent = Object.getOwnPropertyDescriptor(window, 'parent').get;
   var get_parent_A = Object.getOwnPropertyDescriptor(C, 'parent').get;
   var get_parent_B = B.eval('Object.getOwnPropertyDescriptor(parent.C, "parent").get');
-  assert_true(get_self_parent != get_parent_A, 'different Window accessors per-incumbent script settings object');
-  assert_true(get_parent_A != get_parent_B, 'different Window accessors per-incumbent script settings object');
+  assert_not_equals(get_self_parent, get_parent_A, 'different Window accessors per-incumbent script settings object');
+  assert_not_equals(get_parent_A, get_parent_B, 'different Window accessors per-incumbent script settings object');
   checkFunction(get_self_parent, Function.prototype);
   checkFunction(get_parent_A, Function.prototype);
   checkFunction(get_parent_B, B.Function.prototype);
@@ -404,8 +404,8 @@
   var set_self_href = Object.getOwnPropertyDescriptor(window.location, 'href').set;
   var set_href_A = Object.getOwnPropertyDescriptor(C.location, 'href').set;
   var set_href_B = B.eval('Object.getOwnPropertyDescriptor(parent.C.location, "href").set');
-  assert_true(set_self_href != set_href_A, 'different Location accessors per-incumbent script settings object');
-  assert_true(set_href_A != set_href_B, 'different Location accessors per-incumbent script settings object');
+  assert_not_equals(set_self_href, set_href_A, 'different Location accessors per-incumbent script settings object');
+  assert_not_equals(set_href_A, set_href_B, 'different Location accessors per-incumbent script settings object');
   checkFunction(set_self_href, Function.prototype);
   checkFunction(set_href_A, Function.prototype);
   checkFunction(set_href_B, B.Function.prototype);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/payment-handler.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/payment-handler.idl
index 88d3b1ce..8cf44ee1 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/interfaces/payment-handler.idl
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/payment-handler.idl
@@ -27,6 +27,21 @@
              DOMString sizes;
              DOMString type;
 };
+[Constructor(DOMString type, CanMakePaymentEventInit eventInitDict),
+ Exposed=ServiceWorker]
+interface CanMakePaymentEvent : ExtendableEvent {
+    readonly attribute USVString                           topLevelOrigin;
+    readonly attribute USVString                           paymentRequestOrigin;
+    readonly attribute FrozenArray<PaymentMethodData>      methodData;
+    readonly attribute FrozenArray<PaymentDetailsModifier> modifiers;
+    void respondWith(Promise<boolean> canMakePaymentResponse);
+};
+dictionary CanMakePaymentEventInit : ExtendableEventInit {
+    USVString                        topLevelOrigin;
+    USVString                        paymentRequestOrigin;
+    sequence<PaymentMethodData>      methodData;
+    sequence<PaymentDetailsModifier> modifiers;
+};
 [Constructor(DOMString type, PaymentRequestEventInit eventInitDict),
  Exposed=ServiceWorker]
 interface PaymentRequestEvent : ExtendableEvent {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/xhr.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/xhr.idl
index 707e3550..44774da0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/interfaces/xhr.idl
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/xhr.idl
@@ -1,4 +1,4 @@
-/*[Exposed=(Window,Worker)]*/
+[Exposed=(Window,DedicatedWorker,SharedWorker)]
 interface XMLHttpRequestEventTarget : EventTarget {
   // event handlers
   attribute EventHandler onloadstart;
@@ -10,7 +10,7 @@
   attribute EventHandler onloadend;
 };
 
-/*[Exposed=(Window,Worker)]*/
+[Exposed=(Window,DedicatedWorker,SharedWorker)]
 interface XMLHttpRequestUpload : XMLHttpRequestEventTarget {
 };
 
@@ -23,8 +23,8 @@
   "text"
 };
 
-[Constructor/*,
- Exposed=(Window,Worker)*/]
+[Constructor,
+ Exposed=(Window,DedicatedWorker,SharedWorker)]
 interface XMLHttpRequest : XMLHttpRequestEventTarget {
   // event handler
   attribute EventHandler onreadystatechange;
@@ -43,7 +43,7 @@
   void setRequestHeader(ByteString name, ByteString value);
            attribute unsigned long timeout;
            attribute boolean withCredentials;
-  readonly attribute XMLHttpRequestUpload upload;
+  [SameObject] readonly attribute XMLHttpRequestUpload upload;
   void send(optional (Document or BodyInit)? body = null);
   void abort();
 
@@ -62,22 +62,22 @@
 
 typedef (File or USVString) FormDataEntryValue;
 
-[Constructor(optional HTMLFormElement form)/*,
- Exposed=(Window,Worker)*/]
+[Constructor(optional HTMLFormElement form),
+ Exposed=(Window,Worker)]
 interface FormData {
-  void append(USVString name, Blob value, optional USVString filename);
   void append(USVString name, USVString value);
+  void append(USVString name, Blob blobValue, optional USVString filename);
   void delete(USVString name);
   FormDataEntryValue? get(USVString name);
   sequence<FormDataEntryValue> getAll(USVString name);
   boolean has(USVString name);
-  void set(USVString name, Blob value, optional USVString filename);
   void set(USVString name, USVString value);
-  /*iterable<USVString, FormDataEntryValue>;*/
+  void set(USVString name, Blob blobValue, optional USVString filename);
+  iterable<USVString, FormDataEntryValue>;
 };
 
-[Constructor(DOMString type, optional ProgressEventInit eventInitDict)/*,
- Exposed=(Window,Worker)*/]
+[Constructor(DOMString type, optional ProgressEventInit eventInitDict),
+ Exposed=(Window,DedicatedWorker,SharedWorker)]
 interface ProgressEvent : Event {
   readonly attribute boolean lengthComputable;
   readonly attribute unsigned long long loaded;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-handler/app-can-make-payment.js b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/app-can-make-payment.js
new file mode 100644
index 0000000..beec328
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/app-can-make-payment.js
@@ -0,0 +1,114 @@
+self.addEventListener('canmakepayment', event => {
+  if (event.methodData.length !== 1) {
+    const msg = 'Expected exactly one method data.';
+    event.respondWith(Promise.reject(new Error(msg)));
+    return;
+  }
+
+  const [method] = event.methodData;
+  if (!method || method.supportedMethods.length !== 1) {
+    const msg = 'Expected exactly one supported method name';
+    event.respondWith(Promise.reject(new Error(msg)));
+    return;
+  }
+
+  if (method.data.defaultParameter !== 'defaultValue') {
+    const msg = `Unexpected value for "defaultParameter": ${
+      method.data.defaultParameter
+    }`;
+    event.respondWith(Promise.reject(new Error(msg)));
+    return;
+  }
+
+  if ('defaultUnsupportedParameter' in method.data) {
+    const msg = 'Unexpected "defaultUnsupportedParameter"';
+    event.respondWith(Promise.reject(new Error(msg)));
+    return;
+  }
+
+  if (event.modifiers.length !== 1) {
+    const msg = 'Expected exactly one modifier';
+    event.respondWith(Promise.reject(new Error(msg)));
+    return;
+  }
+
+  const [modifier] = event.modifiers;
+
+  if (!modifier || modifier.supportedMethods.length !== 1) {
+    const msg = 'Expected exactly one supported method name in modifier';
+    event.respondWith(Promise.reject(new Error(msg)));
+    return;
+  }
+
+  for (const member of [
+    'additionalDisplayItems',
+    'modifiedUnsupportedParameter',
+    'total',
+  ]) {
+    if (member in modifier) {
+      const msg = `Unexpected member "${member}" in modifier`;
+      event.respondWith(Promise.reject(new Error(msg)));
+      return;
+    }
+  }
+
+  const [methodName] = method.supportedMethods;
+  if (methodName === 'basic-card') {
+    const msg =
+      '"basic-card" payment method must never be checked in CanMakePaymentEvent';
+    event.respondWith(Promise.reject(new Error(msg)));
+    return;
+  }
+
+  const [modifierMethodName] = modifier.supportedMethods;
+  if (modifierMethodName !== methodName) {
+    const msg = `Unexpected modifier method name: "${modifierMethodName}". Expected "${methodName}".`;
+    event.respondWith(Promise.reject(new Error(msg)));
+    return;
+  }
+
+  if (modifier.data.modifiedParameter !== 'modifiedValue') {
+    const msg = `Unexpected value for 'modifiedParameter': ${
+      modifier.data.modifiedParameter
+    }`;
+    event.respondWith(Promise.reject(new Error(msg)));
+    return;
+  }
+
+  const methodAsURL = new URL(methodName);
+  if (event.topLevelOrigin !== methodAsURL.origin) {
+    const msg = `Unexpected event.topLevelOrigin: "${
+      event.topLevelOrigin
+    }". Expected "${methodAsURL.origin}".`;
+    event.respondWith(Promise.reject(new Error(msg)));
+    return;
+  }
+
+  if (event.paymentRequestOrigin !== methodAsURL.origin) {
+    const msg = `Unexpected iframe origin ${event.paymentRequestOrigin}`;
+    event.respondWith(Promise.reject(new Error(msg)));
+    return;
+  }
+
+  switch (methodAsURL.pathname.substr(1)) {
+    case 'canMakePayment-true':
+      event.respondWith(true);
+      break;
+    case 'canMakePayment-false':
+      event.respondWith(false);
+      break;
+    case 'canMakePayment-promise-true':
+      event.respondWith(Promise.resolve(true));
+      break;
+    case 'canMakePayment-promise-false':
+      event.respondWith(Promise.resolve(false));
+      break;
+    case 'canMakePayment-custom-error':
+      event.respondWith(Promise.reject(new Error('Custom error')));
+      break;
+    default:
+      const msg = `Unrecognized payment method name "${methodName}".`;
+      event.respondWith(Promise.reject(new Error(msg)));
+      break;
+  }
+});
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-handler/can-make-payment-event-constructor.https.html b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/can-make-payment-event-constructor.https.html
new file mode 100644
index 0000000..d8480a29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/can-make-payment-event-constructor.https.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Test for CanMakePaymentEvent Constructor (window)</title>
+<link rel="help" href="https://w3c.github.io/payment-handler/#dom-canmakepaymentevent">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+  assert_false('CanMakePaymentEvent' in window);
+}, 'CanMakePaymentEvent constructor must not be exposed in window');
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-handler/can-make-payment-event-constructor.https.worker.js b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/can-make-payment-event-constructor.https.worker.js
new file mode 100644
index 0000000..06af7de
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/can-make-payment-event-constructor.https.worker.js
@@ -0,0 +1,49 @@
+// https://w3c.github.io/payment-handler/#the-canmakepaymentevent
+
+'use strict';
+
+if (self.importScripts) {
+  importScripts('/resources/testharness.js');
+}
+
+test(() => {
+  try {
+    new CanMakePaymentEvent('test');
+  } catch (err) {
+    assert_unreached(`Unexpected exception: ${err.message}`);
+  }
+}, 'CanMakePaymentEvent can be constructed in service worker.');
+
+test(() => {
+  const ev = new CanMakePaymentEvent('test', {
+    bubbles: true,
+    cancelabel: true,
+    composed: true,
+  });
+  assert_false(ev.isTrusted, 'constructed in script, so not be trusted');
+  assert_true(ev.bubbles, 'set by EventInitDict');
+  assert_true(ev.cancelable, 'set by EventInitDict');
+  assert_true(ev.composed, 'set by EventInitDict');
+  assert_equals(ev.target, null, 'initially null');
+  assert_equals(ev.type, 'test');
+}, 'CanMakePaymentEvent can be constructed with an EventInitDict, even if not trusted');
+
+test(() => {
+  const ev = new CanMakePaymentEvent('test', {
+    topLevelOrigin: 'https://foo.com',
+    paymentRequestOrigin: 'https://bar.com',
+    methodData: [],
+    modifiers: [],
+  });
+  assert_false(ev.isTrusted, 'constructed in script, so not be trusted');
+  assert_equals(ev.topLevelOrigin, 'https://foo.com');
+  assert_equals(ev.paymentRequestOrigin, 'https://bar.com');
+}, 'CanMakePaymentEvent can be constructed with a CanMakePaymentEventInit, even if not trusted');
+
+test(() => {
+  const ev = new CanMakePaymentEvent('test');
+  self.addEventListener('test', evt => {
+    assert_equals(ev, evt);
+  });
+  self.dispatchEvent(ev);
+}, 'CanMakePaymentEvent can be dispatched, even if not trusted');
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-handler/can-make-payment-event.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/can-make-payment-event.https-expected.txt
new file mode 100644
index 0000000..37e87e9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/can-make-payment-event.https-expected.txt
@@ -0,0 +1,13 @@
+This is a testharness.js-based test.
+FAIL If a payment handler is not installed, then the payment method is not supported. assert_equals: If it throws, then it must be NotAllowedError expected "NotAllowedError" but got "UnknownError"
+FAIL If CanMakePaymentEvent.respondWith(false) is called, then the payment method is not supported. promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
+FAIL If CanMakePaymentEvent.respondWith(Promise.resolve(false)) is called, then the payment method is not supported. promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
+FAIL If CanMakePaymentEvent.respondWith(true) is called, then the payment method is supported. promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
+FAIL If CanMakePaymentEvent.respondWith(Promise.resolve(true)) is called, then the payment method is supported. promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
+FAIL If CanMakePaymentEvent.respondWith(Promise.reject(error)) is called, then the payment method is not supported. promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
+FAIL If an app supports "basic-card" in general and that's what merchant requests as well, then capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card". promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
+FAIL If an app has less specific "basic-card" capabilites than merchant's request, capability filtering should not make the app available for use. CanMakePaymentEvent should not be fired for "basic-card".  promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
+FAIL If an app has the exact "basic-card" capabilities that a merchant requested, capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card". promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
+FAIL If an app has more specific "basic-card" capabilities than merchant's request, capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card". promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-handler/can-make-payment-event.https.html b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/can-make-payment-event.https.html
new file mode 100644
index 0000000..3266c2c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/can-make-payment-event.https.html
@@ -0,0 +1,386 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Tests for CanMakePaymentEvent</title>
+<link rel="help" href="https://w3c.github.io/payment-handler/#the-canmakepaymentevent">
+<link rel="manifest" href="manifest.json">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p>The "basic-card" test requires that you don't have a prepaid MIR card stored
+in the browser. If you do, please remove it for the duration of the test.</p>
+<script>
+const instrumentKey = 'instrument-key';
+
+async function registerApp(methodName) {
+  await navigator.serviceWorker.register('app-can-make-payment.js');
+  const registration = await navigator.serviceWorker.ready;
+  if (!registration.paymentManager) {
+    return;
+  }
+  if (registration.paymentManager.requestPermission) {
+    const permission = await registration.paymentManager.requestPermission();
+    if (permission !== 'granted') {
+      return;
+    }
+  }
+  await registration.paymentManager.instruments.set(instrumentKey, {
+    name: 'Test Payment Method',
+    enabledMethods: [methodName],
+  });
+  return registration;
+}
+
+function buildPaymentRequest(methodName) {
+  const unsupportedMethodName = methodName + '-unsupported';
+  return new PaymentRequest(
+    [
+      {
+        supportedMethods: methodName,
+        data: {
+          defaultParameter: 'defaultValue',
+        },
+      },
+      {
+        supportedMethods: unsupportedMethodName,
+        data: {
+          defaultUnsupportedParameter: 'defaultUnsupportedValue',
+        },
+      },
+    ],
+    {
+      total: {
+        label: 'Total',
+        amount: {
+          currency: 'USD',
+          value: '0',
+        },
+      },
+      displayItems: [
+        {
+          label: 'Nada',
+          amount: {currency: 'USD', value: '0'},
+        },
+      ],
+      modifiers: [
+        {
+          supportedMethods: [methodName],
+          data: {
+            modifiedParameter: 'modifiedValue',
+          },
+          total: {
+            label: 'Modified Total',
+            amount: {
+              currency: 'USD',
+              value: '0.0001',
+            },
+          },
+          additionalDisplayItems: [
+            {
+              label: 'Something',
+              amount: {currency: 'USD', value: '0.0001'},
+            },
+          ],
+        },
+        {
+          supportedMethods: [unsupportedMethodName],
+          data: {
+            modifiedUnsupportedParameter: 'modifiedUnsupportedValue',
+          },
+          total: {
+            label: 'Modified Unsupported Total',
+            amount: {
+              currency: 'USD',
+              value: '10',
+            },
+          },
+          additionalDisplayItems: [
+            {
+              label: 'Something Unsupported',
+              amount: {currency: 'USD', value: '10'},
+            },
+          ],
+        },
+      ],
+    },
+  );
+}
+
+promise_test(async t => {
+  const methodName = window.location.origin + '/canMakePayment-true';
+  // Intentionally do not install the payment app.
+  const request = buildPaymentRequest(methodName);
+  assert_not_equals(request, undefined);
+  let paymentRequestCanMakePaymentResult;
+  try {
+    paymentRequestCanMakePaymentResult = await request.canMakePayment();
+  } catch (err) {
+    assert_equals(
+      err.name,
+      'NotAllowedError',
+      'If it throws, then it must be NotAllowedError',
+    );
+  }
+  assert_false(
+    paymentRequestCanMakePaymentResult,
+    'canMakePayment() must return false.',
+  );
+  await promise_rejects(t, 'NotSupportedError', request.show());
+}, 'If a payment handler is not installed, then the payment method is not supported.');
+
+promise_test(async t => {
+  const methodName = window.location.origin + '/canMakePayment-false';
+  await registerApp(methodName);
+  const request = buildPaymentRequest(methodName);
+  assert_not_equals(request, undefined);
+  let paymentRequestCanMakePaymentResult;
+  try {
+    paymentRequestCanMakePaymentResult = await request.canMakePayment();
+  } catch (err) {
+    assert_equals(
+      err.name,
+      'NotAllowedError',
+      'If it throws, then it must be NotAllowedError',
+    );
+  }
+  assert_false(
+    paymentRequestCanMakePaymentResult,
+    'canMakePayment() must return false.',
+  );
+  await promise_rejects(t, 'NotSupportedError', request.show());
+}, 'If CanMakePaymentEvent.respondWith(false) is called, then the payment method is not supported.');
+
+promise_test(async t => {
+  const methodName = window.location.origin + '/canMakePayment-promise-false';
+  await registerApp(methodName);
+  const request = buildPaymentRequest(methodName);
+  assert_not_equals(request, undefined);
+  let paymentRequestCanMakePaymentResult;
+  try {
+    paymentRequestCanMakePaymentResult = await request.canMakePayment();
+  } catch (err) {
+    assert_equals(
+      err.name,
+      'NotAllowedError',
+      'If it throws, then it must be NotAllowedError',
+    );
+  }
+  assert_false(
+    paymentRequestCanMakePaymentResult,
+    'canMakePayment() must return false.',
+  );
+  await promise_rejects(t, 'NotSupportedError', request.show());
+}, 'If CanMakePaymentEvent.respondWith(Promise.resolve(false)) is called, then the payment method is not supported.');
+
+promise_test(async t => {
+  const methodName = window.location.origin + '/canMakePayment-true';
+  await registerApp(methodName);
+  const request = buildPaymentRequest(methodName);
+  assert_not_equals(request, undefined);
+  let paymentRequestCanMakePaymentResult;
+  try {
+    paymentRequestCanMakePaymentResult = await request.canMakePayment();
+  } catch (err) {
+    assert_equals(
+      err.name,
+      'NotAllowedError',
+      'If it throws, then it must be NotAllowedError',
+    );
+  }
+  assert_true(
+    paymentRequestCanMakePaymentResult,
+    'canMakePayment() must return true.',
+  );
+  const acceptPromise = request.show();
+  await request.abort();
+  await promise_rejects(t, 'AbortError', acceptPromise);
+}, 'If CanMakePaymentEvent.respondWith(true) is called, then the payment method is supported.');
+
+promise_test(async t => {
+  const methodName = window.location.origin + '/canMakePayment-promise-true';
+  await registerApp(methodName);
+  const request = buildPaymentRequest(methodName);
+  assert_not_equals(request, undefined);
+  let paymentRequestCanMakePaymentResult;
+  try {
+    paymentRequestCanMakePaymentResult = await request.canMakePayment();
+  } catch (err) {
+    assert_equals(
+      err.name,
+      'NotAllowedError',
+      'If it throws, then it must be NotAllowedError',
+    );
+  }
+  assert_true(
+    paymentRequestCanMakePaymentResult,
+    'canMakePayment() must return true.',
+  );
+  const acceptPromise = request.show();
+  await request.abort();
+  await promise_rejects(t, 'AbortError', acceptPromise);
+}, 'If CanMakePaymentEvent.respondWith(Promise.resolve(true)) is called, then the payment method is supported.');
+
+promise_test(async t => {
+  const methodName = window.location.origin + '/canMakePayment-custom-error';
+  await registerApp(methodName);
+  const request = buildPaymentRequest(methodName);
+  assert_not_equals(request, undefined);
+  let paymentRequestCanMakePaymentResult;
+  try {
+    paymentRequestCanMakePaymentResult = await request.canMakePayment();
+  } catch (err) {
+    assert_equals(
+      err.name,
+      'NotAllowedError',
+      'If it throws, then it must be NotAllowedError',
+    );
+  }
+  assert_false(
+    paymentRequestCanMakePaymentResult,
+    'canMakePayment() must return false.',
+  );
+  await promise_rejects(t, 'NotSupportedError', request.show());
+}, 'If CanMakePaymentEvent.respondWith(Promise.reject(error)) is called, then the payment method is not supported.');
+
+promise_test(async t => {
+  const methodName = 'basic-card';
+  await registerApp(methodName);
+  const request = buildPaymentRequest(methodName);
+  assert_not_equals(request, undefined);
+  let paymentRequestCanMakePaymentResult;
+  try {
+    paymentRequestCanMakePaymentResult = await request.canMakePayment();
+  } catch (err) {
+    assert_equals(
+      err.name,
+      'NotAllowedError',
+      'If it throws, then it must be NotAllowedError',
+    );
+  }
+  assert_true(
+    paymentRequestCanMakePaymentResult,
+    'canMakePayment() must return true due to capability matching in the browser.',
+  );
+}, 'If an app supports "basic-card" in general and that\'s what merchant requests as well, then capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card".');
+
+promise_test(async t => {
+  const methodName = 'basic-card';
+  await registerApp(methodName);
+  const cardType = 'prepaid';
+  const cardNetwork = 'mir';
+  const request = new PaymentRequest(
+    [
+      {
+        supportedMethods: methodName,
+        data: {
+          supportedTypes: [cardType],
+          supportedNetworks: [cardNetwork],
+        },
+      },
+    ],
+    {
+      total: {
+        label: 'Total',
+        amount: {
+          currency: 'USD',
+          value: '0',
+        },
+      },
+    },
+  );
+  assert_not_equals(request, undefined);
+  let paymentRequestCanMakePaymentResult;
+  try {
+    paymentRequestCanMakePaymentResult = await request.canMakePayment();
+  } catch (err) {
+    assert_equals(
+      err.name,
+      'NotAllowedError',
+      'If it throws, then it must be NotAllowedError',
+    );
+  }
+  assert_false(
+    paymentRequestCanMakePaymentResult,
+    'canMakePayment() must return false due to capability matching in the browser.',
+  );
+}, 'If an app has less specific "basic-card" capabilites than merchant\'s request, capability filtering should not make the app available for use. CanMakePaymentEvent should not be fired for "basic-card". ');
+
+promise_test(async t => {
+  const methodName = 'basic-card';
+  const cardType = 'prepaid';
+  const cardNetwork = 'mir';
+  const registration = await registerApp(methodName);
+  await registration.paymentManager.instruments.set(instrumentKey, {
+    name: 'Test Payment Method',
+    enabledMethods: [methodName],
+    capabilities: {
+      supportedTypes: [cardType],
+      supportedNetworks: [cardNetwork],
+    },
+  });
+  const request = new PaymentRequest(
+    [
+      {
+        supportedMethods: methodName,
+        data: {
+          supportedTypes: [cardType],
+          supportedNetworks: [cardNetwork],
+        },
+      },
+    ],
+    {
+      total: {
+        label: 'Total',
+        amount: {
+          currency: 'USD',
+          value: '0',
+        },
+      },
+    },
+  );
+  assert_not_equals(request, undefined);
+  let paymentRequestCanMakePaymentResult;
+  try {
+    paymentRequestCanMakePaymentResult = await request.canMakePayment();
+  } catch (err) {
+    assert_equals(
+      err.name,
+      'NotAllowedError',
+      'If it throws, then it must be NotAllowedError',
+    );
+  }
+  assert_true(
+    paymentRequestCanMakePaymentResult,
+    'canMakePayment() must return true due to capability matching in the browser.',
+  );
+}, 'If an app has the exact "basic-card" capabilities that a merchant requested, capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card".');
+
+promise_test(async t => {
+  const methodName = 'basic-card';
+  const cardType = 'prepaid';
+  const cardNetwork = 'mir';
+  const registration = await registerApp(methodName);
+  await registration.paymentManager.instruments.set(instrumentKey, {
+    name: 'Test Payment Method',
+    enabledMethods: [methodName],
+    capabilities: {
+      supportedTypes: [cardType],
+      supportedNetworks: [cardNetwork],
+    },
+  });
+  const request = buildPaymentRequest(methodName);
+  assert_not_equals(request, undefined);
+  let paymentRequestCanMakePaymentResult;
+  try {
+    paymentRequestCanMakePaymentResult = await request.canMakePayment();
+  } catch (err) {
+    assert_equals(
+      err.name,
+      'NotAllowedError',
+      'If it throws, then it must be NotAllowedError',
+    );
+  }
+  assert_true(
+    paymentRequestCanMakePaymentResult,
+    'canMakePayment() must return true due to capability matching in the browser.',
+  );
+}, 'If an app has more specific "basic-card" capabilities than merchant\'s request, capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card".');
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-handler/interfaces.https.any-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/interfaces.https.any-expected.txt
index acb6e796..face3c1 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/payment-handler/interfaces.https.any-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/interfaces.https.any-expected.txt
@@ -28,6 +28,7 @@
 PASS Unscopable handled correctly for set(DOMString, PaymentInstrument) on PaymentInstruments
 PASS PaymentInstruments interface: operation clear()
 PASS Unscopable handled correctly for clear() on PaymentInstruments
+PASS CanMakePaymentEvent interface: existence and properties of interface object
 PASS PaymentRequestEvent interface: existence and properties of interface object
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-handler/interfaces.https.any.js b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/interfaces.https.any.js
index 21f8d35..d0e2328 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/payment-handler/interfaces.https.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/interfaces.https.any.js
@@ -1,20 +1,21 @@
 // META: script=/resources/WebIDLParser.js
 // META: script=/resources/idlharness.js
 
-"use strict";
+'use strict';
 
 if (self.importScripts) {
-    importScripts("/resources/testharness.js");
-    importScripts("/resources/WebIDLParser.js", "/resources/idlharness.js");
+  importScripts('/resources/testharness.js');
+  importScripts('/resources/WebIDLParser.js', '/resources/idlharness.js');
 }
 
 // https://w3c.github.io/payment-handler/
 
-promise_test(async() => {
-    const text = await fetch("/interfaces/payment-handler.idl")
-        .then(response => response.text());
-    const idlArray = new IdlArray();
-    idlArray.add_idls(text);
-    idlArray.test();
-    done();
-}, "Payment handler interfaces.");
+promise_test(async () => {
+  const text = await fetch('/interfaces/payment-handler.idl').then(response =>
+    response.text(),
+  );
+  const idlArray = new IdlArray();
+  idlArray.add_idls(text);
+  idlArray.test();
+  done();
+}, 'Payment handler interfaces.');
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-handler/interfaces.https.any.worker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/interfaces.https.any.worker-expected.txt
index 234aac8..572c566 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/payment-handler/interfaces.https.any.worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/interfaces.https.any.worker-expected.txt
@@ -27,6 +27,7 @@
 PASS Unscopable handled correctly for set(DOMString, PaymentInstrument) on PaymentInstruments
 PASS PaymentInstruments interface: operation clear()
 PASS Unscopable handled correctly for clear() on PaymentInstruments
+PASS CanMakePaymentEvent interface: existence and properties of interface object
 PASS PaymentRequestEvent interface: existence and properties of interface object
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-handler/manifest.json b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/manifest.json
new file mode 100644
index 0000000..875d74b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-handler/manifest.json
@@ -0,0 +1,10 @@
+{
+  "name": "Test Payment Handler",
+  "icons": [
+    {
+      "src": "/images/rgrg-256x256.png",
+      "sizes": "256x256",
+      "type": "image/png"
+    }
+  ]
+}
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webauthn/interfaces.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webauthn/interfaces.https-expected.txt
index bdf8aea..74ed59f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webauthn/interfaces.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webauthn/interfaces.https-expected.txt
@@ -10,7 +10,7 @@
 PASS Unscopable handled correctly for rawId property on PublicKeyCredential
 PASS PublicKeyCredential interface: attribute response
 PASS Unscopable handled correctly for response property on PublicKeyCredential
-FAIL PublicKeyCredential interface: operation getClientExtensionResults() assert_own_property: interface prototype object missing non-static operation expected property "getClientExtensionResults" missing
+PASS PublicKeyCredential interface: operation getClientExtensionResults()
 PASS Unscopable handled correctly for getClientExtensionResults() on PublicKeyCredential
 PASS PublicKeyCredential interface: operation isUserVerifyingPlatformAuthenticatorAvailable()
 PASS Unscopable handled correctly for isUserVerifyingPlatformAuthenticatorAvailable() on PublicKeyCredential
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-animated-images.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-animated-images.html
index d8d131d..e35bd2a 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-animated-images.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-animated-images.html
@@ -30,14 +30,8 @@
 function loaded(img, name, i) {
   // Advance the image away from frame 0 to a frame that has black,
   // white and gray pixels, viz., no color pixels.
-  if (window.internals) {
-    for (var frame = 0; frame < 10; ++frame)
-      window.internals.advanceImageAnimation(img);
-    runTest();
-  } else {  // Manual test.
-    var noColorFrameDelayInMS = 1500;
-    setTimeout(runTest, noColorFrameDelayInMS);
-  }
+  var noColorFrameDelayInMS = 1500;
+  setTimeout(runTest, noColorFrameDelayInMS);
 
   function runTest() {
     requestAnimationFrame(function test() {
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/fs-breakpoint-can-be-removed-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/fs-breakpoint-can-be-removed-expected.txt
new file mode 100644
index 0000000..05af13f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/fs-breakpoint-can-be-removed-expected.txt
@@ -0,0 +1,83 @@
+Tests that breakpoint added to file system can be removed
+
+Add breakpoint
+Dumping breakpoint storage
+  file:///var/www/devtools/persistence/resources/foo.js:1
+Breakpoint sidebar pane 
+foo.js:2
+Source frame breakpoints
+breakpoint at 1
+
+Remove breakpoint
+Dumping breakpoint storage
+Breakpoint sidebar pane 
+No breakpoints
+Source frame breakpoints
+
+Add breakpoint again
+Dumping breakpoint storage
+  file:///var/www/devtools/persistence/resources/foo.js:1
+Breakpoint sidebar pane 
+foo.js:2
+Source frame breakpoints
+breakpoint at 1
+
+Add network mapping
+Dumping breakpoint storage
+  http://127.0.0.1:8000/devtools/persistence/resources/foo.js:1
+Breakpoint sidebar pane 
+foo.js:3window.foo = ()=>'foo';
+Source frame breakpoints
+breakpoint at 2
+  inline breakpoint at (2, 0)
+  inline breakpoint at (2, 17) disabled
+  inline breakpoint at (2, 22) disabled
+  inline breakpoint at (2, 23) disabled
+
+Remove breakpoint
+Dumping breakpoint storage
+Breakpoint sidebar pane 
+No breakpoints
+Source frame breakpoints
+
+Add breakpoint again
+Dumping breakpoint storage
+  http://127.0.0.1:8000/devtools/persistence/resources/foo.js:1
+Breakpoint sidebar pane 
+foo.js:3window.foo = ()=>'foo';
+Source frame breakpoints
+breakpoint at 2
+  inline breakpoint at (2, 0)
+  inline breakpoint at (2, 17) disabled
+  inline breakpoint at (2, 22) disabled
+  inline breakpoint at (2, 23) disabled
+
+Remove binding and drop iframe
+Dumping breakpoint storage
+  http://127.0.0.1:8000/devtools/persistence/resources/foo.js:1
+  file:///var/www/devtools/persistence/resources/foo.js:1
+Breakpoint sidebar pane 
+foo.js:2
+Source frame breakpoints
+breakpoint at 1
+
+Remove breakpoint
+Dumping breakpoint storage
+  http://127.0.0.1:8000/devtools/persistence/resources/foo.js:1
+Breakpoint sidebar pane 
+No breakpoints
+Source frame breakpoints
+
+Add mapping back
+Dumping breakpoint storage
+  http://127.0.0.1:8000/devtools/persistence/resources/foo.js:1
+Breakpoint sidebar pane 
+foo.js:3window.foo = ()=>'foo';
+Source frame breakpoints
+breakpoint at 2
+  inline breakpoint at (2, 0)
+  inline breakpoint at (2, 17) disabled
+  inline breakpoint at (2, 22) disabled
+  inline breakpoint at (2, 23) disabled
+
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/fs-breakpoint-can-be-removed-same-url-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/fs-breakpoint-can-be-removed-same-url-expected.txt
new file mode 100644
index 0000000..414daa4ffc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/fs-breakpoint-can-be-removed-same-url-expected.txt
@@ -0,0 +1,72 @@
+Tests that breakpoint added to file system can be removed when fs url === network url
+
+Add breakpoint
+Dumping breakpoint storage
+  file:///var/www/foo.js:1
+Breakpoint sidebar pane 
+foo.js:2
+Source frame breakpoints
+breakpoint at 1
+
+Remove breakpoint
+Dumping breakpoint storage
+Breakpoint sidebar pane 
+No breakpoints
+Source frame breakpoints
+
+Add breakpoint again
+Dumping breakpoint storage
+  file:///var/www/foo.js:1
+Breakpoint sidebar pane 
+foo.js:2
+Source frame breakpoints
+breakpoint at 1
+
+Add network mapping
+Dumping breakpoint storage
+  file:///var/www/foo.js:1
+Breakpoint sidebar pane 
+foo.js:3  window.foo = () => 42;
+Source frame breakpoints
+breakpoint at 2
+  inline breakpoint at (2, 2)
+  inline breakpoint at (2, 21) disabled
+  inline breakpoint at (2, 23) disabled
+
+Remove breakpoint
+Dumping breakpoint storage
+Breakpoint sidebar pane 
+No breakpoints
+Source frame breakpoints
+
+Add breakpoint again
+Dumping breakpoint storage
+  file:///var/www/foo.js:1
+Breakpoint sidebar pane 
+foo.js:3  window.foo = () => 42;
+Source frame breakpoints
+breakpoint at 2
+  inline breakpoint at (2, 2)
+  inline breakpoint at (2, 21) disabled
+  inline breakpoint at (2, 23) disabled
+
+Remove binding and drop iframe
+Dumping breakpoint storage
+  file:///var/www/foo.js:1
+Breakpoint sidebar pane 
+foo.js:2
+Source frame breakpoints
+breakpoint at 1
+
+Remove breakpoint
+Dumping breakpoint storage
+Breakpoint sidebar pane 
+No breakpoints
+Source frame breakpoints
+
+Add mapping back
+Dumping breakpoint storage
+Breakpoint sidebar pane 
+No breakpoints
+Source frame breakpoints
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/fs-breakpoint-can-be-removed-same-url.js b/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/fs-breakpoint-can-be-removed-same-url.js
new file mode 100644
index 0000000..e6f20d44
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/fs-breakpoint-can-be-removed-same-url.js
@@ -0,0 +1,102 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(
+      `Tests that breakpoint added to file system can be removed when fs url === network url\n`);
+  await TestRunner.loadModule('sources_test_runner');
+  await TestRunner.loadModule('bindings_test_runner');
+  await TestRunner.showPanel('sources');
+
+  const testMapping = BindingsTestRunner.initializeTestMapping();
+  const fs = new BindingsTestRunner.TestFileSystem('file:///var/www');
+
+  fs.root.addFile('foo.js', `function boo() {
+
+  window.foo = () => 42;
+}`);
+
+  await fs.reportCreatedPromise();
+  const fsUISourceCode = await TestRunner.waitForUISourceCode(
+      'foo.js', Workspace.projectTypes.FileSystem);
+  const fsSourceFrame =
+      await SourcesTestRunner.showUISourceCodePromise(fsUISourceCode);
+  TestRunner.addResult('Add breakpoint');
+  SourcesTestRunner.setBreakpoint(fsSourceFrame, 1, '', true);
+  await waitAndDumpBreakpointsState(fsSourceFrame);
+
+  TestRunner.addResult('Remove breakpoint');
+  SourcesTestRunner.toggleBreakpoint(fsSourceFrame, 1, false);
+  await waitAndDumpBreakpointsState(fsSourceFrame);
+
+  TestRunner.addResult('Add breakpoint again');
+  SourcesTestRunner.setBreakpoint(fsSourceFrame, 1, '', true);
+  await waitAndDumpBreakpointsState(fsSourceFrame);
+
+  TestRunner.addResult('Add network mapping');
+  await TestRunner.addIframe(
+      'resources/frame-with-foo-js-with-source-url.html');
+  testMapping.addBinding('foo.js');
+  await BindingsTestRunner.waitForBinding('foo.js');
+  let networkUISourceCode = await TestRunner.waitForUISourceCode(
+      'foo.js', Workspace.projectTypes.Network);
+  let networkSourceFrame =
+      await SourcesTestRunner.showUISourceCodePromise(networkUISourceCode);
+  await waitAndDumpBreakpointsState(networkSourceFrame);
+
+  TestRunner.addResult('Remove breakpoint');
+  SourcesTestRunner.toggleBreakpoint(networkSourceFrame, 2, false);
+  await waitAndDumpBreakpointsState(networkSourceFrame);
+
+  TestRunner.addResult('Add breakpoint again');
+  SourcesTestRunner.setBreakpoint(networkSourceFrame, 1, '', true);
+  await waitAndDumpBreakpointsState(networkSourceFrame);
+
+  TestRunner.addResult('Remove binding and drop iframe');
+  await TestRunner.evaluateInPageAsync(`
+    (function(){
+      const frame = document.querySelector('iframe');
+      frame.parentNode.removeChild(frame);
+    })();
+  `);
+  await testMapping.removeBinding('foo.js');
+  await waitAndDumpBreakpointsState(fsSourceFrame);
+
+  TestRunner.addResult('Remove breakpoint');
+  SourcesTestRunner.toggleBreakpoint(fsSourceFrame, 1, false);
+  await waitAndDumpBreakpointsState(fsSourceFrame);
+
+  TestRunner.addResult('Add mapping back');
+  await TestRunner.addIframe(
+      'resources/frame-with-foo-js-with-source-url.html');
+  testMapping.addBinding('foo.js');
+  await BindingsTestRunner.waitForBinding('foo.js');
+  networkUISourceCode = await TestRunner.waitForUISourceCode(
+      'foo.js', Workspace.projectTypes.Network);
+  networkSourceFrame =
+      await SourcesTestRunner.showUISourceCodePromise(networkUISourceCode);
+  dumpBreakpointStorage();
+  SourcesTestRunner.dumpBreakpointSidebarPane();
+  TestRunner.addResult('Source frame breakpoints');
+  SourcesTestRunner.dumpJavaScriptSourceFrameBreakpoints(networkSourceFrame);
+
+  TestRunner.completeTest();
+
+  function dumpBreakpointStorage() {
+    var breakpointManager = Bindings.breakpointManager;
+    var breakpoints = breakpointManager._storage._setting.get();
+    TestRunner.addResult('Dumping breakpoint storage');
+    for (const {lineNumber, url} of breakpoints)
+      TestRunner.addResult(`  ${url}:${lineNumber}`);
+  }
+
+  async function waitAndDumpBreakpointsState(sourceFrame) {
+    await SourcesTestRunner.waitBreakpointSidebarPane();
+    dumpBreakpointStorage();
+    SourcesTestRunner.dumpBreakpointSidebarPane();
+    TestRunner.addResult('Source frame breakpoints');
+    SourcesTestRunner.dumpJavaScriptSourceFrameBreakpoints(sourceFrame);
+    TestRunner.addResult('');
+  }
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/fs-breakpoint-can-be-removed.js b/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/fs-breakpoint-can-be-removed.js
new file mode 100644
index 0000000..dd7c1b0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/fs-breakpoint-can-be-removed.js
@@ -0,0 +1,98 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(
+      `Tests that breakpoint added to file system can be removed\n`);
+  await TestRunner.loadModule('sources_test_runner');
+  await TestRunner.loadModule('bindings_test_runner');
+  await TestRunner.showPanel('sources');
+
+  const testMapping = BindingsTestRunner.initializeTestMapping();
+  const fs = new BindingsTestRunner.TestFileSystem('file:///var/www');
+  const fsEntry = BindingsTestRunner.addFooJSFile(fs);
+  await fs.reportCreatedPromise();
+  const fsUISourceCode = await TestRunner.waitForUISourceCode(
+      'foo.js', Workspace.projectTypes.FileSystem);
+  const fsSourceFrame =
+      await SourcesTestRunner.showUISourceCodePromise(fsUISourceCode);
+  TestRunner.addResult('Add breakpoint');
+  SourcesTestRunner.setBreakpoint(fsSourceFrame, 1, '', true);
+  await waitAndDumpBreakpointsState(fsSourceFrame);
+
+  TestRunner.addResult('Remove breakpoint');
+  SourcesTestRunner.toggleBreakpoint(fsSourceFrame, 1, false);
+  await waitAndDumpBreakpointsState(fsSourceFrame);
+
+  TestRunner.addResult('Add breakpoint again');
+  SourcesTestRunner.setBreakpoint(fsSourceFrame, 1, '', true);
+  await waitAndDumpBreakpointsState(fsSourceFrame);
+
+  // TODO(kozyatinskiy): after this step we should have both breakpoints by fs
+  // url and by network url in storage otherwise if we close DevTools right now
+  // and reopen it later without established mapping then we won't show
+  // breakpoints in fileSystem UISourceCode.
+  TestRunner.addResult('Add network mapping');
+  await TestRunner.addIframe('resources/frame-with-foo-js.html');
+  testMapping.addBinding('foo.js');
+  await BindingsTestRunner.waitForBinding('foo.js');
+  let networkUISourceCode = await TestRunner.waitForUISourceCode(
+      'foo.js', Workspace.projectTypes.Network);
+  let networkSourceFrame =
+      await SourcesTestRunner.showUISourceCodePromise(networkUISourceCode);
+  await waitAndDumpBreakpointsState(networkSourceFrame);
+
+  TestRunner.addResult('Remove breakpoint');
+  SourcesTestRunner.toggleBreakpoint(networkSourceFrame, 2, false);
+  await waitAndDumpBreakpointsState(networkSourceFrame);
+
+  TestRunner.addResult('Add breakpoint again');
+  SourcesTestRunner.setBreakpoint(networkSourceFrame, 1, '', true);
+  await waitAndDumpBreakpointsState(networkSourceFrame);
+
+  TestRunner.addResult('Remove binding and drop iframe');
+  await testMapping.removeBinding('foo.js');
+  TestRunner.evaluateInPageAsync(`
+    (function(){
+      const frame = document.querySelector('iframe');
+      frame.parentNode.removeChild(frame);
+    })();
+  `);
+  await waitAndDumpBreakpointsState(fsSourceFrame);
+
+  // TODO(kozyatinskiy): after this step we should have no breakpoints in
+  // storage
+  TestRunner.addResult('Remove breakpoint');
+  SourcesTestRunner.toggleBreakpoint(fsSourceFrame, 1, false);
+  await waitAndDumpBreakpointsState(fsSourceFrame);
+
+  TestRunner.addResult('Add mapping back');
+  TestRunner.addIframe('resources/frame-with-foo-js.html');
+  testMapping.addBinding('foo.js');
+  await BindingsTestRunner.waitForBinding('foo.js');
+  networkUISourceCode = await TestRunner.waitForUISourceCode(
+      'foo.js', Workspace.projectTypes.Network);
+  networkSourceFrame =
+      await SourcesTestRunner.showUISourceCodePromise(networkUISourceCode);
+  await waitAndDumpBreakpointsState(networkSourceFrame);
+
+  TestRunner.completeTest();
+
+  function dumpBreakpointStorage() {
+    var breakpointManager = Bindings.breakpointManager;
+    var breakpoints = breakpointManager._storage._setting.get();
+    TestRunner.addResult('Dumping breakpoint storage');
+    for (const {lineNumber, url} of breakpoints)
+      TestRunner.addResult(`  ${url}:${lineNumber}`);
+  }
+
+  async function waitAndDumpBreakpointsState(sourceFrame) {
+    await SourcesTestRunner.waitBreakpointSidebarPane();
+    dumpBreakpointStorage();
+    SourcesTestRunner.dumpBreakpointSidebarPane();
+    TestRunner.addResult('Source frame breakpoints');
+    SourcesTestRunner.dumpJavaScriptSourceFrameBreakpoints(sourceFrame);
+    TestRunner.addResult('');
+  }
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/resources/frame-with-foo-js-with-source-url.html b/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/resources/frame-with-foo-js-with-source-url.html
new file mode 100644
index 0000000..9ca3f62
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/resources/frame-with-foo-js-with-source-url.html
@@ -0,0 +1,7 @@
+<script>
+  eval(`function boo() {
+
+  window.foo = () => 42;
+}
+//# sourceURL=file:///var/www/foo.js`);
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/resources/frame-with-foo-js.html b/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/resources/frame-with-foo-js.html
new file mode 100644
index 0000000..f98bf712
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/resources/frame-with-foo-js.html
@@ -0,0 +1 @@
+<script src='foo.js'></script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/report-script-tag-cross-origin-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/report-script-tag-cross-origin-expected.txt
index f991b24..b0905206 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/report-script-tag-cross-origin-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/report-script-tag-cross-origin-expected.txt
@@ -1,5 +1,4 @@
-CONSOLE ERROR: Error parsing header X-XSS-Protection: 1; report=http://localhost:8080/security/contentSecurityPolicy/resources/save-report.php?test=report-script-tag.html: reporting URL is not same scheme, host, and port as page at character position 10. The default protections will be applied.
-CONSOLE ERROR: line 4: The XSS Auditor blocked access to 'http://127.0.0.1:8000/security/xssAuditor/resources/echo-intertag.pl?test=report-script-tag.html&echo-report=1&enable-report-cross-origin=1&q=%3Cscript%3Ealert(String.fromCharCode(0x58,0x53,0x53))%3C/script%3E%3Cp%3EIf%20you%20see%20this%20message,%20no%20JavaScript%20alert(),%20and%20not%20dump%20of%20a%20report%20is%20displayed%20below,%20then%20the%20test%20PASSED.%3C/p%3E' because the source code of a script was found within the request. The server sent an 'X-XSS-Protection' header requesting this behavior.
+CONSOLE ERROR: line 4: The XSS Auditor refused to execute a script in 'http://127.0.0.1:8000/security/xssAuditor/resources/echo-intertag.pl?test=report-script-tag.html&echo-report=1&enable-report-cross-origin=1&q=%3Cscript%3Ealert(String.fromCharCode(0x58,0x53,0x53))%3C/script%3E%3Cp%3EIf%20you%20see%20this%20message,%20no%20JavaScript%20alert(),%20and%20not%20dump%20of%20a%20report%20is%20displayed%20below,%20then%20the%20test%20PASSED.%3C/p%3E' because its source code was found within the request. The server sent an 'X-XSS-Protection' header requesting this behavior.
 This tests that the X-XSS-Protection reports are sent out properly
 
 
@@ -7,5 +6,9 @@
 --------
 Frame: 'frame'
 --------
-Could not load the requested resource.
-Error code: -28 (net::ERR_BLOCKED_BY_XSS_AUDITOR)
+CSP report received:
+CONTENT_TYPE: application/xss-auditor-report
+HTTP_REFERER: http://127.0.0.1:8000/security/xssAuditor/resources/echo-intertag.pl?test=report-script-tag.html&echo-report=1&enable-report-cross-origin=1&q=%3Cscript%3Ealert(String.fromCharCode(0x58,0x53,0x53))%3C/script%3E%3Cp%3EIf%20you%20see%20this%20message,%20no%20JavaScript%20alert(),%20and%20not%20dump%20of%20a%20report%20is%20displayed%20below,%20then%20the%20test%20PASSED.%3C/p%3E
+REQUEST_METHOD: POST
+=== POST DATA ===
+{"xss-report":{"request-url":"http://127.0.0.1:8000/security/xssAuditor/resources/echo-intertag.pl?test=report-script-tag.html&echo-report=1&enable-report-cross-origin=1&q=%3Cscript%3Ealert(String.fromCharCode(0x58,0x53,0x53))%3C/script%3E%3Cp%3EIf%20you%20see%20this%20message,%20no%20JavaScript%20alert(),%20and%20not%20dump%20of%20a%20report%20is%20displayed%20below,%20then%20the%20test%20PASSED.%3C/p%3E","request-body":""}}
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/report-script-tag-cross-origin-https-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/report-script-tag-cross-origin-https-expected.txt
index 5c416e29..784cae1 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/report-script-tag-cross-origin-https-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/report-script-tag-cross-origin-https-expected.txt
@@ -1,5 +1,4 @@
-CONSOLE ERROR: Error parsing header X-XSS-Protection: 1; report=http://localhost:8080/security/contentSecurityPolicy/resources/save-report.php?test=report-script-tag.html: reporting URL is not same scheme, host, and port as page at character position 10. The default protections will be applied.
-CONSOLE ERROR: line 4: The XSS Auditor blocked access to 'https://127.0.0.1:8443/security/xssAuditor/resources/echo-intertag.pl?test=report-script-tag.html&echo-report=1&enable-report-cross-origin=1&q=%3Cscript%3Ealert(String.fromCharCode(0x58,0x53,0x53))%3C/script%3E%3Cp%3EIf%20you%20see%20this%20message,%20no%20JavaScript%20alert(),%20and%20not%20dump%20of%20a%20report%20is%20displayed%20below,%20then%20the%20test%20PASSED.%3C/p%3E' because the source code of a script was found within the request. The server sent an 'X-XSS-Protection' header requesting this behavior.
+CONSOLE ERROR: line 4: The XSS Auditor refused to execute a script in 'https://127.0.0.1:8443/security/xssAuditor/resources/echo-intertag.pl?test=report-script-tag.html&echo-report=1&enable-report-cross-origin=1&q=%3Cscript%3Ealert(String.fromCharCode(0x58,0x53,0x53))%3C/script%3E%3Cp%3EIf%20you%20see%20this%20message,%20no%20JavaScript%20alert(),%20and%20not%20dump%20of%20a%20report%20is%20displayed%20below,%20then%20the%20test%20PASSED.%3C/p%3E' because its source code was found within the request. The server sent an 'X-XSS-Protection' header requesting this behavior.
 This tests that the X-XSS-Protection reports are sent out properly
 
 
@@ -7,5 +6,8 @@
 --------
 Frame: 'frame'
 --------
-Could not load the requested resource.
-Error code: -28 (net::ERR_BLOCKED_BY_XSS_AUDITOR)
+CSP report received:
+CONTENT_TYPE: application/xss-auditor-report
+REQUEST_METHOD: POST
+=== POST DATA ===
+{"xss-report":{"request-url":"https://127.0.0.1:8443/security/xssAuditor/resources/echo-intertag.pl?test=report-script-tag.html&echo-report=1&enable-report-cross-origin=1&q=%3Cscript%3Ealert(String.fromCharCode(0x58,0x53,0x53))%3C/script%3E%3Cp%3EIf%20you%20see%20this%20message,%20no%20JavaScript%20alert(),%20and%20not%20dump%20of%20a%20report%20is%20displayed%20below,%20then%20the%20test%20PASSED.%3C/p%3E","request-body":""}}
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/report-script-tag-cross-origin-https.html b/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/report-script-tag-cross-origin-https.html
index 0836a9b..b6f0a2e 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/report-script-tag-cross-origin-https.html
+++ b/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/report-script-tag-cross-origin-https.html
@@ -9,17 +9,11 @@
     testRunner.waitUntilDone();
     testRunner.setXSSAuditorEnabled(true);
 }
-
-function notify() {
-  if (window.testRunner) {
-    setTimeout(testRunner.notifyDone.bind(testRunner), 0);
-  }
-}
 </script>
 </head>
 <body>
 <p>This tests that the X-XSS-Protection reports are sent out properly</p>
-<iframe onload="notify()" name="frame" src="https://127.0.0.1:8443/security/xssAuditor/resources/echo-intertag.pl?test=report-script-tag.html&echo-report=1&enable-report-cross-origin=1&q=<script>alert(String.fromCharCode(0x58,0x53,0x53))</script><p>If you see this message, no JavaScript alert(), and not dump of a report is displayed below, then the test PASSED.</p>">
+<iframe name="frame" src="https://127.0.0.1:8443/security/xssAuditor/resources/echo-intertag.pl?test=report-script-tag.html&echo-report=1&enable-report-cross-origin=1&q=<script>alert(String.fromCharCode(0x58,0x53,0x53))</script><p>If you see this message, no JavaScript alert(), and not dump of a report is displayed below, then the test PASSED.</p>">
 </iframe>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background-expected.txt
deleted file mode 100644
index ecbc1a3..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background-expected.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [785, 585],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [2008, 2016],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutBlockFlow DIV id='targetImage'",
-          "rect": [8, 8, 50, 50],
-          "reason": "full"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV id='targetImage'",
-      "reason": "full"
-    },
-    {
-      "object": "LayoutImage IMG id='testTarget'",
-      "reason": "full"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background-offscreen-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background-offscreen-expected.txt
deleted file mode 100644
index 8dcf038..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background-offscreen-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [785, 585],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [2008, 2016],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "transform": 1
-    }
-  ],
-  "transforms": [
-    {
-      "id": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [0, -1000, 0, 1]
-      ],
-      "flattenInheritedTransform": false
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background-offscreen.html b/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background-offscreen.html
deleted file mode 100644
index 5de99b3..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background-offscreen.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<!doctype HTML>
-<body>
-<div style="height: 2000px; width: 2000px;">
-    <div id="targetImage" style="width: 50px; height: 50px"></div>
-    <img id="testTarget" style="visibility: hidden">
-</div>
-
-<script src="../resources/text-based-repaint.js"></script>
-<script>
-window.testIsAsync = true;
-
-// Disable under-invalidation checking because the "under-invalidation" of
-// offscreen gif animation is intentional.
-if (window.internals)
-    internals.runtimeFlags.paintUnderInvalidationCheckingEnabled = false;
-
-function repaintTest() {
-    if (window.internals)
-        internals.advanceImageAnimation(testTarget);
-    requestAnimationFrame(function() {
-        finishRepaintTest();
-    });
-}
-
-function targetImageOnload() {
-    // Scroll targetImage offscreen.
-    window.scrollTo(0, 1000);
-    runRepaintTest();
-}
-
-window.onload = function() {
-    targetImage.style.background = "url(../../../fast/backgrounds/resources/red-green-animated.gif)";
-
-    // Use a parallel image element as a hack to detect whether the image has loaded, and therefore
-    // we'll get a frame.
-    testTarget.onload = targetImageOnload;
-    testTarget.src = "../../../fast/backgrounds/resources/red-green-animated.gif";
-}
-</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background.html b/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background.html
deleted file mode 100644
index 40e703c6..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!doctype HTML>
-<body>
-<div style="height: 2000px; width: 2000px;">
-    <div id="targetImage" style="width: 50px; height: 50px"></div>
-    <img id="testTarget" style="visibility: hidden">
-</div>
-<script src="../resources/text-based-repaint.js"></script>
-<script>
-window.testIsAsync = true;
-
-function repaintTest() {
-    if (window.internals)
-        internals.advanceImageAnimation(testTarget);
-    requestAnimationFrame(function() {
-        finishRepaintTest();
-    });
-}
-
-onload = function() {
-    targetImage.style.background = "url(../../../fast/backgrounds/resources/red-green-animated.gif)";
-
-    // Use a parallel image element has a hack to detect whether the image has loaded, and therefore
-    // we'll get a frame.
-    testTarget.onload = runRepaintTest;
-    testTarget.src = "../../../fast/backgrounds/resources/red-green-animated.gif";
-}
-</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-png-background-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-png-background-expected.txt
deleted file mode 100644
index ecbc1a3..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-png-background-expected.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [785, 585],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [2008, 2016],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutBlockFlow DIV id='targetImage'",
-          "rect": [8, 8, 50, 50],
-          "reason": "full"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV id='targetImage'",
-      "reason": "full"
-    },
-    {
-      "object": "LayoutImage IMG id='testTarget'",
-      "reason": "full"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-png-background.html b/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-png-background.html
deleted file mode 100644
index 65868bb..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-png-background.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!doctype HTML>
-<body>
-<div style="height: 2000px; width: 2000px;">
-    <div id="targetImage" style="width: 50px; height: 50px"></div>
-    <img id="testTarget" style="visibility: hidden">
-</div>
-<script src="../resources/text-based-repaint.js"></script>
-<script>
-window.testIsAsync = true;
-
-function repaintTest() {
-    if (window.internals)
-        internals.advanceImageAnimation(testTarget);
-    requestAnimationFrame(function() {
-        finishRepaintTest();
-    });
-}
-
-onload = function() {
-    targetImage.style.background = "url(../../../fast/backgrounds/resources/red-green-animated.png)";
-
-    // Use a parallel image element has a hack to detect whether the image has loaded, and therefore
-    // we'll get a frame.
-    testTarget.onload = runRepaintTest;
-    testTarget.src = "../../../fast/backgrounds/resources/red-green-animated.png";
-}
-</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background-offscreen-firstline-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-svg-background-offscreen-firstline-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background-offscreen-firstline-expected.txt
rename to third_party/WebKit/LayoutTests/paint/invalidation/background/animated-svg-background-offscreen-firstline-expected.txt
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background-offscreen-firstline.html b/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-svg-background-offscreen-firstline.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background-offscreen-firstline.html
rename to third_party/WebKit/LayoutTests/paint/invalidation/background/animated-svg-background-offscreen-firstline.html
index 30e282b2..2383a00 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-gif-background-offscreen-firstline.html
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-svg-background-offscreen-firstline.html
@@ -29,11 +29,11 @@
 }
 
 onload = function() {
-    targetImage.style.background = "url(../../../fast/backgrounds/resources/red-green-animated.gif)";
+    targetImage.style.background = "url(../../../svg/as-image/resources/animated-rect-fixed-size-2.svg)";
 
     // Use a parallel image element as a hack to detect whether the image has loaded, and therefore
     // we'll get a frame.
     testTarget.onload = targetImageOnload;
-    testTarget.src = "../../../fast/backgrounds/resources/red-green-animated.gif";
+    testTarget.src = "../../../svg/as-image/resources/animated-rect-fixed-size-2.svg";
 }
 </script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-webp-background-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-webp-background-expected.txt
deleted file mode 100644
index ecbc1a3..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-webp-background-expected.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [785, 585],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [2008, 2016],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutBlockFlow DIV id='targetImage'",
-          "rect": [8, 8, 50, 50],
-          "reason": "full"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV id='targetImage'",
-      "reason": "full"
-    },
-    {
-      "object": "LayoutImage IMG id='testTarget'",
-      "reason": "full"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-webp-background.html b/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-webp-background.html
deleted file mode 100644
index 28e6ef8..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/background/animated-webp-background.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!doctype HTML>
-<body>
-<div style="height: 2000px; width: 2000px;">
-    <div id="targetImage" style="width: 50px; height: 50px"></div>
-    <img id="testTarget" style="visibility: hidden">
-</div>
-<script src="../resources/text-based-repaint.js"></script>
-<script>
-window.testIsAsync = true;
-
-function repaintTest() {
-    if (window.internals)
-        internals.advanceImageAnimation(testTarget);
-    requestAnimationFrame(function() {
-        finishRepaintTest();
-    });
-}
-
-onload = function() {
-    targetImage.style.background = "url(../../../fast/backgrounds/resources/red-green-animated.webp)";
-
-    // Use a parallel image element as a hack to detect whether the image has loaded, and therefore
-    // we'll get a frame.
-    testTarget.onload = runRepaintTest;
-    testTarget.src = "../../../fast/backgrounds/resources/red-green-animated.webp";
-}
-</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/background/obscured-background-no-repaint.html b/third_party/WebKit/LayoutTests/paint/invalidation/background/obscured-background-no-repaint.html
index 5b4370c2..8bfe36d 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/background/obscured-background-no-repaint.html
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/background/obscured-background-no-repaint.html
@@ -6,7 +6,7 @@
         width: 100px;
     }
     #test1 .parent {
-        background-image: url(../resources/animated.gif)
+        background-image: url(../svg/resources/animated-svg-fixed-size.svg)
     }
     #test1 .child {
         background-color: green;
@@ -16,7 +16,7 @@
         position: relative;
         height: 100px;
         width: 100px;
-        background-image: url(../resources/animated.gif);
+        background-image: url(../svg/resources/animated-svg-fixed-size.svg);
         background-repeat: no-repeat;
         background-position: center;
     }
@@ -30,7 +30,7 @@
         width: 50px;
     }
     #test3 img {
-        background-image: url(../resources/animated.gif)
+        background-image: url(../svg/resources/animated-svg-fixed-size.svg)
     }
     #test4 .parent {
         position: relative;
@@ -39,7 +39,7 @@
         background-color: red;
         background-repeat: no-repeat;
         background-position: center;
-        background-image: url(../resources/animated.gif)
+        background-image: url(../svg/resources/animated-svg-fixed-size.svg)
     }
 </style>
 <script>
@@ -124,7 +124,7 @@
         </a>
     </div>
 </div>
-<img id="imgForAdvanceImageAnimation" src="../resources/animated.gif">
+<img id="imgForAdvanceImageAnimation" src="../svg/resources/animated-svg-fixed-size.svg">
 <pre id="output"></pre>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-expected.txt
deleted file mode 100644
index e38b0aeb..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [785, 585],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [2008, 2016],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutImage IMG id='targetImage'",
-          "rect": [8, 8, 50, 50],
-          "reason": "full"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutImage IMG id='targetImage'",
-      "reason": "full"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-offscreen-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-offscreen-expected.txt
deleted file mode 100644
index 8dcf038..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-offscreen-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [785, 585],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [2008, 2016],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "transform": 1
-    }
-  ],
-  "transforms": [
-    {
-      "id": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [0, -1000, 0, 1]
-      ],
-      "flattenInheritedTransform": false
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-offscreen.html b/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-offscreen.html
deleted file mode 100644
index 01ea253bd..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-offscreen.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!doctype HTML>
-<body>
-<div style="height: 2000px; width: 2000px;">
-    <img id="targetImage">
-</div>
-<script src="../resources/text-based-repaint.js"></script>
-<script>
-window.testIsAsync = true;
-
-function repaintTest() {
-    if (window.internals)
-        internals.advanceImageAnimation(targetImage);
-    requestAnimationFrame(function() {
-        finishRepaintTest();
-    });
-}
-
-function targetImageOnload() {
-    // Scroll targetImage offscreen.
-    window.scrollTo(0, 1000);
-    runRepaintTest();
-}
-
-onload = function() {
-    targetImage.onload = targetImageOnload;
-    targetImage.src = "../../../fast/backgrounds/resources/red-green-animated.gif";
-}
-</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-transformed-offscreen-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-transformed-offscreen-expected.txt
deleted file mode 100644
index 284c617..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-transformed-offscreen-expected.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [785, 585],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [2008, 2016],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "LayoutBlockFlow DIV id='targetDiv'",
-      "bounds": [2000, 2000],
-      "transform": 2
-    }
-  ],
-  "transforms": [
-    {
-      "id": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [8, 8, 0, 1]
-      ]
-    },
-    {
-      "id": 2,
-      "parent": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [0, -1000, 0, 1]
-      ]
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-transformed-offscreen.html b/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-transformed-offscreen.html
deleted file mode 100644
index cdf29217..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif-transformed-offscreen.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!doctype HTML>
-<body>
-<div id="targetDiv" style="height: 2000px; width: 2000px; will-change: transform">
-    <img id="targetImage">
-</div>
-<script src="../resources/text-based-repaint.js"></script>
-<script>
-window.testIsAsync = true;
-
-function repaintTest() {
-    if (window.internals)
-        internals.advanceImageAnimation(targetImage);
-    requestAnimationFrame(function() {
-        finishRepaintTest();
-    });
-}
-
-function targetImageOnload() {
-    targetDiv.style.transform = "translateY(-1000px)";
-    runRepaintTest();
-}
-
-window.onload = function() {
-    targetImage.onload = targetImageOnload;
-    targetImage.src="../../../fast/backgrounds/resources/red-green-animated.gif";
-}
-</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif.html b/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif.html
deleted file mode 100644
index 5c69f8e..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-gif.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!doctype HTML>
-<body>
-<div style="height: 2000px; width: 2000px;">
-    <img id="targetImage">
-</div>
-<script src="../resources/text-based-repaint.js"></script>
-<script>
-window.testIsAsync = true;
-
-function repaintTest() {
-    if (window.internals)
-        internals.advanceImageAnimation(targetImage);
-    requestAnimationFrame(function() {
-        finishRepaintTest();
-    });
-}
-
-window.onload = function() {
-    targetImage.onload = runRepaintTest;
-    targetImage.src="../../../fast/backgrounds/resources/red-green-animated.gif";
-}
-</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-png-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-png-expected.txt
deleted file mode 100644
index e38b0aeb..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-png-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [785, 585],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [2008, 2016],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutImage IMG id='targetImage'",
-          "rect": [8, 8, 50, 50],
-          "reason": "full"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutImage IMG id='targetImage'",
-      "reason": "full"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-png-offscreen-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-png-offscreen-expected.txt
deleted file mode 100644
index 8dcf038..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-png-offscreen-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [785, 585],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [2008, 2016],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "transform": 1
-    }
-  ],
-  "transforms": [
-    {
-      "id": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [0, -1000, 0, 1]
-      ],
-      "flattenInheritedTransform": false
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-png-offscreen.html b/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-png-offscreen.html
deleted file mode 100644
index 1eba632..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-png-offscreen.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!doctype HTML>
-<body>
-<div style="height: 2000px; width: 2000px;">
-    <img id="targetImage">
-</div>
-<script src="../resources/text-based-repaint.js"></script>
-<script>
-window.testIsAsync = true;
-
-function repaintTest() {
-    if (window.internals)
-        internals.advanceImageAnimation(targetImage);
-    requestAnimationFrame(function() {
-        finishRepaintTest();
-    });
-}
-
-function targetImageOnload() {
-    // Scroll targetImage offscreen.
-    window.scrollTo(0, 1000);
-    runRepaintTest();
-}
-
-onload = function() {
-    targetImage.onload = targetImageOnload;
-    targetImage.src = "../../../fast/backgrounds/resources/red-green-animated.png";
-}
-</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-png.html b/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-png.html
deleted file mode 100644
index 3ea17e4..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-png.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!doctype HTML>
-<body>
-<div style="height: 2000px; width: 2000px;">
-    <img id="targetImage">
-</div>
-<script src="../resources/text-based-repaint.js"></script>
-<script>
-window.testIsAsync = true;
-
-function repaintTest() {
-    if (window.internals)
-        internals.advanceImageAnimation(targetImage);
-    requestAnimationFrame(function() {
-        finishRepaintTest();
-    });
-}
-
-window.onload = function() {
-    targetImage.onload = runRepaintTest;
-    targetImage.src="../../../fast/backgrounds/resources/red-green-animated.png";
-}
-</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-webp-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-webp-expected.txt
deleted file mode 100644
index e38b0aeb..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-webp-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [785, 585],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [2008, 2016],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutImage IMG id='targetImage'",
-          "rect": [8, 8, 50, 50],
-          "reason": "full"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutImage IMG id='targetImage'",
-      "reason": "full"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-webp-offscreen-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-webp-offscreen-expected.txt
deleted file mode 100644
index 8dcf038..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-webp-offscreen-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [785, 585],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [2008, 2016],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "transform": 1
-    }
-  ],
-  "transforms": [
-    {
-      "id": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [0, -1000, 0, 1]
-      ],
-      "flattenInheritedTransform": false
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-webp-offscreen.html b/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-webp-offscreen.html
deleted file mode 100644
index ff92dde..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-webp-offscreen.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!doctype HTML>
-<body>
-<div style="height: 2000px; width: 2000px;">
-    <img id="targetImage">
-</div>
-<script src="../resources/text-based-repaint.js"></script>
-<script>
-window.testIsAsync = true;
-
-function repaintTest() {
-    if (window.internals)
-        internals.advanceImageAnimation(targetImage);
-    requestAnimationFrame(function() {
-        finishRepaintTest();
-    });
-}
-
-function targetImageOnload() {
-    // Scroll targetImage offscreen.
-    window.scrollTo(0, 1000);
-    runRepaintTest();
-}
-
-onload = function() {
-    targetImage.onload = targetImageOnload;
-    targetImage.src = "../../../fast/backgrounds/resources/red-green-animated.webp";
-}
-</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-webp.html b/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-webp.html
deleted file mode 100644
index b70200d8..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/animated-webp.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!doctype HTML>
-<body>
-<div style="height: 2000px; width: 2000px;">
-    <img id="targetImage">
-</div>
-<script src="../resources/text-based-repaint.js"></script>
-<script>
-window.testIsAsync = true;
-
-function repaintTest() {
-    if (window.internals)
-        internals.advanceImageAnimation(targetImage);
-    requestAnimationFrame(function() {
-        finishRepaintTest();
-    });
-}
-
-window.onload = function() {
-    targetImage.onload = runRepaintTest;
-    targetImage.src="../../../fast/backgrounds/resources/red-green-animated.webp";
-}
-</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/resources/animated-svg-fixed-size.svg b/third_party/WebKit/LayoutTests/paint/invalidation/svg/resources/animated-svg-fixed-size.svg
new file mode 100644
index 0000000..24364b78
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/resources/animated-svg-fixed-size.svg
@@ -0,0 +1,11 @@
+<svg width="50" height="50" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
+  <rect id="blue-rectangle" width="10" height="10" fill="#0099cc"></rect>
+  <animate
+          xlink:href="#blue-rectangle"
+          attributeName="fill"
+          from="#00ffcc"
+          to="#0000cc"
+          dur="1s"
+          begin="0.25s"
+          repeatCount="indefinite"/>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/animated-row-background.html b/third_party/WebKit/LayoutTests/paint/invalidation/table/animated-row-background.html
index ef44a82..b74fa63 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/animated-row-background.html
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/animated-row-background.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <style>
 td { width: 100px; height: 100px }
-tr { background-image: url(../../../fast/backgrounds/resources/red-green-animated.gif) }
+tr { background-image: url(../svg/resources/animated-svg-fixed-size.svg) }
 </style>
 <table>
   <tr id="row">
@@ -24,6 +24,6 @@
   // The image element ensures that the test runs just after the image
   // resource is loaded, and will be used in advanceImageAnimation().
   image.onload = runRepaintTest;
-  image.src="../../../fast/backgrounds/resources/red-green-animated.gif";
+  image.src="../svg/resources/animated-svg-fixed-size.svg";
 }
 </script>
diff --git a/third_party/WebKit/LayoutTests/svg/filters/filtered-animated-image-crash.html b/third_party/WebKit/LayoutTests/svg/filters/filtered-animated-image-crash.html
deleted file mode 100644
index 473717c..0000000
--- a/third_party/WebKit/LayoutTests/svg/filters/filtered-animated-image-crash.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<script src=../../resources/run-after-layout-and-paint.js></script>
-<svg>
-  <filter id='filt'><feMerge><feMergeNode/></feMerge></filter>
-  <image height='400' width='400' filter='url(#filt)'
-         xlink:href='../../images/resources/animated-10color-fast.gif'
-         onload="runTest()"/>
-</svg>
-<p>PASS if no crash.</p>
-<script>
-if (window.testRunner)
-  testRunner.dumpAsText();
-
-function runTest() {
-  // Nudge the frame time for the image to force the animation to catch up.
-  if (window.internals)
-    internals.advanceTimeForImage(document.querySelector('image'), 0.5);
-  runAfterLayoutAndPaint(function(){}, true);
-}
-</script>
diff --git a/third_party/WebKit/LayoutTests/svg/masking/css-mask-of-root-expected.html b/third_party/WebKit/LayoutTests/svg/masking/css-mask-of-root-expected.html
new file mode 100644
index 0000000..4b1ff5e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/masking/css-mask-of-root-expected.html
@@ -0,0 +1,2 @@
+<!doctype HTML>
+<svg></svg>
diff --git a/third_party/WebKit/LayoutTests/svg/masking/css-mask-of-root.html b/third_party/WebKit/LayoutTests/svg/masking/css-mask-of-root.html
new file mode 100644
index 0000000..7d73c2b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/masking/css-mask-of-root.html
@@ -0,0 +1,2 @@
+<!doctype HTML>
+<svg style="-webkit-mask-image: url(fake);"></svg>
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt
index 1f0cebf..ad378e2 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt
@@ -39,12 +39,12 @@
         },
         {
           "object": "LayoutSVGRect rect id='rect'",
-          "rect": [355, 125, 103, 104],
+          "rect": [109, 85, 103, 100],
           "reason": "full"
         },
         {
           "object": "LayoutSVGRect rect id='rect'",
-          "rect": [109, 84, 103, 101],
+          "rect": [356, 126, 102, 102],
           "reason": "full"
         },
         {
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-late-marker-and-object-creation-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-late-marker-and-object-creation-expected.txt
index 0a06b4e69..c64a706 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-late-marker-and-object-creation-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-late-marker-and-object-creation-expected.txt
@@ -18,17 +18,17 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
+          "object": "LayoutSVGContainer g id='content'",
+          "rect": [190, 198, 137, 137],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGPath path",
-          "rect": [189, 197, 139, 139],
+          "rect": [190, 198, 137, 137],
           "reason": "full"
         },
         {
           "object": "LayoutSVGPath path",
-          "rect": [189, 197, 139, 139],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGContainer g id='content'",
           "rect": [190, 198, 137, 137],
           "reason": "appeared"
         }
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-late-marker-creation-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-late-marker-creation-expected.txt
index 73729997..c9c3c3a 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-late-marker-creation-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-late-marker-creation-expected.txt
@@ -19,7 +19,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutSVGPath path",
-          "rect": [189, 197, 139, 139],
+          "rect": [190, 198, 137, 137],
           "reason": "full"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-repaint-rect-on-path-with-stroke-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-repaint-rect-on-path-with-stroke-expected.txt
index 57bfc38..1edb7ec 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-repaint-rect-on-path-with-stroke-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-repaint-rect-on-path-with-stroke-expected.txt
@@ -19,12 +19,12 @@
       "paintInvalidations": [
         {
           "object": "LayoutSVGPath path id='path'",
-          "rect": [29, 29, 142, 92],
+          "rect": [30, 30, 140, 90],
           "reason": "subtree"
         },
         {
           "object": "LayoutSVGPath path id='path'",
-          "rect": [179, 29, 122, 92],
+          "rect": [180, 30, 120, 90],
           "reason": "subtree"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-container-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-container-expected.txt
index 554e827b..b0f4649 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-container-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-container-expected.txt
@@ -26,11 +26,6 @@
           "object": "LayoutSVGRect rect",
           "rect": [0, 0, 76, 76],
           "reason": "subtree"
-        },
-        {
-          "object": "LayoutSVGRect rect",
-          "rect": [-1, -1, 41, 41],
-          "reason": "subtree"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-polygon-changes-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-polygon-changes-expected.txt
index 16c1547..27153cd 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-polygon-changes-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-polygon-changes-expected.txt
@@ -19,12 +19,12 @@
       "paintInvalidations": [
         {
           "object": "LayoutSVGPath polygon id='polygon'",
-          "rect": [253, 208, 184, 129],
+          "rect": [254, 209, 182, 127],
           "reason": "full"
         },
         {
           "object": "LayoutSVGPath polygon id='polygon'",
-          "rect": [253, 198, 184, 129],
+          "rect": [254, 199, 182, 127],
           "reason": "full"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-polygon-removal-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-polygon-removal-expected.txt
index 949d895..345e5d7 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-polygon-removal-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-polygon-removal-expected.txt
@@ -19,7 +19,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutSVGPath polygon id='polygon'",
-          "rect": [253, 208, 184, 129],
+          "rect": [254, 209, 182, 127],
           "reason": "full"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-style-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-style-expected.txt
index 35f1de9..90005bfb 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-style-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-style-expected.txt
@@ -18,29 +18,29 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "LayoutSVGRect rect",
-          "rect": [5, 185, 160, 60],
-          "reason": "style change"
-        },
-        {
-          "object": "LayoutSVGRect rect",
-          "rect": [5, 125, 160, 60],
-          "reason": "style change"
-        },
-        {
-          "object": "LayoutSVGRect rect",
-          "rect": [5, 65, 160, 60],
-          "reason": "style change"
-        },
-        {
-          "object": "LayoutSVGRect rect",
-          "rect": [5, 5, 160, 60],
-          "reason": "style change"
-        },
-        {
           "object": "LayoutSVGContainer g",
           "rect": [6, 6, 158, 238],
           "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [6, 186, 158, 58],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [6, 126, 158, 58],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [6, 66, 158, 58],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [6, 6, 158, 58],
+          "reason": "style change"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-transform-addition-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-transform-addition-expected.txt
index e87c000..0fcc26c6 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-transform-addition-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-transform-addition-expected.txt
@@ -19,7 +19,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutSVGPath polygon id='polygon'",
-          "rect": [253, 208, 184, 129],
+          "rect": [254, 209, 182, 127],
           "reason": "subtree"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-transform-changes-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-transform-changes-expected.txt
index e87c000..0fcc26c6 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-transform-changes-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/js-update-transform-changes-expected.txt
@@ -19,7 +19,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutSVGPath polygon id='polygon'",
-          "rect": [253, 208, 184, 129],
+          "rect": [254, 209, 182, 127],
           "reason": "subtree"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-child-changes-css-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-child-changes-css-expected.txt
index ccea323..53976c84 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-child-changes-css-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-child-changes-css-expected.txt
@@ -19,7 +19,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutSVGPath path",
-          "rect": [113, 118, 84, 84],
+          "rect": [114, 119, 82, 82],
           "reason": "style change"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-child-changes-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-child-changes-expected.txt
index ccea323..53976c84 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-child-changes-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-child-changes-expected.txt
@@ -19,7 +19,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutSVGPath path",
-          "rect": [113, 118, 84, 84],
+          "rect": [114, 119, 82, 82],
           "reason": "style change"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-strokeWidth-changes-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-strokeWidth-changes-expected.txt
index e519c67..6583459 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-strokeWidth-changes-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-strokeWidth-changes-expected.txt
@@ -18,14 +18,14 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "LayoutSVGPath path id='path'",
-          "rect": [113, 118, 84, 84],
-          "reason": "style change"
-        },
-        {
           "object": "LayoutSVGContainer g",
           "rect": [114, 119, 82, 82],
           "reason": "geometry"
+        },
+        {
+          "object": "LayoutSVGPath path id='path'",
+          "rect": [114, 119, 82, 82],
+          "reason": "style change"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-viewBox-changes-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-viewBox-changes-expected.txt
index 09779df2..c9526d28 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-viewBox-changes-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/marker-viewBox-changes-expected.txt
@@ -18,14 +18,14 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "LayoutSVGPath path id='go'",
-          "rect": [89, 94, 108, 108],
-          "reason": "full"
-        },
-        {
           "object": "LayoutSVGContainer g",
           "rect": [90, 95, 106, 106],
           "reason": "geometry"
+        },
+        {
+          "object": "LayoutSVGPath path id='go'",
+          "rect": [90, 95, 106, 106],
+          "reason": "full"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/paintorder-filtered-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/paintorder-filtered-expected.txt
index 472e40f..92d29a0 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/paintorder-filtered-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/paintorder-filtered-expected.txt
@@ -18,23 +18,8 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "LayoutSVGPath polygon id='t3'",
-          "rect": [304, 150, 165, 164],
-          "reason": "style change"
-        },
-        {
           "object": "LayoutSVGPath polygon",
-          "rect": [31, 150, 165, 164],
-          "reason": "style change"
-        },
-        {
-          "object": "LayoutSVGPath polygon",
-          "rect": [441, 150, 164, 164],
-          "reason": "style change"
-        },
-        {
-          "object": "LayoutSVGPath polygon",
-          "rect": [168, 150, 164, 164],
+          "rect": [168, 150, 164, 163],
           "reason": "style change"
         },
         {
@@ -43,6 +28,21 @@
           "reason": "style change"
         },
         {
+          "object": "LayoutSVGPath polygon",
+          "rect": [441, 150, 163, 163],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGPath polygon id='t3'",
+          "rect": [305, 150, 163, 163],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGPath polygon",
+          "rect": [32, 150, 163, 163],
+          "reason": "style change"
+        },
+        {
           "object": "LayoutSVGViewportContainer svg id='poly'",
           "rect": [32, 150, 163, 163],
           "reason": "style change"
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/path-pathlength-change-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/path-pathlength-change-expected.txt
index 6bd76d5e..7ab932c 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/path-pathlength-change-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/path-pathlength-change-expected.txt
@@ -19,7 +19,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutSVGPath path",
-          "rect": [67, 67, 182, 182],
+          "rect": [68, 68, 180, 180],
           "reason": "full"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/relative-sized-content-with-resources-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/relative-sized-content-with-resources-expected.txt
index 70effc7e..5bb488d 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/relative-sized-content-with-resources-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/relative-sized-content-with-resources-expected.txt
@@ -34,7 +34,7 @@
         },
         {
           "object": "LayoutSVGEllipse circle",
-          "rect": [8, 155, 102, 236],
+          "rect": [9, 155, 100, 236],
           "reason": "SVG resource change"
         },
         {
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/relative-sized-document-scrollbars-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/relative-sized-document-scrollbars-expected.txt
index 4a5504d..3b6205a8 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/relative-sized-document-scrollbars-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/relative-sized-document-scrollbars-expected.txt
@@ -24,7 +24,7 @@
         },
         {
           "object": "LayoutSVGRect rect",
-          "rect": [6, 4, 788, 592],
+          "rect": [7, 5, 786, 590],
           "reason": "style change"
         },
         {
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/repaint-paintorder-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/repaint-paintorder-expected.txt
index 90a883be1..924969a 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/repaint-paintorder-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/repaint-paintorder-expected.txt
@@ -18,41 +18,41 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "LayoutSVGPath polygon id='t3'",
-          "rect": [304, 150, 165, 164],
-          "reason": "style change"
-        },
-        {
-          "object": "LayoutSVGPath polygon",
-          "rect": [31, 150, 165, 164],
-          "reason": "style change"
-        },
-        {
-          "object": "LayoutSVGPath polygon",
-          "rect": [441, 150, 164, 164],
-          "reason": "style change"
-        },
-        {
-          "object": "LayoutSVGPath polygon",
-          "rect": [168, 150, 164, 164],
-          "reason": "style change"
-        },
-        {
           "object": "LayoutSVGContainer use id='t2'",
           "rect": [168, 150, 164, 163],
           "reason": "style change"
         },
         {
+          "object": "LayoutSVGPath polygon",
+          "rect": [168, 150, 164, 163],
+          "reason": "style change"
+        },
+        {
           "object": "LayoutSVGViewportContainer svg id='poly'",
           "rect": [168, 150, 164, 163],
           "reason": "style change"
         },
         {
+          "object": "LayoutSVGPath polygon",
+          "rect": [441, 150, 163, 163],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGPath polygon id='t3'",
+          "rect": [305, 150, 163, 163],
+          "reason": "style change"
+        },
+        {
           "object": "LayoutSVGContainer use id='t1'",
           "rect": [32, 150, 163, 163],
           "reason": "style change"
         },
         {
+          "object": "LayoutSVGPath polygon",
+          "rect": [32, 150, 163, 163],
+          "reason": "style change"
+        },
+        {
           "object": "LayoutSVGViewportContainer svg id='poly'",
           "rect": [32, 150, 163, 163],
           "reason": "style change"
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/repaint-stroke-width-changes-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/repaint-stroke-width-changes-expected.txt
index 51118c1..350cc49 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/repaint-stroke-width-changes-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/repaint-stroke-width-changes-expected.txt
@@ -19,7 +19,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutSVGRect rect",
-          "rect": [9, 9, 462, 342],
+          "rect": [10, 10, 460, 340],
           "reason": "style change"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/stroke-opacity-update-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/stroke-opacity-update-expected.txt
index 914df47..6043853e 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/stroke-opacity-update-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/stroke-opacity-update-expected.txt
@@ -19,7 +19,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutSVGEllipse circle",
-          "rect": [-1, -1, 204, 204],
+          "rect": [0, 0, 203, 203],
           "reason": "style change"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/tabgroup-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/tabgroup-expected.txt
index 5087023..e092c39 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/tabgroup-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/tabgroup-expected.txt
@@ -18,26 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "LayoutSVGPath path",
-          "rect": [388, 37, 318, 83],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath path",
-          "rect": [388, 37, 318, 83],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath path",
-          "rect": [388, 37, 318, 83],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath path",
-          "rect": [388, 37, 318, 83],
-          "reason": "appeared"
-        },
-        {
           "object": "LayoutSVGContainer g",
           "rect": [389, 37, 316, 82],
           "reason": "appeared"
@@ -69,22 +49,22 @@
         },
         {
           "object": "LayoutSVGPath path",
-          "rect": [261, 277, 305, 305],
+          "rect": [389, 37, 316, 82],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path",
-          "rect": [261, 277, 305, 305],
+          "rect": [389, 37, 316, 82],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path",
-          "rect": [261, 277, 305, 305],
+          "rect": [389, 37, 316, 82],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path",
-          "rect": [261, 277, 305, 305],
+          "rect": [389, 37, 316, 82],
           "reason": "appeared"
         },
         {
@@ -119,42 +99,22 @@
         },
         {
           "object": "LayoutSVGPath path",
-          "rect": [5, 255, 240, 162],
+          "rect": [262, 277, 304, 304],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path",
-          "rect": [5, 255, 240, 162],
+          "rect": [262, 277, 304, 304],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path",
-          "rect": [5, 255, 240, 162],
+          "rect": [262, 277, 304, 304],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path",
-          "rect": [5, 255, 240, 162],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath path",
-          "rect": [505, 232, 240, 161],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath path",
-          "rect": [505, 232, 240, 161],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath path",
-          "rect": [505, 232, 240, 161],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath path",
-          "rect": [505, 232, 240, 161],
+          "rect": [262, 277, 304, 304],
           "reason": "appeared"
         },
         {
@@ -188,6 +148,26 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGPath path",
+          "rect": [506, 232, 238, 161],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path",
+          "rect": [506, 232, 238, 161],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path",
+          "rect": [506, 232, 238, 161],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path",
+          "rect": [506, 232, 238, 161],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGContainer g",
           "rect": [6, 256, 238, 160],
           "reason": "appeared"
@@ -218,6 +198,26 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGPath path",
+          "rect": [6, 256, 238, 160],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path",
+          "rect": [6, 256, 238, 160],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path",
+          "rect": [6, 256, 238, 160],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path",
+          "rect": [6, 256, 238, 160],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGContainer g id='tabgroupTriangle__0_content'",
           "rect": [15, 291, 211, 37],
           "reason": "appeared"
@@ -253,26 +253,6 @@
           "reason": "appeared"
         },
         {
-          "object": "LayoutSVGPath path",
-          "rect": [37, 5, 161, 240],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath path",
-          "rect": [37, 5, 161, 240],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath path",
-          "rect": [37, 5, 161, 240],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath path",
-          "rect": [37, 5, 161, 240],
-          "reason": "appeared"
-        },
-        {
           "object": "LayoutSVGContainer g",
           "rect": [37, 6, 160, 238],
           "reason": "appeared"
@@ -303,18 +283,28 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGPath path",
+          "rect": [37, 6, 160, 238],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path",
+          "rect": [37, 6, 160, 238],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path",
+          "rect": [37, 6, 160, 238],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path",
+          "rect": [37, 6, 160, 238],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGPath path id='tabgroupTriangle__1'",
-          "rect": [60, 255, 69, 33],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath path id='tabgroupRectTriangle__0'",
-          "rect": [505, 232, 69, 21],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath path id='tabgroupRound__0'",
-          "rect": [388, 37, 69, 20],
+          "rect": [60, 256, 69, 31],
           "reason": "appeared"
         },
         {
@@ -323,133 +313,138 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGPath path id='tabgroupRectTriangle__0'",
+          "rect": [506, 232, 68, 20],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='tabgroupRound__0'",
+          "rect": [389, 37, 68, 20],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGPath path id='tabgroupRectRound__1'",
-          "rect": [414, 331, 66, 65],
+          "rect": [415, 331, 64, 65],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path id='tabgroupRectTriangle__1'",
-          "rect": [569, 232, 66, 21],
+          "rect": [570, 232, 64, 20],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path id='tabgroupRound__1'",
-          "rect": [452, 37, 65, 20],
+          "rect": [453, 37, 64, 20],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path id='tabgroupTriangle__0'",
-          "rect": [5, 255, 60, 33],
+          "rect": [6, 256, 59, 31],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path id='tabgroupTriangle__2'",
-          "rect": [124, 255, 59, 33],
+          "rect": [125, 256, 58, 31],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGInlineText #text",
+          "rect": [66, 257, 57, 29],
+          "reason": "subtree"
+        },
+        {
+          "object": "LayoutSVGInlineText #text",
+          "rect": [66, 257, 57, 29],
+          "reason": "subtree"
+        },
+        {
+          "object": "LayoutSVGTSpan tspan",
+          "rect": [66, 257, 57, 29],
+          "reason": "subtree"
+        },
+        {
+          "object": "LayoutSVGTSpan tspan",
+          "rect": [66, 257, 57, 29],
+          "reason": "subtree"
+        },
+        {
+          "object": "LayoutSVGText text",
+          "rect": [66, 257, 57, 29],
+          "reason": "subtree"
+        },
+        {
+          "object": "LayoutSVGInlineText #text",
+          "rect": [511, 234, 57, 16],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGTSpan tspan",
+          "rect": [511, 234, 57, 16],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGText text",
+          "rect": [511, 234, 57, 16],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGInlineText #text",
+          "rect": [394, 38, 57, 16],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGTSpan tspan",
+          "rect": [394, 38, 57, 16],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGText text",
+          "rect": [394, 38, 57, 16],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path id='tabgroupRectRound__3'",
-          "rect": [505, 421, 57, 57],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGInlineText #text",
-          "rect": [66, 257, 57, 29],
-          "reason": "subtree"
-        },
-        {
-          "object": "LayoutSVGInlineText #text",
-          "rect": [66, 257, 57, 29],
-          "reason": "subtree"
-        },
-        {
-          "object": "LayoutSVGTSpan tspan",
-          "rect": [66, 257, 57, 29],
-          "reason": "subtree"
-        },
-        {
-          "object": "LayoutSVGTSpan tspan",
-          "rect": [66, 257, 57, 29],
-          "reason": "subtree"
-        },
-        {
-          "object": "LayoutSVGText text",
-          "rect": [66, 257, 57, 29],
-          "reason": "subtree"
-        },
-        {
-          "object": "LayoutSVGInlineText #text",
-          "rect": [511, 234, 57, 16],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGTSpan tspan",
-          "rect": [511, 234, 57, 16],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGText text",
-          "rect": [511, 234, 57, 16],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGInlineText #text",
-          "rect": [394, 38, 57, 16],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGTSpan tspan",
-          "rect": [394, 38, 57, 16],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGText text",
-          "rect": [394, 38, 57, 16],
+          "rect": [505, 422, 56, 55],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path id='tabgroupRectRound__2'",
-          "rect": [465, 381, 55, 55],
+          "rect": [466, 382, 53, 53],
           "reason": "appeared"
         },
         {
-          "object": "LayoutSVGPath path id='tabgroupRectTriangle__3'",
-          "rect": [674, 232, 53, 21],
+          "object": "LayoutSVGInlineText #text",
+          "rect": [575, 234, 53, 16],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGTSpan tspan",
+          "rect": [575, 234, 53, 16],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGText text",
+          "rect": [575, 234, 53, 16],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGInlineText #text",
+          "rect": [458, 38, 53, 16],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGTSpan tspan",
+          "rect": [458, 38, 53, 16],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGText text",
+          "rect": [458, 38, 53, 16],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path id='tabgroupRound__3'",
-          "rect": [557, 37, 53, 20],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGInlineText #text",
-          "rect": [575, 234, 53, 16],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGTSpan tspan",
-          "rect": [575, 234, 53, 16],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGText text",
-          "rect": [575, 234, 53, 16],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGInlineText #text",
-          "rect": [458, 38, 53, 16],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGTSpan tspan",
-          "rect": [458, 38, 53, 16],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGText text",
-          "rect": [458, 38, 53, 16],
+          "rect": [557, 37, 52, 20],
           "reason": "appeared"
         },
         {
@@ -468,53 +463,58 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGPath path id='tabgroupRectTriangle__3'",
+          "rect": [675, 232, 51, 20],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGInlineText #text",
+          "rect": [423, 339, 49, 48],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGTSpan tspan",
+          "rect": [423, 339, 49, 48],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGText text",
+          "rect": [423, 339, 49, 48],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGPath path id='tabgroupRectTriangle__2'",
-          "rect": [629, 232, 50, 21],
+          "rect": [630, 232, 49, 20],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGInlineText #text",
+          "rect": [11, 257, 48, 29],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGInlineText #text",
+          "rect": [11, 257, 48, 29],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGTSpan tspan",
+          "rect": [11, 257, 48, 29],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGTSpan tspan",
+          "rect": [11, 257, 48, 29],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGText text",
+          "rect": [11, 257, 48, 29],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path id='tabgroupRound__2'",
-          "rect": [512, 37, 50, 20],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGInlineText #text",
-          "rect": [423, 339, 49, 48],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGTSpan tspan",
-          "rect": [423, 339, 49, 48],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGText text",
-          "rect": [423, 339, 49, 48],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGInlineText #text",
-          "rect": [11, 257, 48, 29],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGInlineText #text",
-          "rect": [11, 257, 48, 29],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGTSpan tspan",
-          "rect": [11, 257, 48, 29],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGTSpan tspan",
-          "rect": [11, 257, 48, 29],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGText text",
-          "rect": [11, 257, 48, 29],
+          "rect": [513, 37, 48, 20],
           "reason": "appeared"
         },
         {
@@ -544,7 +544,7 @@
         },
         {
           "object": "LayoutSVGPath path id='tabgroupTriangle__3'",
-          "rect": [178, 255, 44, 33],
+          "rect": [178, 256, 44, 31],
           "reason": "appeared"
         },
         {
@@ -664,22 +664,22 @@
         },
         {
           "object": "LayoutSVGPath path id='tabgroupRect__0'",
-          "rect": [177, 5, 21, 69],
+          "rect": [178, 6, 19, 68],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path id='tabgroupRect__1'",
-          "rect": [177, 69, 21, 66],
+          "rect": [178, 70, 19, 64],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path id='tabgroupRect__3'",
-          "rect": [177, 174, 21, 53],
+          "rect": [178, 175, 19, 51],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGPath path id='tabgroupRect__2'",
-          "rect": [177, 129, 21, 50],
+          "rect": [178, 130, 19, 49],
           "reason": "appeared"
         },
         {
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/use-setAttribute-crash-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/use-setAttribute-crash-expected.txt
index 370253e..c8ba024 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/use-setAttribute-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/use-setAttribute-crash-expected.txt
@@ -23,16 +23,16 @@
           "reason": "geometry"
         },
         {
-          "object": "LayoutSVGEllipse svg:circle id='circle'",
-          "rect": [32, 32, 52, 52],
-          "reason": "appeared"
-        },
-        {
           "object": "LayoutSVGContainer svg:use id='use'",
           "rect": [33, 33, 50, 50],
           "reason": "full"
         },
         {
+          "object": "LayoutSVGEllipse svg:circle id='circle'",
+          "rect": [33, 33, 50, 50],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGRoot svg:svg id='svg'",
           "rect": [33, 33, 50, 50],
           "reason": "full"
@@ -44,7 +44,7 @@
         },
         {
           "object": "LayoutSVGEllipse svg:circle id='circle'",
-          "rect": [52, 52, 12, 12],
+          "rect": [53, 53, 10, 10],
           "reason": "disappeared"
         },
         {
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/window-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/window-expected.txt
index 3741eff..165ce95ec 100644
--- a/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/window-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spv175/paint/invalidation/svg/window-expected.txt
@@ -28,11 +28,6 @@
           "reason": "appeared"
         },
         {
-          "object": "LayoutSVGRect rect",
-          "rect": [38, 142, 549, 394],
-          "reason": "appeared"
-        },
-        {
           "object": "LayoutSVGContainer g id='bigWindow'",
           "rect": [38, 143, 549, 392],
           "reason": "appeared"
@@ -43,8 +38,8 @@
           "reason": "appeared"
         },
         {
-          "object": "LayoutSVGRect rect id='titleBarbigWindow'",
-          "rect": [38, 142, 549, 16],
+          "object": "LayoutSVGRect rect",
+          "rect": [38, 143, 549, 392],
           "reason": "appeared"
         },
         {
@@ -53,13 +48,13 @@
           "reason": "appeared"
         },
         {
-          "object": "LayoutSVGRect rect",
-          "rect": [38, 523, 549, 13],
+          "object": "LayoutSVGRect rect id='titleBarbigWindow'",
+          "rect": [38, 143, 549, 15],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGRect rect",
-          "rect": [77, 181, 315, 238],
+          "rect": [38, 523, 549, 12],
           "reason": "appeared"
         },
         {
@@ -73,8 +68,8 @@
           "reason": "appeared"
         },
         {
-          "object": "LayoutSVGRect rect id='titleBarnestedWindow'",
-          "rect": [77, 181, 315, 16],
+          "object": "LayoutSVGRect rect",
+          "rect": [77, 182, 315, 236],
           "reason": "appeared"
         },
         {
@@ -83,8 +78,13 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGRect rect id='titleBarnestedWindow'",
+          "rect": [77, 182, 315, 15],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGRect rect",
-          "rect": [77, 406, 315, 13],
+          "rect": [77, 406, 315, 12],
           "reason": "appeared"
         },
         {
@@ -108,11 +108,6 @@
           "reason": "appeared"
         },
         {
-          "object": "LayoutSVGRect rect",
-          "rect": [624, 92, 174, 159],
-          "reason": "appeared"
-        },
-        {
           "object": "LayoutSVGContainer g id='navWindow'",
           "rect": [624, 92, 174, 158],
           "reason": "appeared"
@@ -123,6 +118,11 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGRect rect",
+          "rect": [624, 92, 174, 158],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGContainer g id='colourPickerWindow'",
           "rect": [77, 195, 174, 143],
           "reason": "appeared"
@@ -144,22 +144,17 @@
         },
         {
           "object": "LayoutSVGRect rect id='titleBarcolourPickerWindow'",
-          "rect": [77, 195, 174, 16],
+          "rect": [77, 195, 174, 15],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGRect rect",
-          "rect": [624, 238, 174, 13],
+          "rect": [624, 238, 174, 12],
           "reason": "appeared"
         },
         {
           "object": "LayoutSVGRect rect",
-          "rect": [77, 325, 174, 13],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGRect rect",
-          "rect": [311, 377, 159, 143],
+          "rect": [77, 326, 174, 12],
           "reason": "appeared"
         },
         {
@@ -238,16 +233,6 @@
           "reason": "appeared"
         },
         {
-          "object": "LayoutSVGRect rect id='titleBarsmallWindow'",
-          "rect": [311, 377, 159, 16],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGRect rect",
-          "rect": [311, 507, 159, 13],
-          "reason": "appeared"
-        },
-        {
           "object": "LayoutSVGContainer g id='smallWindow'",
           "rect": [312, 377, 158, 143],
           "reason": "appeared"
@@ -258,13 +243,23 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGRect rect",
+          "rect": [312, 377, 158, 143],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGContainer g id='windowTitlebarGroupsmallWindow'",
           "rect": [312, 377, 158, 16],
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGRect rect id='titleBarsmallWindow'",
+          "rect": [312, 377, 158, 15],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGRect rect",
-          "rect": [249, 343, 143, 65],
+          "rect": [312, 508, 158, 12],
           "reason": "appeared"
         },
         {
@@ -278,8 +273,8 @@
           "reason": "appeared"
         },
         {
-          "object": "LayoutSVGRect rect id='titleBarstatusWindow'",
-          "rect": [249, 343, 143, 16],
+          "object": "LayoutSVGRect rect",
+          "rect": [249, 344, 143, 64],
           "reason": "appeared"
         },
         {
@@ -288,6 +283,11 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGRect rect id='titleBarstatusWindow'",
+          "rect": [249, 344, 143, 15],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGContainer g",
           "rect": [320, 398, 136, 65],
           "reason": "appeared"
@@ -373,11 +373,6 @@
           "reason": "appeared"
         },
         {
-          "object": "LayoutSVGRect rect",
-          "rect": [38, 475, 120, 50],
-          "reason": "appeared"
-        },
-        {
           "object": "LayoutSVGContainer g",
           "rect": [257, 364, 120, 40],
           "reason": "appeared"
@@ -423,6 +418,11 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGRect rect",
+          "rect": [39, 476, 119, 48],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGContainer g",
           "rect": [43, 480, 115, 38],
           "reason": "appeared"
@@ -548,76 +548,6 @@
           "reason": "appeared"
         },
         {
-          "object": "LayoutSVGPath line",
-          "rect": [615, 83, 12, 12],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutSVGPath line",
-          "rect": [615, 83, 12, 12],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutSVGPath line",
-          "rect": [782, 94, 12, 11],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutSVGPath line",
-          "rect": [782, 94, 12, 11],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutSVGPath line",
-          "rect": [782, 94, 12, 11],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath line",
-          "rect": [782, 94, 12, 11],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGRect rect",
-          "rect": [615, 99, 12, 11],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutSVGPath line",
-          "rect": [571, 145, 12, 11],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath line",
-          "rect": [571, 145, 12, 11],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath line",
-          "rect": [235, 197, 12, 11],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath line",
-          "rect": [235, 197, 12, 11],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath line",
-          "rect": [615, 123, 12, 3],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutSVGPath line",
-          "rect": [349, 192, 12, 3],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGRect rect",
-          "rect": [376, 345, 11, 12],
-          "reason": "appeared"
-        },
-        {
           "object": "LayoutSVGContainer use id='maximizeButtonnavWindow'",
           "rect": [769, 94, 11, 11],
           "reason": "full"
@@ -663,21 +593,6 @@
           "reason": "appeared"
         },
         {
-          "object": "LayoutSVGPath line",
-          "rect": [454, 379, 11, 11],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath line",
-          "rect": [454, 379, 11, 11],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGRect rect",
-          "rect": [441, 379, 11, 11],
-          "reason": "appeared"
-        },
-        {
           "object": "LayoutSVGContainer use id='closeButtonnestedWindow'",
           "rect": [376, 184, 11, 11],
           "reason": "appeared"
@@ -713,11 +628,6 @@
           "reason": "appeared"
         },
         {
-          "object": "LayoutSVGRect rect",
-          "rect": [222, 197, 11, 11],
-          "reason": "appeared"
-        },
-        {
           "object": "LayoutSVGContainer use id='minimizeButtonnavWindow'",
           "rect": [756, 95, 11, 10],
           "reason": "full"
@@ -753,6 +663,11 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGRect rect",
+          "rect": [441, 380, 11, 10],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGViewportContainer svg id='maximizeButton'",
           "rect": [441, 380, 11, 10],
           "reason": "appeared"
@@ -763,6 +678,11 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGRect rect",
+          "rect": [376, 346, 11, 10],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGViewportContainer svg id='maximizeButton'",
           "rect": [376, 346, 11, 10],
           "reason": "appeared"
@@ -789,16 +709,6 @@
         },
         {
           "object": "LayoutSVGPath line",
-          "rect": [545, 153, 11, 3],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath line",
-          "rect": [363, 354, 11, 3],
-          "reason": "appeared"
-        },
-        {
-          "object": "LayoutSVGPath line",
           "rect": [756, 103, 11, 2],
           "reason": "geometry"
         },
@@ -809,7 +719,7 @@
         },
         {
           "object": "LayoutSVGPath line",
-          "rect": [428, 388, 11, 2],
+          "rect": [545, 154, 11, 2],
           "reason": "appeared"
         },
         {
@@ -818,6 +728,11 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGPath line",
+          "rect": [363, 355, 11, 1],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGContainer use id='closeButtonnavWindow'",
           "rect": [783, 94, 10, 11],
           "reason": "full"
@@ -828,6 +743,26 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGPath line",
+          "rect": [783, 94, 10, 11],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutSVGPath line",
+          "rect": [783, 94, 10, 11],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutSVGPath line",
+          "rect": [783, 94, 10, 11],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath line",
+          "rect": [783, 94, 10, 11],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGViewportContainer svg id='closeButton'",
           "rect": [783, 94, 10, 11],
           "reason": "full"
@@ -843,6 +778,16 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGPath line",
+          "rect": [572, 145, 10, 11],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath line",
+          "rect": [572, 145, 10, 11],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGViewportContainer svg id='closeButton'",
           "rect": [572, 145, 10, 11],
           "reason": "appeared"
@@ -853,6 +798,11 @@
           "reason": "full"
         },
         {
+          "object": "LayoutSVGRect rect",
+          "rect": [616, 100, 10, 10],
+          "reason": "geometry"
+        },
+        {
           "object": "LayoutSVGViewportContainer svg id='maximizeButton'",
           "rect": [616, 100, 10, 10],
           "reason": "full"
@@ -863,6 +813,16 @@
           "reason": "full"
         },
         {
+          "object": "LayoutSVGPath line",
+          "rect": [616, 84, 10, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutSVGPath line",
+          "rect": [616, 84, 10, 10],
+          "reason": "geometry"
+        },
+        {
           "object": "LayoutSVGRect rect",
           "rect": [616, 84, 10, 10],
           "reason": "geometry"
@@ -878,6 +838,16 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGPath line",
+          "rect": [455, 380, 10, 10],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath line",
+          "rect": [455, 380, 10, 10],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGRect rect",
           "rect": [455, 380, 10, 10],
           "reason": "appeared"
@@ -918,6 +888,16 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGPath line",
+          "rect": [236, 198, 10, 10],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath line",
+          "rect": [236, 198, 10, 10],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGRect rect",
           "rect": [236, 198, 10, 10],
           "reason": "appeared"
@@ -933,6 +913,11 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGRect rect",
+          "rect": [223, 198, 10, 10],
+          "reason": "appeared"
+        },
+        {
           "object": "LayoutSVGViewportContainer svg id='maximizeButton'",
           "rect": [223, 198, 10, 10],
           "reason": "appeared"
@@ -973,6 +958,21 @@
           "reason": "appeared"
         },
         {
+          "object": "LayoutSVGPath line",
+          "rect": [428, 388, 10, 2],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath line",
+          "rect": [350, 193, 10, 2],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath line",
+          "rect": [616, 124, 10, 1],
+          "reason": "geometry"
+        },
+        {
           "object": "LayoutSVGRect rect",
           "rect": [364, 346, 9, 10],
           "reason": "appeared"
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index c6ddaa67..2fc4a43 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -5234,6 +5234,7 @@
     getter rawId
     getter response
     method constructor
+    method getClientExtensionResults
 interface PushManager
     static getter supportedContentEncodings
     attribute @@toStringTag
@@ -9012,6 +9013,22 @@
     getter bounds
     getter emulatedHeight
     method constructor
+interface XRInputPose
+    attribute @@toStringTag
+    getter emulatedPosition
+    getter gripMatrix
+    getter pointerMatrix
+    method constructor
+interface XRInputSource
+    attribute @@toStringTag
+    getter handedness
+    getter pointerOrigin
+    method constructor
+interface XRInputSourceEvent : Event
+    attribute @@toStringTag
+    getter frame
+    getter inputSource
+    method constructor
 interface XRLayer
     attribute @@toStringTag
     method constructor
@@ -9025,6 +9042,7 @@
     getter views
     method constructor
     method getDevicePose
+    method getInputPose
 interface XRSession : EventTarget
     attribute @@toStringTag
     getter baseLayer
@@ -9040,6 +9058,7 @@
     method cancelAnimationFrame
     method constructor
     method end
+    method getInputSources
     method requestAnimationFrame
     method requestFrameOfReference
     setter baseLayer
diff --git a/third_party/WebKit/LayoutTests/xr/events_session_select.html b/third_party/WebKit/LayoutTests/xr/events_session_select.html
new file mode 100644
index 0000000..5874cc6e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/xr/events_session_select.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
+<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../xr/resources/xr-test-utils.js"></script>
+<script src="../xr/resources/test-constants.js"></script>
+<canvas id="webgl-canvas"></canvas>
+
+<script>
+let fakeDevices = fakeXRDevices();
+let watcherDone = new Event("watcherdone");
+
+xr_session_promise_test( (session, t) => {
+    let eventWatcher = new EventWatcher(t, session, ["selectstart", "selectend", "select", "watcherdone"]);
+    let eventPromise = eventWatcher.wait_for(["selectstart", "selectend", "select", "watcherdone"]);
+
+    // Need to have a valid pose or input event's don't process.
+    setPose(VALID_POSE);
+
+    function onSessionSelectStart(event) {
+      t.step( () => {
+        let input_sources = session.getInputSources();
+        assert_equals(event.frame.session, session);
+        assert_equals(event.inputSource, input_sources[0]);
+      });
+    }
+
+    function onSessionSelectEnd(event) {
+      t.step( () => {
+        let input_sources = session.getInputSources();
+        assert_equals(event.frame.session, session);
+        assert_equals(event.inputSource, input_sources[0]);
+      });
+    }
+
+    function onSessionSelect(event) {
+      t.step( () => {
+        let input_sources = session.getInputSources();
+        assert_equals(event.frame.session, session);
+        assert_equals(event.inputSource, input_sources[0]);
+      });
+      session.dispatchEvent(watcherDone);
+    }
+    session.addEventListener("selectstart", onSessionSelectStart, false);
+    session.addEventListener("selectend", onSessionSelectEnd, false);
+    session.addEventListener("select", onSessionSelect, false);
+
+    // Session must have a baseLayer or frame requests will be ignored.
+    session.baseLayer = new XRWebGLLayer(session, gl);
+
+    let input_source = new MockXRInputSource();
+
+    addInputSource(input_source);
+
+    // Press the primary input button and then release it a short time later.
+    session.requestAnimationFrame((time, xrFrame) => {
+      input_source.primaryInputPressed = true;
+
+       session.requestAnimationFrame((time, xrFrame) => {
+          input_source.primaryInputPressed = false;
+
+          session.requestAnimationFrame((time, xrFrame) => {
+            // Need to process one more frame to allow select to propegate.
+          });
+       });
+    });
+
+    return eventPromise;
+  }, fakeDevices["FakeGooglePixelPhone"], [ { exclusive: true } ],
+"XRInputSources primary input presses properly fires off the right events");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/xr/events_session_select_subframe.html b/third_party/WebKit/LayoutTests/xr/events_session_select_subframe.html
new file mode 100644
index 0000000..ae4093fab
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/xr/events_session_select_subframe.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
+<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../xr/resources/xr-test-utils.js"></script>
+<script src="../xr/resources/test-constants.js"></script>
+<canvas id="webgl-canvas"></canvas>
+
+<script>
+let fakeDevices = fakeXRDevices();
+let watcherDone = new Event("watcherdone");
+
+xr_session_promise_test( (session, t) => {
+    let eventWatcher = new EventWatcher(t, session, ["selectstart", "selectend", "select", "watcherdone"]);
+    let eventPromise = eventWatcher.wait_for(["selectstart", "selectend", "select", "watcherdone"]);
+
+    // Need to have a valid pose or input event's don't process.
+    setPose(VALID_POSE);
+
+    function onSessionSelectStart(event) {
+      t.step( () => {
+        let input_sources = session.getInputSources();
+        assert_equals(event.frame.session, session);
+        assert_equals(event.inputSource, input_sources[0]);
+      });
+    }
+
+    function onSessionSelectEnd(event) {
+      t.step( () => {
+        let input_sources = session.getInputSources();
+        assert_equals(event.frame.session, session);
+        assert_equals(event.inputSource, input_sources[0]);
+      });
+    }
+
+    function onSessionSelect(event) {
+      t.step( () => {
+        let input_sources = session.getInputSources();
+        assert_equals(event.frame.session, session);
+        assert_equals(event.inputSource, input_sources[0]);
+      });
+      session.dispatchEvent(watcherDone);
+    }
+    session.addEventListener("selectstart", onSessionSelectStart, false);
+    session.addEventListener("selectend", onSessionSelectEnd, false);
+    session.addEventListener("select", onSessionSelect, false);
+
+    // Session must have a baseLayer or frame requests will be ignored.
+    session.baseLayer = new XRWebGLLayer(session, gl);
+
+    let input_source = new MockXRInputSource();
+
+    addInputSource(input_source);
+
+    // Press the primary input button and then release it a short time later.
+    session.requestAnimationFrame((time, xrFrame) => {
+      input_source.primaryInputPressed = true;
+      input_source.primaryInputPressed = false;
+
+      session.requestAnimationFrame((time, xrFrame) => {
+        // Need to process one more frame to allow select to propegate.
+      });
+    });
+
+    return eventPromise;
+  }, fakeDevices["FakeGooglePixelPhone"], [ { exclusive: true } ],
+"Ensures that an XRInputSources primary input being pressed and released in the space of a single frame properly fires off the right events");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/xr/getDevicePose_oneframeupdate.html b/third_party/WebKit/LayoutTests/xr/getDevicePose_oneframeupdate.html
index 2d3a4fc..ae1361b 100644
--- a/third_party/WebKit/LayoutTests/xr/getDevicePose_oneframeupdate.html
+++ b/third_party/WebKit/LayoutTests/xr/getDevicePose_oneframeupdate.html
@@ -11,7 +11,7 @@
 <script>
 let fakeDevices = fakeXRDevices();
 
-xr_session_promise_test( (session) =>  {
+xr_session_promise_test( (session, t) =>  {
   // Session must have a baseLayer or else frame requests will be ignored.
   session.baseLayer = new XRWebGLLayer(session, gl);
 
@@ -24,23 +24,27 @@
       function onFrame(time, vrFrame) {
         session.requestAnimationFrame(onFrame);
         if (counter == 0) {
-          // Expecting to not get a pose since none has been supplied
-          assert_equals(vrFrame.getDevicePose(frameOfRef), null);
+          t.step( () => {
+            // Expecting to not get a pose since none has been supplied
+            assert_equals(vrFrame.getDevicePose(frameOfRef), null);
 
-          setPose(expected_pose);
+            setPose(expected_pose);
 
-          // Check that pose does not update pose within the same frame.
-          assert_equals(vrFrame.getDevicePose(frameOfRef), null);
+            // Check that pose does not update pose within the same frame.
+            assert_equals(vrFrame.getDevicePose(frameOfRef), null);
+          });
         } else {
-          // Check that pose was updated.
-          let pose = vrFrame.getDevicePose(frameOfRef);
-          assert_not_equals(pose, null);
+          t.step( () => {
+            // Check that pose was updated.
+            let pose = vrFrame.getDevicePose(frameOfRef);
+            assert_not_equals(pose, null);
 
-          let poseMatrix = pose.poseModelMatrix;
-          assert_not_equals(poseMatrix, null);
+            let poseMatrix = pose.poseModelMatrix;
+            assert_not_equals(poseMatrix, null);
 
-          let expectedMatrix = matrixFrom11Pose(expected_pose);
-          assert_matrices_approx_equal(poseMatrix, expectedMatrix);
+            let expectedMatrix = matrixFrom11Pose(expected_pose);
+            assert_matrices_approx_equal(poseMatrix, expectedMatrix);
+          });
 
           // Finished.
           resolve();
diff --git a/third_party/WebKit/LayoutTests/xr/getInputPose_hand.html b/third_party/WebKit/LayoutTests/xr/getInputPose_hand.html
new file mode 100644
index 0000000..772b4dc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/xr/getInputPose_hand.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
+<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../xr/resources/xr-test-utils.js"></script>
+<script src="../xr/resources/test-constants.js"></script>
+<canvas id="webgl-canvas"></canvas>
+
+<script>
+let fakeDevices = fakeXRDevices();
+
+xr_session_promise_test( (session, t) => new Promise((resolve) => {
+    // Session must have a baseLayer or frame requests will be ignored.
+    session.baseLayer = new XRWebGLLayer(session, gl);
+
+    // Need to have a valid pose or input updates don't process.
+    setPose(VALID_POSE);
+
+    let input_source = new MockXRInputSource();
+    input_source.pointerOrigin = "hand";
+    input_source.handedness = "right";
+
+    // Don't set a grip matrix yet
+
+    addInputSource(input_source);
+
+    // Must have a frameOfReference to get input poses. eyeLevel doesn't apply
+    // any transforms to the given matrix.
+    session.requestFrameOfReference("eyeLevel").then( (frameOfRef) => {
+
+      function CheckInvalidGrip(time, xrFrame) {
+        let source = session.getInputSources()[0];
+
+        let input_pose = xrFrame.getInputPose(source, frameOfRef);
+
+        t.step( () => {
+          // The input pose should be null when no grip matrix is provided.
+          assert_equals(source.pointerOrigin, "hand");
+          assert_equals(input_pose, null);
+        });
+
+        input_source.grip = VALID_GRIP;
+
+        session.requestAnimationFrame(CheckValidGrip);
+      }
+
+      function CheckValidGrip(time, xrFrame) {
+        let source = session.getInputSources()[0];
+
+        let input_pose = xrFrame.getInputPose(source, frameOfRef);
+
+        t.step( () => {
+          // When a grip matrix is present but no pointer offset is specified,
+          // the grip and pointer matrices should be the same.
+          assert_matrices_approx_equal(input_pose.gripMatrix, VALID_GRIP, FLOAT_EPSILON, "A");
+          assert_matrices_approx_equal(input_pose.pointerMatrix, input_pose.gripMatrix, FLOAT_EPSILON, "B");
+        });
+
+        input_source.pointerOffset = VALID_POINTER_OFFSET;
+
+        session.requestAnimationFrame(CheckValidGripAndPointer);
+      }
+
+      function CheckValidGripAndPointer(time, xrFrame) {
+        let source = session.getInputSources()[0];
+
+        let input_pose = xrFrame.getInputPose(source, frameOfRef);
+
+        t.step( () => {
+          // When a grip matrix and pointer offset are specified,
+          // pointer matrix should be grip matrix multiplied with the pointer
+          // offset.
+          assert_matrices_approx_equal(input_pose.gripMatrix, VALID_GRIP, FLOAT_EPSILON, "C");
+          assert_matrices_approx_equal(input_pose.pointerMatrix, VALID_GRIP_WITH_POINTER_OFFSET, FLOAT_EPSILON, "D");
+        });
+
+        resolve();
+      }
+
+      // Can only request input poses in an xr frame.
+      session.requestAnimationFrame(CheckInvalidGrip);
+    });
+  }), fakeDevices["FakeGooglePixelPhone"], [ { exclusive: true } ],
+"XRInputSources with a pointer origin of 'hand' properly communicate their poses");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/xr/resources/test-constants.js b/third_party/WebKit/LayoutTests/xr/resources/test-constants.js
index 56612113..afa7aa15 100644
--- a/third_party/WebKit/LayoutTests/xr/resources/test-constants.js
+++ b/third_party/WebKit/LayoutTests/xr/resources/test-constants.js
@@ -11,3 +11,20 @@
   angularVelocity: [1.1, 2.1, 3.1],
   angularAcceleration: [1.0, 2.0, 3.0]
 }
+
+// A valid input grip matrix for  when we don't care about specific values
+var VALID_GRIP = [1, 0, 0, 0,
+                  0, 1, 0, 0,
+                  0, 0, 1, 0,
+                  4, 3, 2, 1];
+
+// A valid input pointer offset for  when we don't care about specific values
+var VALID_POINTER_OFFSET = [1, 0, 0, 0,
+                            0, 1, 0, 0,
+                            0, 0, 1, 0,
+                            0, 0, 1, 1];
+
+var VALID_GRIP_WITH_POINTER_OFFSET = [1, 0, 0, 0,
+                                      0, 1, 0, 0,
+                                      0, 0, 1, 0,
+                                      4, 3, 3, 1];
diff --git a/third_party/WebKit/LayoutTests/xr/resources/xr-device-mocking.js b/third_party/WebKit/LayoutTests/xr/resources/xr-device-mocking.js
index ab56df9..f36ebf3 100644
--- a/third_party/WebKit/LayoutTests/xr/resources/xr-device-mocking.js
+++ b/third_party/WebKit/LayoutTests/xr/resources/xr-device-mocking.js
@@ -36,6 +36,14 @@
   return mockVRService.mockVRDisplays_[0].getSubmitFrameCount();
 }
 
+function addInputSource(input_source) {
+  return mockVRService.mockVRDisplays_[0].addInputSource(input_source);
+}
+
+function removeInputSource(input_source) {
+  return mockVRService.mockVRDisplays_[0].removeInputSource(input_source);
+}
+
 function fakeXRDevices() {
   let generic_left_fov = {
     upDegrees: 45,
@@ -202,7 +210,7 @@
   return out;
 }
 
-function assert_matrices_approx_equal(matA, matB, epsilon = FLOAT_EPSILON) {
+function assert_matrices_approx_equal(matA, matB, epsilon = FLOAT_EPSILON, message = "") {
   if (matA == null && matB == null) {
     return;
   }
@@ -212,8 +220,32 @@
 
   assert_equals(matA.length, 16);
   assert_equals(matB.length, 16);
+
+  let mismatched_element = -1;
   for (let i = 0; i < 16; ++i) {
-    assert_approx_equals(matA[i], matB[i], epsilon);
+    if (Math.abs(matA[i] - matB[i]) > epsilon) {
+      mismatched_element = i;
+      break;
+    }
+  }
+
+  if (mismatched_element > -1) {
+    let matA_str = "[";
+    let matB_str = "[";
+    for (let i = 0; i < 16; ++i) {
+      matA_str += matA[i] + (i < 15 ? ", " : "");
+      matB_str += matB[i] + (i < 15 ? ", " : "");
+    }
+    matA_str += "]";
+    matB_str += "]";
+
+    let error_message = message ? message + "\n" : "Matrix comparison failed.\n";
+    error_message += " Difference in element " + mismatched_element + " exceeded the given epsilon.\n";
+
+    error_message += " Matrix A: " + matA_str + "\n";
+    error_message += " Matrix B: " + matB_str + "\n";
+
+    assert_approx_equals(matA[mismatched_element], matB[mismatched_element], epsilon, error_message);
   }
 }
 
@@ -281,13 +313,25 @@
     this.service_.client_.onDisplayConnected(
         magicWindowPtr, displayPtr, clientRequest, this.displayInfo_);
   }
+
+  addInputSource(input_source) {
+    this.presentation_provider_.OnInputSourceAdded(input_source);
+  }
+
+  removeInputSource(input_source) {
+    this.presentation_provider_.OnInputSourceRemoved(input_source);
+  }
 }
 
 class MockVRPresentationProvider {
   constructor() {
     this.binding_ = new mojo.Binding(device.mojom.VRPresentationProvider, this);
     this.pose_ = null;
+    this.next_frame_id_ = 0;
     this.submit_frame_count_ = 0;
+
+    this.input_sources_ = [];
+    this.next_input_source_index_ = 1;
   }
 
   bind(client, request) {
@@ -311,6 +355,13 @@
   getVSync() {
     if (this.pose_) {
       this.pose_.poseIndex++;
+
+      let input_states = [];
+      for (let i = 0; i < this.input_sources_.length; ++i) {
+        input_states.push(this.input_sources_[i].getInputSourceState());
+      }
+
+      this.pose_.inputState = input_states;
     }
 
     // Convert current document time to monotonic time.
@@ -324,7 +375,7 @@
       time: {
         microseconds: now,
       },
-      frameId: 0,
+      frameId: this.next_frame_id_++,
       status: device.mojom.VRPresentationProvider.VSyncStatus.SUCCESS,
     });
 
@@ -339,6 +390,7 @@
       linearVelocity: null,
       angularAcceleration: null,
       linearAcceleration: null,
+      inputState: null,
       poseIndex: 0
     };
   }
@@ -350,6 +402,22 @@
       }
     }
   }
+
+  OnInputSourceAdded(input_source) {
+    let index = this.next_input_source_index_;
+    input_source.source_id_ = index;
+    this.next_input_source_index_++;
+    this.input_sources_.push(input_source);
+  }
+
+  OnInputSourceRemoved(input_source) {
+    for (let i = 0; i < this.input_sources_.length; ++i) {
+      if (input_source.source_id_ == this.input_sources_[i].source_id_) {
+        this.input_sources_.splice(i, 1);
+        break;
+      }
+    }
+  }
 }
 
 class MockVRService {
@@ -392,4 +460,142 @@
     let device_number = this.mockVRDisplays_.length;
     return Promise.resolve({numberOfConnectedDevices: device_number});
   }
-}
\ No newline at end of file
+}
+
+class MockXRInputSource {
+  constructor() {
+    this.source_id_ = 0;
+    this.primary_input_pressed_ = false;
+    this.primary_input_clicked_ = false;
+    this.grip_ = null;
+
+    this.pointer_origin_ = "head";
+    this.pointer_offset_ = null;
+    this.emulated_position_ = false;
+    this.handedness_ = "";
+    this.desc_dirty_ = true;
+  }
+
+  get primaryInputPressed() {
+    return this.primary_input_pressed_;
+  }
+
+  set primaryInputPressed(value) {
+    if (this.primary_input_pressed_ && !value) {
+      this.primary_input_clicked_ = true;
+    }
+    this.primary_input_pressed_ = value;
+  }
+
+  get grip() {
+    if (this.grip_) {
+      return this.grip_.matrix;
+    }
+    return null;
+  }
+
+  set grip(value) {
+    if (!value) {
+      this.grip_ = null;
+      return;
+    }
+    this.grip_ = new gfx.mojom.Transform();
+    this.grip_.matrix = new Float32Array(value);
+  }
+
+  get pointerOrigin() {
+    return this.pointer_origin_;
+  }
+
+  set pointerOrigin(value) {
+    if (this.pointer_origin_ != value) {
+      this.desc_dirty_ = true;
+      this.pointer_origin_ = value;
+    }
+  }
+
+  get pointerOffset() {
+    if (this.pointer_offset_) {
+      return this.pointer_offset_.matrix;
+    }
+    return null;
+  }
+
+  set pointerOffset(value) {
+    this.desc_dirty_ = true;
+    if (!value) {
+      this.pointer_offset_ = null;
+      return;
+    }
+    this.pointer_offset_ = new gfx.mojom.Transform();
+    this.pointer_offset_.matrix = new Float32Array(value);
+  }
+
+  get emulatedPosition() {
+    return this.emulated_position_;
+  }
+
+  set emulatedPosition(value) {
+    if (this.emulated_position_ != value) {
+      this.desc_dirty_ = true;
+      this.emulated_position_ = value;
+    }
+  }
+
+  get handedness() {
+    return this.handedness_;
+  }
+
+  set handedness(value) {
+    if (this.handedness_ != value) {
+      this.desc_dirty_ = true;
+      this.handedness_ = value;
+    }
+  }
+
+  getInputSourceState() {
+    let input_state = new device.mojom.XRInputSourceState();
+
+    input_state.sourceId = this.source_id_;
+
+    input_state.primaryInputPressed = this.primary_input_pressed_;
+    input_state.primaryInputClicked = this.primary_input_clicked_;
+
+    input_state.grip = this.grip_;
+
+    if (this.desc_dirty_) {
+      let input_desc = new device.mojom.XRInputSourceDescription();
+
+      input_desc.emulatedPosition = this.emulated_position_;
+
+      switch (this.pointer_origin_) {
+        case "head":
+          input_desc.pointerOrigin = device.mojom.XRPointerOrigin.HEAD;
+          break;
+        case "hand":
+          input_desc.pointerOrigin = device.mojom.XRPointerOrigin.HAND;
+          break;
+      }
+
+      switch (this.handedness_) {
+        case "left":
+          input_desc.handedness = device.mojom.XRHandedness.LEFT;
+          break;
+        case "right":
+          input_desc.handedness = device.mojom.XRHandedness.RIGHT;
+          break;
+        default:
+          input_desc.handedness = device.mojom.XRHandedness.NONE;
+          break;
+      }
+
+      input_desc.pointerOffset = this.pointer_offset_;
+
+      input_state.description = input_desc;
+
+      this.desc_dirty_ = false;
+    }
+
+    return input_state;
+  }
+}
diff --git a/third_party/WebKit/LayoutTests/xr/xrInputSource_add_remove.html b/third_party/WebKit/LayoutTests/xr/xrInputSource_add_remove.html
new file mode 100644
index 0000000..e215aff
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/xr/xrInputSource_add_remove.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
+<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../xr/resources/xr-test-utils.js"></script>
+<script src="../xr/resources/test-constants.js"></script>
+<canvas id="webgl-canvas"></canvas>
+
+<script>
+let fakeDevices = fakeXRDevices();
+
+xr_session_promise_test( (session, t) => new Promise((resolve) => {
+    // Session must have a baseLayer or frame requests will be ignored.
+    session.baseLayer = new XRWebGLLayer(session, gl);
+
+    // Need to have a valid pose or input updates don't process.
+    setPose(VALID_POSE);
+
+    let input_sources = session.getInputSources();
+
+    t.step( () => {
+      assert_equals(input_sources.length, 0);
+    });
+
+    let input_source_1 = new MockXRInputSource();
+    input_source_1.pointerOrigin = "hand";
+    input_source_1.handedness = "right";
+
+    addInputSource(input_source_1);
+
+    session.requestAnimationFrame((time, xrFrame) => {
+      let input_sources = session.getInputSources();
+
+      t.step( () => {
+        assert_equals(input_sources.length, 1);
+        assert_equals(input_sources[0].pointerOrigin, "hand");
+        assert_equals(input_sources[0].handedness, "right");
+      });
+
+      let input_source_2 = new MockXRInputSource();
+      input_source_2.pointerOrigin = "head";
+      input_source_2.emulatedPosition = "true";
+      addInputSource(input_source_2);
+
+      session.requestAnimationFrame((time, xrFrame) => {
+        let input_sources = session.getInputSources();
+
+        t.step( () => {
+          assert_equals(input_sources.length, 2);
+          assert_equals(input_sources[1].pointerOrigin, "head");
+          assert_equals(input_sources[1].handedness, "");
+        });
+
+        removeInputSource(input_source_1);
+
+        session.requestAnimationFrame((time, xrFrame) => {
+          let input_sources = session.getInputSources();
+
+          t.step( () => {
+            assert_equals(input_sources.length, 1);
+            assert_equals(input_sources[0].pointerOrigin, "head");
+            assert_equals(input_sources[0].handedness, "");
+          });
+
+          resolve();
+        });
+      });
+    });
+  }), fakeDevices["FakeGooglePixelPhone"], [ { exclusive: true } ],
+"XRInputSources can be properly added and removed from the session");
+
+</script>
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableMarkingVisitorTest.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableMarkingVisitorTest.cpp
index 78da3da9..1bd9a08 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableMarkingVisitorTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableMarkingVisitorTest.cpp
@@ -501,7 +501,6 @@
       DeathAwareScriptWrappable::Create();
   Base* base = Base::Create(base_wrapper, mixin_wrapper);
   Mixin* mixin = static_cast<Mixin*>(base);
-
   HeapObjectHeader* base_header = HeapObjectHeader::FromPayload(base);
   EXPECT_FALSE(base_header->IsWrapperHeaderMarked());
 
@@ -516,7 +515,7 @@
   TraceWrapperMember<Mixin> mixin_handle = mixin;
   EXPECT_TRUE(base_header->IsWrapperHeaderMarked());
   EXPECT_FALSE(visitor->MarkingDeque()->IsEmpty());
-  EXPECT_TRUE(visitor->MarkingDequeContains(mixin));
+  EXPECT_TRUE(visitor->MarkingDequeContains(base));
 
   visitor->AdvanceTracing(
       0, v8::EmbedderHeapTracer::AdvanceTracingActions(
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8EmbedderGraphBuilder.cpp b/third_party/WebKit/Source/bindings/core/v8/V8EmbedderGraphBuilder.cpp
index ecf0a5a..5bca7e4a 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8EmbedderGraphBuilder.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8EmbedderGraphBuilder.cpp
@@ -61,10 +61,10 @@
 }
 
 void V8EmbedderGraphBuilder::Visit(
-    const WrapperDescriptor& wrapper_descriptor) const {
+    const TraceWrapperDescriptor& wrapper_descriptor) const {
   // Add an edge from the current parent to this object.
   // Also push the object to the worklist in order to process its members.
-  const void* traceable = wrapper_descriptor.traceable;
+  const void* traceable = wrapper_descriptor.base_object_payload;
   Graph::Node* graph_node =
       GraphNode(traceable, wrapper_descriptor.name_callback(traceable));
   graph_->AddEdge(current_parent_, graph_node);
@@ -112,8 +112,8 @@
 
 V8EmbedderGraphBuilder::WorklistItem V8EmbedderGraphBuilder::ToWorklistItem(
     Graph::Node* node,
-    const WrapperDescriptor& wrapper_descriptor) const {
-  return {node, wrapper_descriptor.traceable,
+    const TraceWrapperDescriptor& wrapper_descriptor) const {
+  return {node, wrapper_descriptor.base_object_payload,
           wrapper_descriptor.trace_wrappers_callback};
 }
 
@@ -123,7 +123,7 @@
     auto item = worklist_.back();
     worklist_.pop_back();
     ParentScope parent(this, item.node);
-    item.trace_wrappers_callback(this, item.traceable);
+    item.trace_wrappers_callback(this, const_cast<void*>(item.traceable));
   }
 }
 
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8EmbedderGraphBuilder.h b/third_party/WebKit/Source/bindings/core/v8/V8EmbedderGraphBuilder.h
index 9ab8f89..6210fef 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8EmbedderGraphBuilder.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8EmbedderGraphBuilder.h
@@ -29,7 +29,7 @@
  protected:
   // ScriptWrappableVisitor overrides.
   void Visit(const TraceWrapperV8Reference<v8::Value>&) const final;
-  void Visit(const WrapperDescriptor&) const final;
+  void Visit(const TraceWrapperDescriptor&) const final;
   void Visit(DOMWrapperMap<ScriptWrappable>*,
              const ScriptWrappable*) const final;
 
@@ -74,7 +74,8 @@
     TraceWrappersCallback trace_wrappers_callback;
   };
 
-  WorklistItem ToWorklistItem(Graph::Node*, const WrapperDescriptor&) const;
+  WorklistItem ToWorklistItem(Graph::Node*,
+                              const TraceWrapperDescriptor&) const;
 
   Graph::Node* GraphNode(const v8::Local<v8::Value>&) const;
   Graph::Node* GraphNode(Traceable, const char* name) const;
diff --git a/third_party/WebKit/Source/bindings/modules/BUILD.gn b/third_party/WebKit/Source/bindings/modules/BUILD.gn
index f160b5f..b19f82f 100644
--- a/third_party/WebKit/Source/bindings/modules/BUILD.gn
+++ b/third_party/WebKit/Source/bindings/modules/BUILD.gn
@@ -60,6 +60,7 @@
     "//third_party/WebKit/Source/modules/webmidi/MIDIConnectionEvent.idl",
     "//third_party/WebKit/Source/modules/webmidi/MIDIMessageEvent.idl",
     "//third_party/WebKit/Source/modules/websockets/CloseEvent.idl",
+    "//third_party/WebKit/Source/modules/xr/XRInputSourceEvent.idl",
     "//third_party/WebKit/Source/modules/xr/XRSessionEvent.idl",
   ]
   output_file = "event_modules_names.json5"
diff --git a/third_party/WebKit/Source/core/animation/Animation.cpp b/third_party/WebKit/Source/core/animation/Animation.cpp
index 10ab095..0b8be045 100644
--- a/third_party/WebKit/Source/core/animation/Animation.cpp
+++ b/third_party/WebKit/Source/core/animation/Animation.cpp
@@ -115,7 +115,7 @@
     : ContextLifecycleObserver(execution_context),
       play_state_(kIdle),
       playback_rate_(1),
-      start_time_(NullValue()),
+      start_time_(),
       hold_time_(0),
       sequence_number_(NextSequenceNumber()),
       content_(content),
@@ -185,14 +185,14 @@
   bool old_held = held_;
   bool outdated = false;
   bool is_limited = Limited(new_current_time);
-  held_ = paused_ || !playback_rate_ || is_limited || std::isnan(start_time_);
+  held_ = paused_ || !playback_rate_ || is_limited || !start_time_;
   if (held_) {
     if (!old_held || hold_time_ != new_current_time)
       outdated = true;
     hold_time_ = new_current_time;
     if (paused_ || !playback_rate_) {
-      start_time_ = NullValue();
-    } else if (is_limited && std::isnan(start_time_) &&
+      start_time_ = WTF::nullopt;
+    } else if (is_limited && !start_time_ &&
                reason == kTimingUpdateForAnimationFrame) {
       start_time_ = CalculateStartTime(new_current_time);
     }
@@ -214,7 +214,7 @@
     return;
   if (held_) {
     double new_current_time = hold_time_;
-    if (play_state_ == kFinished && !IsNull(start_time_) && timeline_) {
+    if (play_state_ == kFinished && start_time_ && timeline_) {
       // Add hystersis due to floating point error accumulation
       if (!Limited(CalculateCurrentTime() + 0.001 * playback_rate_)) {
         // The current time became unlimited, eg. due to a backwards
@@ -235,13 +235,14 @@
 }
 
 double Animation::startTime(bool& is_null) const {
-  double result = startTime();
-  is_null = std::isnan(result);
-  return result;
+  WTF::Optional<double> result = startTime();
+  is_null = !result;
+  return result.value_or(0);
 }
 
-double Animation::startTime() const {
-  return start_time_ * 1000;
+WTF::Optional<double> Animation::startTime() const {
+  return start_time_ ? WTF::make_optional(start_time_.value() * 1000)
+                     : WTF::nullopt;
 }
 
 double Animation::currentTime(bool& is_null) {
@@ -253,7 +254,7 @@
 double Animation::currentTime() {
   PlayStateUpdateScope update_scope(*this, kTimingUpdateOnDemand);
 
-  if (PlayStateInternal() == kIdle || (!held_ && !HasStartTime()))
+  if (PlayStateInternal() == kIdle || (!held_ && !start_time_))
     return std::numeric_limits<double>::quiet_NaN();
 
   return CurrentTimeInternal() * 1000;
@@ -277,7 +278,7 @@
 #if DCHECK_IS_ON()
   CurrentTimeInternal();
 #endif
-  return PlayStateInternal() == kPaused || IsNull(start_time_)
+  return PlayStateInternal() == kPaused || !start_time_
              ? CurrentTimeInternal()
              : CalculateCurrentTime();
 }
@@ -314,7 +315,7 @@
     compositor_state_ = nullptr;
   }
 
-  DCHECK(!compositor_state_ || !std::isnan(compositor_state_->start_time));
+  DCHECK(!compositor_state_ || compositor_state_->start_time);
 
   if (!should_start) {
     current_time_pending_ = false;
@@ -367,17 +368,18 @@
 
   switch (compositor_state_->pending_action) {
     case kStart:
-      if (!std::isnan(compositor_state_->start_time)) {
-        DCHECK_EQ(start_time_, compositor_state_->start_time);
+      if (compositor_state_->start_time) {
+        DCHECK_EQ(start_time_.value(), compositor_state_->start_time.value());
         compositor_state_->pending_action = kNone;
       }
       break;
     case kPause:
     case kPauseThenStart:
-      DCHECK(std::isnan(start_time_));
+      DCHECK(!start_time_);
       compositor_state_->pending_action = kNone;
       SetCurrentTimeInternal(
-          (timeline_time - compositor_state_->start_time) * playback_rate_,
+          (timeline_time - compositor_state_->start_time.value()) *
+              playback_rate_,
           kTimingUpdateForAnimationFrame);
       current_time_pending_ = false;
       break;
@@ -392,12 +394,14 @@
 
   if (compositor_state_) {
     DCHECK_EQ(compositor_state_->pending_action, kStart);
-    DCHECK(std::isnan(compositor_state_->start_time));
+    DCHECK(!compositor_state_->start_time);
 
     double initial_compositor_hold_time = compositor_state_->hold_time;
     compositor_state_->pending_action = kNone;
+    // TODO(crbug.com/791086): Determine whether this can ever be null.
+    double start_time = timeline_time + CurrentTimeInternal() / -playback_rate_;
     compositor_state_->start_time =
-        timeline_time + CurrentTimeInternal() / -playback_rate_;
+        IsNull(start_time) ? WTF::nullopt : WTF::make_optional(start_time);
 
     if (start_time_ == timeline_time) {
       // The start time was set to the incoming compositor start time.
@@ -408,8 +412,7 @@
       return;
     }
 
-    if (!std::isnan(start_time_) ||
-        CurrentTimeInternal() != initial_compositor_hold_time) {
+    if (start_time_ || CurrentTimeInternal() != initial_compositor_hold_time) {
       // A new start time or current time was set while starting.
       SetCompositorPending(true);
       return;
@@ -421,7 +424,7 @@
 
 void Animation::NotifyStartTime(double timeline_time) {
   if (Playing()) {
-    DCHECK(std::isnan(start_time_));
+    DCHECK(!start_time_);
     DCHECK(held_);
 
     if (playback_rate_ == 0) {
@@ -450,16 +453,21 @@
          effect->Affects(PropertyHandle(property));
 }
 
-double Animation::CalculateStartTime(double current_time) const {
-  return timeline_->EffectiveTime() - current_time / playback_rate_;
+WTF::Optional<double> Animation::CalculateStartTime(double current_time) const {
+  WTF::Optional<double> start_time =
+      timeline_->EffectiveTime() - current_time / playback_rate_;
+  DCHECK(!IsNull(start_time.value()));
+  return start_time;
 }
 
 double Animation::CalculateCurrentTime() const {
-  if (IsNull(start_time_) || !timeline_)
+  if (!start_time_ || !timeline_)
     return 0;
-  return (timeline_->EffectiveTime() - start_time_) * playback_rate_;
+  return (timeline_->EffectiveTime() - start_time_.value()) * playback_rate_;
 }
 
+// TODO(crbug.com/771722): This doesn't handle anim.startTime = null; we just
+// silently convert that to anim.startTime = 0.
 void Animation::setStartTime(double start_time, bool is_null) {
   PlayStateUpdateScope update_scope(*this, kTimingUpdateOnDemand);
 
@@ -472,12 +480,12 @@
   SetStartTimeInternal(start_time / 1000);
 }
 
-void Animation::SetStartTimeInternal(double new_start_time) {
+void Animation::SetStartTimeInternal(WTF::Optional<double> new_start_time) {
   DCHECK(!paused_);
-  DCHECK(std::isfinite(new_start_time));
-  DCHECK_NE(new_start_time, start_time_);
+  DCHECK(new_start_time.has_value());
+  DCHECK(new_start_time != start_time_);
 
-  bool had_start_time = HasStartTime();
+  bool had_start_time = start_time_.has_value();
   double previous_current_time = CurrentTimeInternal();
   start_time_ = new_start_time;
   if (held_ && playback_rate_) {
@@ -549,12 +557,12 @@
   return play_state_;
 }
 
-Animation::AnimationPlayState Animation::CalculatePlayState() {
+Animation::AnimationPlayState Animation::CalculatePlayState() const {
   if (paused_ && !current_time_pending_)
     return kPaused;
   if (play_state_ == kIdle)
     return kIdle;
-  if (current_time_pending_ || (IsNull(start_time_) && playback_rate_ != 0))
+  if (current_time_pending_ || (!start_time_ && playback_rate_ != 0))
     return kPending;
   if (Limited())
     return kFinished;
@@ -615,7 +623,7 @@
   }
 
   if (!Playing()) {
-    start_time_ = NullValue();
+    start_time_ = WTF::nullopt;
   }
 
   if (PlayStateInternal() == kIdle) {
@@ -628,11 +636,11 @@
   UnpauseInternal();
 
   if (playback_rate_ > 0 && (current_time < 0 || current_time >= EffectEnd())) {
-    start_time_ = NullValue();
+    start_time_ = WTF::nullopt;
     SetCurrentTimeInternal(0, kTimingUpdateOnDemand);
   } else if (playback_rate_ < 0 &&
              (current_time <= 0 || current_time > EffectEnd())) {
-    start_time_ = NullValue();
+    start_time_ = WTF::nullopt;
     SetCurrentTimeInternal(EffectEnd(), kTimingUpdateOnDemand);
   }
 }
@@ -737,12 +745,12 @@
 
   PlayStateUpdateScope update_scope(*this, kTimingUpdateOnDemand);
 
-  double start_time_before = start_time_;
+  WTF::Optional<double> start_time_before = start_time_;
   SetPlaybackRateInternal(playback_rate);
 
   // Adds a UseCounter to check if setting playbackRate causes a compensatory
   // seek forcing a change in start_time_
-  if (!std::isnan(start_time_before) && start_time_ != start_time_before &&
+  if (start_time_before && start_time_ != start_time_before &&
       play_state_ != kFinished) {
     UseCounter::Count(GetExecutionContext(),
                       WebFeature::kAnimationSetPlaybackRateCompensatorySeek);
@@ -753,7 +761,7 @@
   DCHECK(std::isfinite(playback_rate));
   DCHECK_NE(playback_rate, playback_rate_);
 
-  if (!Limited() && !Paused() && HasStartTime())
+  if (!Limited() && !Paused() && start_time_)
     current_time_pending_ = true;
 
   double stored_current_time = CurrentTimeInternal();
@@ -762,7 +770,7 @@
     finished_ = false;
 
   playback_rate_ = playback_rate;
-  start_time_ = std::numeric_limits<double>::quiet_NaN();
+  start_time_ = WTF::nullopt;
   SetCurrentTimeInternal(stored_current_time, kTimingUpdateOnDemand);
 }
 
@@ -842,7 +850,7 @@
 
   // If the optional element id set has no value we must be in SPv1 mode in
   // which case we trust the compositing logic will create a layer if needed.
-  if (composited_element_ids.has_value()) {
+  if (composited_element_ids) {
     DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
     Element* target_element =
         ToKeyframeEffectReadOnly(content_.Get())->target();
@@ -883,17 +891,19 @@
 
   bool reversed = playback_rate_ < 0;
 
-  double start_time = TimelineInternal()->ZeroTime() + StartTimeInternal();
-  if (reversed) {
-    start_time -= EffectEnd() / fabs(playback_rate_);
-  }
-
+  // TODO(crbug.com/791086): Make StartAnimationOnCompositor use WTF::Optional.
+  double start_time = NullValue();
   double time_offset = 0;
-  if (std::isnan(start_time)) {
+  if (start_time_) {
+    start_time = TimelineInternal()->ZeroTime() + start_time_.value();
+    if (reversed)
+      start_time -= EffectEnd() / fabs(playback_rate_);
+  } else {
     time_offset =
         reversed ? EffectEnd() - CurrentTimeInternal() : CurrentTimeInternal();
     time_offset = time_offset / fabs(playback_rate_);
   }
+
   DCHECK_NE(compositor_group_, 0);
   ToKeyframeEffectReadOnly(content_.Get())
       ->StartAnimationOnCompositor(compositor_group_, start_time, time_offset,
@@ -970,7 +980,7 @@
   }
 
   if ((idle || Limited()) && !finished_) {
-    if (reason == kTimingUpdateForAnimationFrame && (idle || HasStartTime())) {
+    if (reason == kTimingUpdateForAnimationFrame && (idle || start_time_)) {
       if (idle) {
         const AtomicString& event_type = EventTypeNames::cancel;
         if (GetExecutionContext() && HasEventListeners(event_type)) {
@@ -1016,12 +1026,12 @@
 }
 
 bool Animation::IsEventDispatchAllowed() const {
-  return Paused() || HasStartTime();
+  return Paused() || start_time_;
 }
 
 double Animation::TimeToEffectChange() {
   DCHECK(!outdated_);
-  if (!HasStartTime() || held_)
+  if (!start_time_ || held_)
     return std::numeric_limits<double>::infinity();
 
   if (!content_)
@@ -1045,7 +1055,7 @@
   held_ = false;
   paused_ = false;
   play_state_ = kIdle;
-  start_time_ = NullValue();
+  start_time_ = WTF::nullopt;
   current_time_pending_ = false;
   ForceServiceOnNextFrame();
 }
diff --git a/third_party/WebKit/Source/core/animation/Animation.h b/third_party/WebKit/Source/core/animation/Animation.h
index 02a8091..79fbe9cb 100644
--- a/third_party/WebKit/Source/core/animation/Animation.h
+++ b/third_party/WebKit/Source/core/animation/Animation.h
@@ -52,6 +52,7 @@
 #include "platform/animation/CompositorAnimationDelegate.h"
 #include "platform/graphics/CompositorElementId.h"
 #include "platform/heap/Handle.h"
+#include "platform/wtf/Optional.h"
 
 namespace blink {
 
@@ -155,13 +156,10 @@
   const DocumentTimeline* TimelineInternal() const { return timeline_; }
   DocumentTimeline* TimelineInternal() { return timeline_; }
 
-  double CalculateStartTime(double current_time) const;
-  bool HasStartTime() const { return !IsNull(start_time_); }
   double startTime(bool& is_null) const;
-  double startTime() const;
-  double StartTimeInternal() const { return start_time_; }
+  WTF::Optional<double> startTime() const;
+  WTF::Optional<double> StartTimeInternal() const { return start_time_; }
   void setStartTime(double, bool is_null);
-  void SetStartTimeInternal(double);
 
   const AnimationEffectReadOnly* effect() const { return content_.Get(); }
   AnimationEffectReadOnly* effect() { return content_.Get(); }
@@ -240,11 +238,13 @@
   double EffectEnd() const;
   bool Limited(double current_time) const;
 
-  AnimationPlayState CalculatePlayState();
+  AnimationPlayState CalculatePlayState() const;
+  WTF::Optional<double> CalculateStartTime(double current_time) const;
   double CalculateCurrentTime() const;
 
   void UnpauseInternal();
   void SetPlaybackRateInternal(double);
+  void SetStartTimeInternal(WTF::Optional<double>);
   void UpdateCurrentTimingState(TimingUpdateReason);
 
   void BeginUpdatingState();
@@ -274,7 +274,7 @@
 
   AnimationPlayState play_state_;
   double playback_rate_;
-  double start_time_;
+  WTF::Optional<double> start_time_;
   double hold_time_;
 
   unsigned sequence_number_;
@@ -315,7 +315,7 @@
           playback_rate(animation.playback_rate_),
           effect_changed(false),
           pending_action(kStart) {}
-    double start_time;
+    WTF::Optional<double> start_time;
     double hold_time;
     double playback_rate;
     bool effect_changed;
diff --git a/third_party/WebKit/Source/core/animation/AnimationTest.cpp b/third_party/WebKit/Source/core/animation/AnimationTest.cpp
index d2182ac..07d986a 100644
--- a/third_party/WebKit/Source/core/animation/AnimationTest.cpp
+++ b/third_party/WebKit/Source/core/animation/AnimationTest.cpp
@@ -108,8 +108,7 @@
   EXPECT_EQ(0, animation->CurrentTimeInternal());
   EXPECT_FALSE(animation->Paused());
   EXPECT_EQ(1, animation->playbackRate());
-  EXPECT_FALSE(animation->HasStartTime());
-  EXPECT_TRUE(IsNull(animation->StartTimeInternal()));
+  EXPECT_FALSE(animation->StartTimeInternal());
 
   StartTimeline();
   EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
@@ -117,8 +116,7 @@
   EXPECT_EQ(0, animation->CurrentTimeInternal());
   EXPECT_FALSE(animation->Paused());
   EXPECT_EQ(1, animation->playbackRate());
-  EXPECT_EQ(0, animation->StartTimeInternal());
-  EXPECT_TRUE(animation->HasStartTime());
+  EXPECT_EQ(0, animation->StartTimeInternal().value());
 }
 
 TEST_F(AnimationAnimationTest, CurrentTimeDoesNotSetOutdated) {
@@ -212,14 +210,14 @@
 TEST_F(AnimationAnimationTest, SetStartTime) {
   SimulateFrame(20);
   EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
-  EXPECT_EQ(0, animation->StartTimeInternal());
+  EXPECT_EQ(0, animation->StartTimeInternal().value());
   EXPECT_EQ(20 * 1000, animation->currentTime());
   animation->setStartTime(10 * 1000, false);
   EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
-  EXPECT_EQ(10, animation->StartTimeInternal());
+  EXPECT_EQ(10, animation->StartTimeInternal().value());
   EXPECT_EQ(10 * 1000, animation->currentTime());
   SimulateFrame(30);
-  EXPECT_EQ(10, animation->StartTimeInternal());
+  EXPECT_EQ(10, animation->StartTimeInternal().value());
   EXPECT_EQ(20 * 1000, animation->currentTime());
   animation->setStartTime(-20 * 1000, false);
   EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
@@ -253,7 +251,7 @@
   NonThrowableExceptionState exception_state;
   animation->pause();
   EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
-  EXPECT_TRUE(std::isnan(animation->startTime()));
+  EXPECT_FALSE(animation->startTime());
   animation->finish(exception_state);
   EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
   EXPECT_EQ(-30000, animation->startTime());
@@ -274,13 +272,13 @@
   animation->finish(exception_state);
   EXPECT_EQ(-30 * 1000, animation->startTime());
   animation->pause();
-  EXPECT_TRUE(std::isnan(animation->startTime()));
+  EXPECT_FALSE(animation->startTime());
 }
 
 TEST_F(AnimationAnimationTest, StartTimeWithZeroPlaybackRate) {
   animation->setPlaybackRate(0);
   EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
-  EXPECT_TRUE(std::isnan(animation->startTime()));
+  EXPECT_FALSE(animation->startTime());
   SimulateFrame(10);
   EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
 }
@@ -733,11 +731,11 @@
   animation->cancel();
   EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
   EXPECT_TRUE(std::isnan(animation->currentTime()));
-  EXPECT_TRUE(std::isnan(animation->startTime()));
+  EXPECT_FALSE(animation->startTime());
   animation->play();
   EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
   EXPECT_EQ(0, animation->currentTime());
-  EXPECT_TRUE(std::isnan(animation->startTime()));
+  EXPECT_FALSE(animation->startTime());
   SimulateFrame(10);
   EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
   EXPECT_EQ(0, animation->currentTime());
@@ -751,11 +749,11 @@
   animation->cancel();
   EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
   EXPECT_TRUE(std::isnan(animation->currentTime()));
-  EXPECT_TRUE(std::isnan(animation->startTime()));
+  EXPECT_FALSE(animation->startTime());
   animation->play();
   EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
   EXPECT_EQ(30 * 1000, animation->currentTime());
-  EXPECT_TRUE(std::isnan(animation->startTime()));
+  EXPECT_FALSE(animation->startTime());
   SimulateFrame(10);
   EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
   EXPECT_EQ(30 * 1000, animation->currentTime());
@@ -766,11 +764,11 @@
   animation->cancel();
   EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
   EXPECT_TRUE(std::isnan(animation->currentTime()));
-  EXPECT_TRUE(std::isnan(animation->startTime()));
+  EXPECT_FALSE(animation->startTime());
   animation->reverse();
   EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
   EXPECT_EQ(30 * 1000, animation->currentTime());
-  EXPECT_TRUE(std::isnan(animation->startTime()));
+  EXPECT_FALSE(animation->startTime());
   SimulateFrame(10);
   EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
   EXPECT_EQ(30 * 1000, animation->currentTime());
@@ -782,7 +780,7 @@
   animation->cancel();
   EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
   EXPECT_TRUE(std::isnan(animation->currentTime()));
-  EXPECT_TRUE(std::isnan(animation->startTime()));
+  EXPECT_FALSE(animation->startTime());
   animation->finish(exception_state);
   EXPECT_EQ(30000, animation->currentTime());
   EXPECT_EQ(-30000, animation->startTime());
@@ -793,11 +791,11 @@
   animation->cancel();
   EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
   EXPECT_TRUE(std::isnan(animation->currentTime()));
-  EXPECT_TRUE(std::isnan(animation->startTime()));
+  EXPECT_FALSE(animation->startTime());
   animation->pause();
   EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
   EXPECT_EQ(0, animation->currentTime());
-  EXPECT_TRUE(std::isnan(animation->startTime()));
+  EXPECT_FALSE(animation->startTime());
 }
 
 TEST_F(AnimationAnimationTest, NoCompositeWithoutCompositedElementId) {
diff --git a/third_party/WebKit/Source/core/animation/PendingAnimations.cpp b/third_party/WebKit/Source/core/animation/PendingAnimations.cpp
index af6ded87..a5688d4 100644
--- a/third_party/WebKit/Source/core/animation/PendingAnimations.cpp
+++ b/third_party/WebKit/Source/core/animation/PendingAnimations.cpp
@@ -76,14 +76,14 @@
         animation->HasActiveAnimationsOnCompositor();
     // Animations with a start time do not participate in compositor start-time
     // grouping.
-    if (animation->PreCommit(animation->HasStartTime() ? 1 : compositor_group,
+    if (animation->PreCommit(animation->startTime() ? 1 : compositor_group,
                              composited_element_ids, start_on_compositor)) {
       if (animation->HasActiveAnimationsOnCompositor() &&
           !had_compositor_animation) {
         started_synchronized_on_compositor = true;
       }
 
-      if (animation->Playing() && !animation->HasStartTime() &&
+      if (animation->Playing() && !animation->startTime() &&
           animation->TimelineInternal() &&
           animation->TimelineInternal()->IsActive()) {
         waiting_for_start_time.push_back(animation.Get());
@@ -98,13 +98,13 @@
   // start time. Otherwise they may start immediately.
   if (started_synchronized_on_compositor) {
     for (auto& animation : waiting_for_start_time) {
-      if (!animation->HasStartTime()) {
+      if (!animation->startTime()) {
         waiting_for_compositor_animation_start_.push_back(animation);
       }
     }
   } else {
     for (auto& animation : waiting_for_start_time) {
-      if (!animation->HasStartTime()) {
+      if (!animation->startTime()) {
         animation->NotifyCompositorStartTime(
             animation->TimelineInternal()->CurrentTimeInternal());
       }
@@ -148,7 +148,7 @@
   animations.swap(waiting_for_compositor_animation_start_);
 
   for (auto animation : animations) {
-    if (animation->HasStartTime() ||
+    if (animation->startTime() ||
         animation->PlayStateInternal() != Animation::kPending ||
         !animation->TimelineInternal() ||
         !animation->TimelineInternal()->IsActive()) {
diff --git a/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp b/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
index 5cf55016..37619f3 100644
--- a/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
+++ b/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
@@ -532,8 +532,9 @@
             pending_update_.NewTransitions().end() &&
         !animation->Limited()) {
       retargeted_compositor_transitions.insert(
-          property, std::pair<KeyframeEffectReadOnly*, double>(
-                        effect, animation->StartTimeInternal()));
+          property,
+          std::pair<KeyframeEffectReadOnly*, double>(
+              effect, animation->StartTimeInternal().value_or(NullValue())));
     }
     animation->cancel();
     // after cancelation, transitions must be downgraded or they'll fail
diff --git a/third_party/WebKit/Source/core/css/cssom/StyleValueFactory.cpp b/third_party/WebKit/Source/core/css/cssom/StyleValueFactory.cpp
index 8bf00b9..135b24f 100644
--- a/third_party/WebKit/Source/core/css/cssom/StyleValueFactory.cpp
+++ b/third_party/WebKit/Source/core/css/cssom/StyleValueFactory.cpp
@@ -228,7 +228,7 @@
     return result;
   }
 
-  if (property_id == CSSPropertyVariable ||
+  if ((property_id == CSSPropertyVariable && !tokens.IsEmpty()) ||
       CSSVariableParser::ContainsValidVariableReferences(range)) {
     const auto variable_data =
         CSSVariableData::Create(range, false /* is_animation_tainted */,
diff --git a/third_party/WebKit/Source/core/events/event_type_names.json5 b/third_party/WebKit/Source/core/events/event_type_names.json5
index 74eb74c..e3fcfffe 100644
--- a/third_party/WebKit/Source/core/events/event_type_names.json5
+++ b/third_party/WebKit/Source/core/events/event_type_names.json5
@@ -140,6 +140,7 @@
     "icegatheringstatechange",
     "inactive",
     "input",
+    "inputsourceschange",
     "install",
     "interfacerequest",
     "invalid",
@@ -227,6 +228,7 @@
     "select",
     "selectionchange",
     "selectstart",
+    "selectend",
     "shippingaddresschange",
     "shippingoptionchange",
     "show",
diff --git a/third_party/WebKit/Source/core/html/parser/XSSAuditor.cpp b/third_party/WebKit/Source/core/html/parser/XSSAuditor.cpp
index 95540555..958c498c 100644
--- a/third_party/WebKit/Source/core/html/parser/XSSAuditor.cpp
+++ b/third_party/WebKit/Source/core/html/parser/XSSAuditor.cpp
@@ -425,12 +425,9 @@
          xss_protection_header == kBlockReflectedXSS) &&
         !report_url.IsEmpty()) {
       xss_protection_report_url = document->CompleteURL(report_url);
-      // Note: Because violation reports may now only be sent same-origin,
-      // we no longer need to check for a mixed-content report URL.
-      if (!SecurityOrigin::Create(xss_protection_report_url)
-               ->IsSameSchemeHostPort(document->GetSecurityOrigin())) {
-        error_details =
-            "reporting URL is not same scheme, host, and port as page";
+      if (MixedContentChecker::IsMixedContent(document->GetSecurityOrigin(),
+                                              xss_protection_report_url)) {
+        error_details = "insecure reporting URL for secure page";
         xss_protection_header = kReflectedXSSInvalid;
         xss_protection_report_url = KURL();
       }
diff --git a/third_party/WebKit/Source/core/input/EventHandler.cpp b/third_party/WebKit/Source/core/input/EventHandler.cpp
index e5f24cd..8b2f4feb 100644
--- a/third_party/WebKit/Source/core/input/EventHandler.cpp
+++ b/third_party/WebKit/Source/core/input/EventHandler.cpp
@@ -661,7 +661,10 @@
       WebInputEvent::kPointerDown, mev.InnerNode(), mev.CanvasRegionId(),
       mev.Event(), Vector<WebMouseEvent>());
 
-  if (event_result == WebInputEventResult::kNotHandled && frame_->View()) {
+  // Disabled form controls still need to resize the scrollable area.
+  if ((event_result == WebInputEventResult::kNotHandled ||
+       event_result == WebInputEventResult::kHandledSuppressed) &&
+      frame_->View()) {
     LocalFrameView* view = frame_->View();
     PaintLayer* layer =
         mev.InnerNode()->GetLayoutObject()
diff --git a/third_party/WebKit/Source/core/inspector/InspectorAnimationAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorAnimationAgent.cpp
index ce248a6..354b657 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorAnimationAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorAnimationAgent.cpp
@@ -254,8 +254,8 @@
     *current_time = animation->currentTime();
   } else {
     // Use startTime where possible since currentTime is limited.
-    *current_time =
-        animation->TimelineInternal()->currentTime() - animation->startTime();
+    *current_time = animation->TimelineInternal()->currentTime() -
+                    animation->startTime().value_or(NullValue());
   }
   return Response::OK();
 }
@@ -274,8 +274,8 @@
       return Response::Error("Failed to clone detached animation");
     if (paused && !clone->Paused()) {
       // Ensure we restore a current time if the animation is limited.
-      double current_time =
-          clone->TimelineInternal()->currentTime() - clone->startTime();
+      double current_time = clone->TimelineInternal()->currentTime() -
+                            clone->startTime().value_or(NullValue());
       clone->pause();
       clone->setCurrentTime(current_time, false);
     } else if (!paused && clone->Paused()) {
@@ -323,7 +323,7 @@
     id_to_animation_clone_.Set(id, clone);
     id_to_animation_.Set(String::Number(clone->SequenceNumber()), clone);
     clone->play();
-    clone->setStartTime(animation->startTime(), false);
+    clone->setStartTime(animation->startTime().value_or(NullValue()), false);
 
     animation->SetEffectSuppressed(true);
   }
@@ -551,12 +551,14 @@
 double InspectorAnimationAgent::NormalizedStartTime(
     blink::Animation& animation) {
   if (ReferenceTimeline().PlaybackRate() == 0) {
-    return animation.startTime() + ReferenceTimeline().currentTime() -
+    return animation.startTime().value_or(NullValue()) +
+           ReferenceTimeline().currentTime() -
            animation.TimelineInternal()->currentTime();
   }
-  return animation.startTime() + (animation.TimelineInternal()->ZeroTime() -
-                                  ReferenceTimeline().ZeroTime()) *
-                                     1000 * ReferenceTimeline().PlaybackRate();
+  return animation.startTime().value_or(NullValue()) +
+         (animation.TimelineInternal()->ZeroTime() -
+          ReferenceTimeline().ZeroTime()) *
+             1000 * ReferenceTimeline().PlaybackRate();
 }
 
 void InspectorAnimationAgent::Trace(blink::Visitor* visitor) {
diff --git a/third_party/WebKit/Source/core/layout/LayoutImage.cpp b/third_party/WebKit/Source/core/layout/LayoutImage.cpp
index 68d5e26..94a1d08 100644
--- a/third_party/WebKit/Source/core/layout/LayoutImage.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutImage.cpp
@@ -250,8 +250,7 @@
   // Check for image with alpha.
   TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage",
                "data", InspectorPaintImageEvent::Data(this, *image_content));
-  return image_content->GetImage()->CurrentFrameKnownToBeOpaque(
-      Image::kPreCacheMetadata);
+  return image_content->GetImage()->CurrentFrameKnownToBeOpaque();
 }
 
 bool LayoutImage::ComputeBackgroundIsKnownToBeObscured() const {
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.cpp
index 3cbbde0..8e8c224e 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.cpp
@@ -428,15 +428,15 @@
   return *rare_data_.get();
 }
 
-LayoutUnit LayoutSVGShape::VisualRectOutsetForRasterEffects() const {
+float LayoutSVGShape::VisualRectOutsetForRasterEffects() const {
   // Account for raster expansions due to SVG stroke hairline raster effects.
   if (StyleRef().SvgStyle().HasVisibleStroke()) {
-    LayoutUnit outset(0.5f);
+    float outset = 0.5f;
     if (StyleRef().SvgStyle().CapStyle() != kButtCap)
       outset += 0.5f;
     return outset;
   }
-  return LayoutUnit();
+  return 0;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.h b/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.h
index 1e6dd86..9d5be9c 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.h
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.h
@@ -107,7 +107,7 @@
   const char* GetName() const override { return "LayoutSVGShape"; }
 
  protected:
-  LayoutUnit VisualRectOutsetForRasterEffects() const override;
+  float VisualRectOutsetForRasterEffects() const override;
 
   void ClearPath() { path_.reset(); }
   void CreatePath();
diff --git a/third_party/WebKit/Source/core/paint/CSSMaskPainter.cpp b/third_party/WebKit/Source/core/paint/CSSMaskPainter.cpp
index c250c90..c93ca23 100644
--- a/third_party/WebKit/Source/core/paint/CSSMaskPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/CSSMaskPainter.cpp
@@ -22,11 +22,13 @@
     SVGResources* resources =
         SVGResourcesCache::CachedResourcesForLayoutObject(object);
     LayoutSVGResourceMasker* masker = resources ? resources->Masker() : nullptr;
-    if (!masker)
-      return WTF::nullopt;
-    return EnclosingIntRect(masker->ResourceBoundingBox(&object));
+    if (masker)
+      return EnclosingIntRect(masker->ResourceBoundingBox(&object));
   }
 
+  if (object.IsSVGChild())
+    return WTF::nullopt;
+
   const ComputedStyle& style = object.StyleRef();
   if (!style.HasMask())
     return WTF::nullopt;
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
index a2b2054..cbd6651 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
@@ -5229,4 +5229,18 @@
   EXPECT_TRUE(root.FirstFragment().PaintProperties()->Mask());
 }
 
+TEST_P(PaintPropertyTreeBuilderTest, SVGRootWithCSSMask) {
+  // SPv1 has no effect tree.
+  if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+    return;
+  SetBodyInnerHTML(R"HTML(
+    <svg id="svg" width="16" height="16" style="-webkit-mask-image: url(fake);">
+    </svg>
+  )HTML");
+
+  const LayoutSVGRoot& root =
+      *ToLayoutSVGRoot(GetLayoutObjectByElementId("svg"));
+  EXPECT_TRUE(root.FirstFragment().PaintProperties()->Mask());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.cpp b/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.cpp
index 694eae4..bce1cac 100644
--- a/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.cpp
+++ b/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.cpp
@@ -91,10 +91,12 @@
       update_type != kForceUpdate)
     return;
 
+  LayoutBoxModelObject& layout_object = layer->GetLayoutObject();
+
   const PaintLayer* previous_overflow_layer = layer->AncestorOverflowLayer();
   layer->UpdateAncestorOverflowLayer(info.last_overflow_clip_layer);
   if (info.last_overflow_clip_layer && layer->NeedsCompositingInputsUpdate() &&
-      layer->GetLayoutObject().Style()->HasStickyConstrainedPosition()) {
+      layout_object.Style()->HasStickyConstrainedPosition()) {
     if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
       if (info.last_overflow_clip_layer != previous_overflow_layer) {
         // Old ancestor scroller should no longer have these constraints.
@@ -107,21 +109,17 @@
         // If our ancestor scroller has changed and the previous one was the
         // root layer, we are no longer viewport constrained.
         if (previous_overflow_layer && previous_overflow_layer->IsRootLayer()) {
-          layer->GetLayoutObject()
-              .View()
-              ->GetFrameView()
-              ->RemoveViewportConstrainedObject(layer->GetLayoutObject());
+          layout_object.View()->GetFrameView()->RemoveViewportConstrainedObject(
+              layout_object);
         }
       }
 
       if (info.last_overflow_clip_layer->IsRootLayer()) {
-        layer->GetLayoutObject()
-            .View()
-            ->GetFrameView()
-            ->AddViewportConstrainedObject(layer->GetLayoutObject());
+        layout_object.View()->GetFrameView()->AddViewportConstrainedObject(
+            layout_object);
       }
     }
-    layer->GetLayoutObject().UpdateStickyPositionConstraints();
+    layout_object.UpdateStickyPositionConstraints();
 
     // Sticky position constraints and ancestor overflow scroller affect
     // the sticky layer position, so we need to update it again here.
@@ -133,140 +131,32 @@
 
   geometry_map_.PushMappingsToAncestor(layer, layer->Parent());
 
-  if (layer->HasCompositedLayerMapping())
-    info.enclosing_composited_layer = layer;
-
+  PaintLayer* enclosing_composited_layer =
+      layer->HasCompositedLayerMapping() ? layer
+                                         : info.enclosing_composited_layer;
   if (layer->NeedsCompositingInputsUpdate()) {
-    if (info.enclosing_composited_layer) {
-      info.enclosing_composited_layer->GetCompositedLayerMapping()
+    if (enclosing_composited_layer) {
+      enclosing_composited_layer->GetCompositedLayerMapping()
           ->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree);
     }
     update_type = kForceUpdate;
   }
 
-  if (update_type == kForceUpdate) {
-    PaintLayer::AncestorDependentCompositingInputs properties;
+  if (update_type == kForceUpdate)
+    UpdateAncestorDependentCompositingInputs(layer, info);
 
-    if (!layer->IsRootLayer()) {
-      if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
-        properties.unclipped_absolute_bounding_box =
-            EnclosingIntRect(geometry_map_.AbsoluteRect(
-                FloatRect(layer->BoundingBoxForCompositingOverlapTest())));
-
-        ClipRect clip_rect;
-        layer->Clipper(PaintLayer::kDoNotUseGeometryMapper)
-            .CalculateBackgroundClipRect(
-                ClipRectsContext(root_layer_,
-                                 kAbsoluteClipRectsIgnoringViewportClip,
-                                 kIgnorePlatformOverlayScrollbarSize,
-                                 kIgnoreOverflowClipAndScroll),
-                clip_rect);
-        // Scroll offset is not included in the clip rect returned above
-        // (see kIgnoreOverflowClipAndScroll), so we need to add it in
-        // now. Scroll offset is excluded so that we do not need to invalidate
-        // the clip rect cache on scroll.
-        if (root_layer_->ScrollsOverflow()) {
-          clip_rect.Move(
-              LayoutSize(-root_layer_->GetScrollableArea()->GetScrollOffset()));
-        }
-        IntRect snapped_clip_rect = PixelSnappedIntRect(clip_rect.Rect());
-        properties.clipped_absolute_bounding_box =
-            properties.unclipped_absolute_bounding_box;
-        properties.clipped_absolute_bounding_box.Intersect(snapped_clip_rect);
-      }
-
-      const PaintLayer* parent = layer->Parent();
-      properties.opacity_ancestor =
-          parent->IsTransparent() ? parent : parent->OpacityAncestor();
-      properties.transform_ancestor =
-          parent->Transform() ? parent : parent->TransformAncestor();
-      properties.filter_ancestor = parent->HasFilterInducingProperty()
-                                       ? parent
-                                       : parent->FilterAncestor();
-      properties.clip_path_ancestor = parent->GetLayoutObject().HasClipPath()
-                                          ? parent
-                                          : parent->ClipPathAncestor();
-      properties.mask_ancestor =
-          parent->GetLayoutObject().HasMask() ? parent : parent->MaskAncestor();
-
-      bool layer_is_fixed_position =
-          layer->GetLayoutObject().Style()->GetPosition() == EPosition::kFixed;
-
-      properties.nearest_fixed_position_layer =
-          layer_is_fixed_position ? layer : parent->NearestFixedPositionLayer();
-
-      if (info.has_ancestor_with_clip_related_property) {
-        // This is the ancestor that is |layer|'s containing block, or has a
-        // CSS clip, which ever is the closest.
-        const PaintLayer* parent_layer_on_clipping_container_chain =
-            FindParentLayerOnClippingContainerChain(layer);
-        const bool parent_has_clip_related_property =
-            parent_layer_on_clipping_container_chain->GetLayoutObject()
-                .HasClipRelatedProperty();
-        properties.clipping_container =
-            parent_has_clip_related_property
-                ? &parent_layer_on_clipping_container_chain->GetLayoutObject()
-                : parent_layer_on_clipping_container_chain->ClippingContainer();
-
-        if (!layer->SubtreeIsInvisible()) {
-          if (layer->GetLayoutObject().IsOutOfFlowPositioned() &&
-              NeedsToEscapeClipInheritedFromCompositingContainer(
-                  layer, parent_layer_on_clipping_container_chain
-                             ->GetLayoutObject())) {
-            properties.clip_parent = parent_layer_on_clipping_container_chain;
-          } else if (parent_layer_on_clipping_container_chain
-                         ->CompositingContainer() ==
-                     layer->CompositingContainer()) {
-            // If the clipping container of |layer| is a sibling in the
-            // stacking tree, and it escapes a stacking ancestor clip,
-            // this layer should escape that clip also.
-            if (parent_layer_on_clipping_container_chain->ClipParent()) {
-              // It may be attempting to inherit the clip state of
-              // parent_layer_on_clipping_container_chain directly, but our
-              // paint order can be before the clipping parent due to negative
-              // z-index. Our compositor implementation currently only allow
-              // inheriting clip from layer that paints before us.
-              properties.clip_parent =
-                  parent_layer_on_clipping_container_chain->ClipParent();
-            }
-          }
-        }
-      }
-
-      if (info.last_scrolling_ancestor) {
-        const LayoutObject* containing_block =
-            layer->GetLayoutObject().ContainingBlock();
-        const PaintLayer* parent_layer_on_containing_block_chain =
-            FindParentLayerOnContainingBlockChain(containing_block);
-
-        properties.ancestor_scrolling_layer =
-            parent_layer_on_containing_block_chain->AncestorScrollingLayer();
-        if (parent_layer_on_containing_block_chain->ScrollsOverflow()) {
-          properties.ancestor_scrolling_layer =
-              parent_layer_on_containing_block_chain;
-        }
-
-        if (layer->StackingNode()->IsStacked() &&
-            properties.ancestor_scrolling_layer &&
-            !info.ancestor_stacking_context->GetLayoutObject().IsDescendantOf(
-                &properties.ancestor_scrolling_layer->GetLayoutObject()))
-          properties.scroll_parent = properties.ancestor_scrolling_layer;
-      }
-    }
-
-    layer->UpdateAncestorDependentCompositingInputs(properties);
-  }
+  info.enclosing_composited_layer = enclosing_composited_layer;
 
   if (layer->StackingNode()->IsStackingContext())
     info.ancestor_stacking_context = layer;
 
-  if (layer->IsRootLayer() || layer->GetLayoutObject().HasOverflowClip())
+  if (layer->IsRootLayer() || layout_object.HasOverflowClip())
     info.last_overflow_clip_layer = layer;
 
   if (layer->ScrollsOverflow())
     info.last_scrolling_ancestor = layer;
 
-  if (layer->GetLayoutObject().HasClipRelatedProperty())
+  if (layout_object.HasClipRelatedProperty())
     info.has_ancestor_with_clip_related_property = true;
 
   for (PaintLayer* child = layer->FirstChild(); child;
@@ -282,13 +172,131 @@
     // If the floating object becomes non-self-painting, so some ancestor should
     // paint it; if it becomes self-painting, it should paint itself and no
     // ancestor should paint it.
-    if (layer->GetLayoutObject().IsFloating()) {
+    if (layout_object.IsFloating()) {
       LayoutBlockFlow::UpdateAncestorShouldPaintFloatingObject(
           *layer->GetLayoutBox());
     }
   }
 }
 
+void CompositingInputsUpdater::UpdateAncestorDependentCompositingInputs(
+    PaintLayer* layer,
+    const AncestorInfo& info) {
+  if (layer->IsRootLayer()) {
+    layer->UpdateAncestorDependentCompositingInputs(
+        PaintLayer::AncestorDependentCompositingInputs());
+    return;
+  }
+
+  PaintLayer::AncestorDependentCompositingInputs properties;
+  LayoutBoxModelObject& layout_object = layer->GetLayoutObject();
+
+  if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
+    properties.unclipped_absolute_bounding_box =
+        EnclosingIntRect(geometry_map_.AbsoluteRect(
+            FloatRect(layer->BoundingBoxForCompositingOverlapTest())));
+
+    ClipRect clip_rect;
+    layer->Clipper(PaintLayer::kDoNotUseGeometryMapper)
+        .CalculateBackgroundClipRect(
+            ClipRectsContext(root_layer_,
+                             kAbsoluteClipRectsIgnoringViewportClip,
+                             kIgnorePlatformOverlayScrollbarSize,
+                             kIgnoreOverflowClipAndScroll),
+            clip_rect);
+    // Scroll offset is not included in the clip rect returned above
+    // (see kIgnoreOverflowClipAndScroll), so we need to add it in
+    // now. Scroll offset is excluded so that we do not need to invalidate
+    // the clip rect cache on scroll.
+    if (root_layer_->ScrollsOverflow()) {
+      clip_rect.Move(
+          LayoutSize(-root_layer_->GetScrollableArea()->GetScrollOffset()));
+    }
+    IntRect snapped_clip_rect = PixelSnappedIntRect(clip_rect.Rect());
+    properties.clipped_absolute_bounding_box =
+        properties.unclipped_absolute_bounding_box;
+    properties.clipped_absolute_bounding_box.Intersect(snapped_clip_rect);
+  }
+
+  const PaintLayer* parent = layer->Parent();
+  properties.opacity_ancestor =
+      parent->IsTransparent() ? parent : parent->OpacityAncestor();
+  properties.transform_ancestor =
+      parent->Transform() ? parent : parent->TransformAncestor();
+  properties.filter_ancestor =
+      parent->HasFilterInducingProperty() ? parent : parent->FilterAncestor();
+  properties.clip_path_ancestor = parent->GetLayoutObject().HasClipPath()
+                                      ? parent
+                                      : parent->ClipPathAncestor();
+  properties.mask_ancestor =
+      parent->GetLayoutObject().HasMask() ? parent : parent->MaskAncestor();
+
+  bool layer_is_fixed_position =
+      layout_object.Style()->GetPosition() == EPosition::kFixed;
+
+  properties.nearest_fixed_position_layer =
+      layer_is_fixed_position ? layer : parent->NearestFixedPositionLayer();
+
+  if (info.has_ancestor_with_clip_related_property) {
+    // This is the ancestor that is |layer|'s containing block, or has a
+    // CSS clip, which ever is the closest.
+    const PaintLayer* parent_layer_on_clipping_container_chain =
+        FindParentLayerOnClippingContainerChain(layer);
+    const bool parent_has_clip_related_property =
+        parent_layer_on_clipping_container_chain->GetLayoutObject()
+            .HasClipRelatedProperty();
+    properties.clipping_container =
+        parent_has_clip_related_property
+            ? &parent_layer_on_clipping_container_chain->GetLayoutObject()
+            : parent_layer_on_clipping_container_chain->ClippingContainer();
+
+    if (!layer->SubtreeIsInvisible()) {
+      if (layout_object.IsOutOfFlowPositioned() &&
+          NeedsToEscapeClipInheritedFromCompositingContainer(
+              layer,
+              parent_layer_on_clipping_container_chain->GetLayoutObject())) {
+        properties.clip_parent = parent_layer_on_clipping_container_chain;
+      } else if (parent_layer_on_clipping_container_chain
+                     ->CompositingContainer() ==
+                 layer->CompositingContainer()) {
+        // If the clipping container of |layer| is a sibling in the
+        // stacking tree, and it escapes a stacking ancestor clip,
+        // this layer should escape that clip also.
+        if (parent_layer_on_clipping_container_chain->ClipParent()) {
+          // It may be attempting to inherit the clip state of
+          // parent_layer_on_clipping_container_chain directly, but our
+          // paint order can be before the clipping parent due to negative
+          // z-index. Our compositor implementation currently only allow
+          // inheriting clip from layer that paints before us.
+          properties.clip_parent =
+              parent_layer_on_clipping_container_chain->ClipParent();
+        }
+      }
+    }
+  }
+
+  if (info.last_scrolling_ancestor) {
+    const LayoutObject* containing_block = layout_object.ContainingBlock();
+    const PaintLayer* parent_layer_on_containing_block_chain =
+        FindParentLayerOnContainingBlockChain(containing_block);
+
+    properties.ancestor_scrolling_layer =
+        parent_layer_on_containing_block_chain->AncestorScrollingLayer();
+    if (parent_layer_on_containing_block_chain->ScrollsOverflow()) {
+      properties.ancestor_scrolling_layer =
+          parent_layer_on_containing_block_chain;
+    }
+
+    if (layer->StackingNode()->IsStacked() &&
+        properties.ancestor_scrolling_layer &&
+        !info.ancestor_stacking_context->GetLayoutObject().IsDescendantOf(
+            &properties.ancestor_scrolling_layer->GetLayoutObject()))
+      properties.scroll_parent = properties.ancestor_scrolling_layer;
+  }
+
+  layer->UpdateAncestorDependentCompositingInputs(properties);
+}
+
 #if DCHECK_IS_ON()
 
 void CompositingInputsUpdater::AssertNeedsCompositingInputsUpdateBitsCleared(
diff --git a/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.h b/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.h
index 34674a63..982c6a4 100644
--- a/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.h
+++ b/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.h
@@ -49,10 +49,11 @@
     // containing block chain.
     PaintLayer* last_scrolling_ancestor;
     bool has_ancestor_with_clip_related_property;
-    bool has_ancestor_with_clip_path;
   };
 
   void UpdateRecursive(PaintLayer*, UpdateType, AncestorInfo);
+  void UpdateAncestorDependentCompositingInputs(PaintLayer*,
+                                                const AncestorInfo&);
 
   LayoutGeometryMap geometry_map_;
   PaintLayer* root_layer_;
diff --git a/third_party/WebKit/Source/core/style/StyleFetchedImage.cpp b/third_party/WebKit/Source/core/style/StyleFetchedImage.cpp
index 6117553..fbf455d 100644
--- a/third_party/WebKit/Source/core/style/StyleFetchedImage.cpp
+++ b/third_party/WebKit/Source/core/style/StyleFetchedImage.cpp
@@ -140,8 +140,7 @@
 
 bool StyleFetchedImage::KnownToBeOpaque(const Document&,
                                         const ComputedStyle&) const {
-  return image_->GetImage()->CurrentFrameKnownToBeOpaque(
-      Image::kPreCacheMetadata);
+  return image_->GetImage()->CurrentFrameKnownToBeOpaque();
 }
 
 void StyleFetchedImage::Trace(blink::Visitor* visitor) {
diff --git a/third_party/WebKit/Source/core/style/StyleFetchedImageSet.cpp b/third_party/WebKit/Source/core/style/StyleFetchedImageSet.cpp
index 5650005..9f903ce 100644
--- a/third_party/WebKit/Source/core/style/StyleFetchedImageSet.cpp
+++ b/third_party/WebKit/Source/core/style/StyleFetchedImageSet.cpp
@@ -131,8 +131,7 @@
 
 bool StyleFetchedImageSet::KnownToBeOpaque(const Document&,
                                            const ComputedStyle&) const {
-  return best_fit_image_->GetImage()->CurrentFrameKnownToBeOpaque(
-      Image::kPreCacheMetadata);
+  return best_fit_image_->GetImage()->CurrentFrameKnownToBeOpaque();
 }
 
 void StyleFetchedImageSet::Trace(blink::Visitor* visitor) {
diff --git a/third_party/WebKit/Source/core/svg/SVGResource.cpp b/third_party/WebKit/Source/core/svg/SVGResource.cpp
index 02165ecc..9ce7a72 100644
--- a/third_party/WebKit/Source/core/svg/SVGResource.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGResource.cpp
@@ -5,26 +5,29 @@
 #include "core/svg/SVGResource.h"
 
 #include "core/dom/Element.h"
+#include "core/dom/IdTargetObserver.h"
 #include "core/dom/TreeScope.h"
 #include "core/layout/svg/LayoutSVGResourceContainer.h"
 #include "core/layout/svg/SVGResources.h"
 #include "core/layout/svg/SVGResourcesCache.h"
 #include "core/svg/SVGElement.h"
+#include "core/svg/SVGURIReference.h"
 
 namespace blink {
 
 SVGResource::SVGResource(TreeScope& tree_scope, const AtomicString& id)
-    : IdTargetObserver(tree_scope.GetIdTargetObserverRegistry(), id),
-      tree_scope_(tree_scope),
-      target_(tree_scope.getElementById(id)) {}
-
-SVGResource::~SVGResource() = default;
+    : tree_scope_(tree_scope) {
+  target_ = SVGURIReference::ObserveTarget(
+      id_observer_, tree_scope, id,
+      WTF::BindRepeating(&SVGResource::TargetChanged, WrapWeakPersistent(this),
+                         id));
+}
 
 void SVGResource::Trace(Visitor* visitor) {
   visitor->Trace(tree_scope_);
   visitor->Trace(target_);
+  visitor->Trace(id_observer_);
   visitor->Trace(pending_clients_);
-  IdTargetObserver::Trace(visitor);
 }
 
 void SVGResource::AddWatch(SVGElement& element) {
@@ -41,6 +44,10 @@
   return (!container || !container->HasClients()) && pending_clients_.IsEmpty();
 }
 
+void SVGResource::Unregister() {
+  SVGURIReference::UnobserveTarget(id_observer_);
+}
+
 void SVGResource::NotifyResourceClients() {
   HeapHashSet<Member<SVGElement>> pending_clients;
   pending_clients.swap(pending_clients_);
@@ -60,8 +67,8 @@
   return ToLayoutSVGResourceContainer(layout_object);
 }
 
-void SVGResource::IdTargetChanged() {
-  Element* new_target = tree_scope_->getElementById(Id());
+void SVGResource::TargetChanged(const AtomicString& id) {
+  Element* new_target = tree_scope_->getElementById(id);
   if (new_target == target_)
     return;
   // Detach clients from the old resource, moving them to the pending list
diff --git a/third_party/WebKit/Source/core/svg/SVGResource.h b/third_party/WebKit/Source/core/svg/SVGResource.h
index b12e9ca..75e368e 100644
--- a/third_party/WebKit/Source/core/svg/SVGResource.h
+++ b/third_party/WebKit/Source/core/svg/SVGResource.h
@@ -6,23 +6,22 @@
 #define SVGResource_h
 
 #include "base/macros.h"
-#include "core/dom/IdTargetObserver.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/HashSet.h"
 
 namespace blink {
 
 class Element;
+class IdTargetObserver;
 class LayoutSVGResourceContainer;
 class SVGElement;
 class TreeScope;
 
 // A class tracking a reference to an SVG resource (an element that constitutes
 // a paint server, mask, clip-path, filter et.c.)
-class SVGResource : public IdTargetObserver {
+class SVGResource : public GarbageCollected<SVGResource> {
  public:
   SVGResource(TreeScope&, const AtomicString& id);
-  ~SVGResource() override;
 
   Element* Target() const { return target_; }
   LayoutSVGResourceContainer* ResourceContainer() const;
@@ -30,6 +29,8 @@
   void AddWatch(SVGElement&);
   void RemoveWatch(SVGElement&);
 
+  void Unregister();
+
   bool IsEmpty() const;
 
   void Trace(Visitor*);
@@ -37,10 +38,11 @@
   void NotifyResourceClients();
 
  private:
-  void IdTargetChanged() override;
+  void TargetChanged(const AtomicString& id);
 
   Member<TreeScope> tree_scope_;
   Member<Element> target_;
+  Member<IdTargetObserver> id_observer_;
   HeapHashSet<Member<SVGElement>> pending_clients_;
 
   DISALLOW_COPY_AND_ASSIGN(SVGResource);
diff --git a/third_party/WebKit/Source/core/svg/graphics/SVGImage.h b/third_party/WebKit/Source/core/svg/graphics/SVGImage.h
index 8185c49a..4c1635d 100644
--- a/third_party/WebKit/Source/core/svg/graphics/SVGImage.h
+++ b/third_party/WebKit/Source/core/svg/graphics/SVGImage.h
@@ -139,10 +139,7 @@
   void DestroyDecodedData() override {}
 
   // FIXME: Implement this to be less conservative.
-  bool CurrentFrameKnownToBeOpaque(
-      MetadataMode = kUseCurrentMetadata) override {
-    return false;
-  }
+  bool CurrentFrameKnownToBeOpaque() override { return false; }
 
   void Draw(PaintCanvas*,
             const PaintFlags&,
diff --git a/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.h b/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.h
index 2e56394..368c742 100644
--- a/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.h
+++ b/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.h
@@ -87,10 +87,7 @@
             ImageDecodingMode) override;
 
   // FIXME: Implement this to be less conservative.
-  bool CurrentFrameKnownToBeOpaque(
-      MetadataMode = kUseCurrentMetadata) override {
-    return false;
-  }
+  bool CurrentFrameKnownToBeOpaque() override { return false; }
 
   PaintImage PaintImageForCurrentFrame() override;
 
diff --git a/third_party/WebKit/Source/core/testing/Internals.cpp b/third_party/WebKit/Source/core/testing/Internals.cpp
index 0100a7ea..fd446a5 100644
--- a/third_party/WebKit/Source/core/testing/Internals.cpp
+++ b/third_party/WebKit/Source/core/testing/Internals.cpp
@@ -562,45 +562,6 @@
   RuntimeEnabledFeatures::SetCSSAdditiveAnimationsEnabled(false);
 }
 
-void Internals::advanceTimeForImage(Element* image,
-                                    double delta_time_in_seconds,
-                                    ExceptionState& exception_state) {
-  DCHECK(image);
-  if (delta_time_in_seconds < 0) {
-    exception_state.ThrowDOMException(
-        kInvalidAccessError,
-        ExceptionMessages::IndexExceedsMinimumBound(
-            "deltaTimeInSeconds", delta_time_in_seconds, 0.0));
-    return;
-  }
-
-  ImageResourceContent* resource = nullptr;
-  if (auto* html_image = ToHTMLImageElementOrNull(*image)) {
-    resource = html_image->CachedImage();
-  } else if (auto* svg_image = ToSVGImageElementOrNull(*image)) {
-    resource = svg_image->CachedImage();
-  } else {
-    exception_state.ThrowDOMException(
-        kInvalidAccessError, "The element provided is not a image element.");
-    return;
-  }
-
-  if (!resource || !resource->HasImage()) {
-    exception_state.ThrowDOMException(kInvalidAccessError,
-                                      "The image resource is not available.");
-    return;
-  }
-
-  Image* image_data = resource->GetImage();
-  if (!image_data->IsBitmapImage()) {
-    exception_state.ThrowDOMException(
-        kInvalidAccessError, "The image resource is not a BitmapImage type.");
-    return;
-  }
-
-  image_data->AdvanceTime(TimeDelta::FromSecondsD(delta_time_in_seconds));
-}
-
 void Internals::advanceImageAnimation(Element* image,
                                       ExceptionState& exception_state) {
   DCHECK(image);
diff --git a/third_party/WebKit/Source/core/testing/Internals.h b/third_party/WebKit/Source/core/testing/Internals.h
index f297a8c..9abd4c62 100644
--- a/third_party/WebKit/Source/core/testing/Internals.h
+++ b/third_party/WebKit/Source/core/testing/Internals.h
@@ -128,12 +128,6 @@
   void disableCompositedAnimation(Animation*);
   void disableCSSAdditiveAnimations();
 
-  // Modifies m_desiredFrameStartTime in BitmapImage to advance the next frame
-  // time for testing whether animated images work properly.
-  void advanceTimeForImage(Element* image,
-                           double delta_time_in_seconds,
-                           ExceptionState&);
-
   // Advances an animated image. For BitmapImage (e.g., animated gifs) this
   // will advance to the next frame. For SVGImage, this will trigger an
   // animation update for CSS and advance the SMIL timeline by one frame.
diff --git a/third_party/WebKit/Source/core/testing/Internals.idl b/third_party/WebKit/Source/core/testing/Internals.idl
index 4c87eed..84b1b75e 100644
--- a/third_party/WebKit/Source/core/testing/Internals.idl
+++ b/third_party/WebKit/Source/core/testing/Internals.idl
@@ -80,10 +80,6 @@
     void disableCompositedAnimation(Animation animation);
     void disableCSSAdditiveAnimations();
 
-    // Modifies m_desiredFrameStartTime in BitmapImage to advance the next frame time
-    // for testing whether animated images work properly.
-    [RaisesException] void advanceTimeForImage(Element image, double deltaTimeInSeconds);
-
     // Advances an animated image. For BitmapImage (e.g., animated gifs) this
     // will advance to the next frame. For SVGImage, this will trigger an
     // animation update for CSS and advance the SMIL timeline by one frame.
diff --git a/third_party/WebKit/Source/devtools/front_end/browser_sdk/HAREntry.js b/third_party/WebKit/Source/devtools/front_end/browser_sdk/HAREntry.js
index 4f67c2e..cf90721 100644
--- a/third_party/WebKit/Source/devtools/front_end/browser_sdk/HAREntry.js
+++ b/third_party/WebKit/Source/devtools/front_end/browser_sdk/HAREntry.js
@@ -70,7 +70,7 @@
       time += Math.max(t, 0);
 
     const entry = {
-      startedDateTime: BrowserSDK.HARLog.pseudoWallTime(harEntry._request, harEntry._request.issueTime()),
+      startedDateTime: BrowserSDK.HARLog.pseudoWallTime(harEntry._request, harEntry._request.issueTime()).toJSON(),
       time: time,
       request: await harEntry._buildRequest(),
       response: harEntry._buildResponse(),
@@ -333,10 +333,10 @@
   /**
    * @param {!SDK.NetworkRequest} request
    * @param {number} monotonicTime
-   * @return {string}
+   * @return {!Date}
    */
   static pseudoWallTime(request, monotonicTime) {
-    return new Date(request.pseudoWallTime(monotonicTime) * 1000).toJSON();
+    return new Date(request.pseudoWallTime(monotonicTime) * 1000);
   }
 
   /**
@@ -383,7 +383,7 @@
    */
   _convertPage(page, request) {
     return {
-      startedDateTime: BrowserSDK.HARLog.pseudoWallTime(request, page.startTime),
+      startedDateTime: BrowserSDK.HARLog.pseudoWallTime(request, page.startTime).toJSON(),
       id: 'page_' + page.id,
       title: page.url,  // We don't have actual page title here. URL is probably better than nothing.
       pageTimings: {
diff --git a/third_party/WebKit/Source/modules/credentialmanager/AuthenticationExtensionsClientOutputs.idl b/third_party/WebKit/Source/modules/credentialmanager/AuthenticationExtensionsClientOutputs.idl
new file mode 100644
index 0000000..fbd15407
--- /dev/null
+++ b/third_party/WebKit/Source/modules/credentialmanager/AuthenticationExtensionsClientOutputs.idl
@@ -0,0 +1,9 @@
+// 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.
+
+// https://w3c.github.io/webauthn/#dictdef-authenticationextensionsclientoutputs
+
+dictionary AuthenticationExtensionsClientOutputs {
+  boolean appid;
+};
diff --git a/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp b/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp
index 261342a7..3ac2d80 100644
--- a/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp
+++ b/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp
@@ -323,7 +323,8 @@
         AuthenticatorAttestationResponse::Create(client_data_buffer,
                                                  attestation_buffer);
     resolver->Resolve(PublicKeyCredential::Create(credential->info->id, raw_id,
-                                                  authenticator_response));
+                                                  authenticator_response,
+                                                  {} /* extensions_outputs */));
   } else {
     DCHECK(!credential);
     resolver->Reject(CredentialManagerErrorToDOMException(
@@ -360,8 +361,11 @@
         AuthenticatorAssertionResponse::Create(client_data_buffer,
                                                authenticator_buffer,
                                                signature_buffer, user_handle);
+    AuthenticationExtensionsClientOutputs extension_outputs;
+    extension_outputs.setAppid(credential->echo_appid_extension);
     resolver->Resolve(PublicKeyCredential::Create(credential->info->id, raw_id,
-                                                  authenticator_response));
+                                                  authenticator_response,
+                                                  extension_outputs));
   } else {
     DCHECK(!credential);
     resolver->Reject(CredentialManagerErrorToDOMException(
diff --git a/third_party/WebKit/Source/modules/credentialmanager/PublicKeyCredential.cpp b/third_party/WebKit/Source/modules/credentialmanager/PublicKeyCredential.cpp
index 49814069..b8e9ba1e 100644
--- a/third_party/WebKit/Source/modules/credentialmanager/PublicKeyCredential.cpp
+++ b/third_party/WebKit/Source/modules/credentialmanager/PublicKeyCredential.cpp
@@ -20,16 +20,20 @@
 PublicKeyCredential* PublicKeyCredential::Create(
     const String& id,
     DOMArrayBuffer* raw_id,
-    AuthenticatorResponse* response) {
-  return new PublicKeyCredential(id, raw_id, response);
+    AuthenticatorResponse* response,
+    const AuthenticationExtensionsClientOutputs& extension_outputs) {
+  return new PublicKeyCredential(id, raw_id, response, extension_outputs);
 }
 
-PublicKeyCredential::PublicKeyCredential(const String& id,
-                                         DOMArrayBuffer* raw_id,
-                                         AuthenticatorResponse* response)
+PublicKeyCredential::PublicKeyCredential(
+    const String& id,
+    DOMArrayBuffer* raw_id,
+    AuthenticatorResponse* response,
+    const AuthenticationExtensionsClientOutputs& extension_outputs)
     : Credential(id, kPublicKeyCredentialType),
       raw_id_(raw_id),
-      response_(response) {}
+      response_(response),
+      extension_outputs_(extension_outputs) {}
 
 ScriptPromise
 PublicKeyCredential::isUserVerifyingPlatformAuthenticatorAvailable(
@@ -39,6 +43,11 @@
       DOMException::Create(kNotSupportedError, "Operation not implemented."));
 }
 
+void PublicKeyCredential::getClientExtensionResults(
+    AuthenticationExtensionsClientOutputs& result) const {
+  result = extension_outputs_;
+}
+
 void PublicKeyCredential::Trace(blink::Visitor* visitor) {
   visitor->Trace(raw_id_);
   visitor->Trace(response_);
diff --git a/third_party/WebKit/Source/modules/credentialmanager/PublicKeyCredential.h b/third_party/WebKit/Source/modules/credentialmanager/PublicKeyCredential.h
index 9cdf0686..040d7cb9 100644
--- a/third_party/WebKit/Source/modules/credentialmanager/PublicKeyCredential.h
+++ b/third_party/WebKit/Source/modules/credentialmanager/PublicKeyCredential.h
@@ -6,9 +6,10 @@
 #define PublicKeyCredential_h
 
 #include "core/typed_arrays/DOMArrayBuffer.h"
-#include "modules/ModulesExport.h"
+#include "modules/credentialmanager/AuthenticationExtensionsClientOutputs.h"
 #include "modules/credentialmanager/AuthenticatorResponse.h"
 #include "modules/credentialmanager/Credential.h"
+#include "modules/ModulesExport.h"
 #include "platform/heap/Handle.h"
 
 namespace blink {
@@ -21,26 +22,32 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static PublicKeyCredential* Create(const String& id,
-                                     DOMArrayBuffer* raw_id,
-                                     AuthenticatorResponse*);
+  static PublicKeyCredential* Create(
+      const String& id,
+      DOMArrayBuffer* raw_id,
+      AuthenticatorResponse*,
+      const AuthenticationExtensionsClientOutputs&);
 
   DOMArrayBuffer* rawId() const { return raw_id_.Get(); }
   AuthenticatorResponse* response() const { return response_.Get(); }
   static ScriptPromise isUserVerifyingPlatformAuthenticatorAvailable(
       ScriptState*);
+  void getClientExtensionResults(AuthenticationExtensionsClientOutputs&) const;
 
   // Credential:
   void Trace(blink::Visitor*) override;
   bool IsPublicKeyCredential() const override;
 
  private:
-  explicit PublicKeyCredential(const String& id,
-                               DOMArrayBuffer* raw_id,
-                               AuthenticatorResponse*);
+  explicit PublicKeyCredential(
+      const String& id,
+      DOMArrayBuffer* raw_id,
+      AuthenticatorResponse*,
+      const AuthenticationExtensionsClientOutputs& extension_outputs);
 
   const Member<DOMArrayBuffer> raw_id_;
   const Member<AuthenticatorResponse> response_;
+  AuthenticationExtensionsClientOutputs extension_outputs_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/credentialmanager/PublicKeyCredential.idl b/third_party/WebKit/Source/modules/credentialmanager/PublicKeyCredential.idl
index d449542..cb5b091 100644
--- a/third_party/WebKit/Source/modules/credentialmanager/PublicKeyCredential.idl
+++ b/third_party/WebKit/Source/modules/credentialmanager/PublicKeyCredential.idl
@@ -12,5 +12,5 @@
     [SameObject] readonly attribute ArrayBuffer           rawId;
     [SameObject] readonly attribute AuthenticatorResponse response;
     [CallWith=ScriptState] static Promise <boolean> isUserVerifyingPlatformAuthenticatorAvailable();
-    // TODO(crbug.com/733033): Add extension support
+    AuthenticationExtensionsClientOutputs getClientExtensionResults();
 };
diff --git a/third_party/WebKit/Source/modules/modules_idl_files.gni b/third_party/WebKit/Source/modules/modules_idl_files.gni
index c40c886..7a9d109 100644
--- a/third_party/WebKit/Source/modules/modules_idl_files.gni
+++ b/third_party/WebKit/Source/modules/modules_idl_files.gni
@@ -419,6 +419,9 @@
           "xr/XRDevice.idl",
           "xr/XRDevicePose.idl",
           "xr/XRFrameOfReference.idl",
+          "xr/XRInputPose.idl",
+          "xr/XRInputSource.idl",
+          "xr/XRInputSourceEvent.idl",
           "xr/XRLayer.idl",
           "xr/XRPresentationContext.idl",
           "xr/XRPresentationFrame.idl",
@@ -461,6 +464,7 @@
           "cookie_store/CookieStoreGetOptions.idl",
           "cookie_store/CookieStoreSetOptions.idl",
           "credentialmanager/AuthenticationExtensionsClientInputs.idl",
+          "credentialmanager/AuthenticationExtensionsClientOutputs.idl",
           "credentialmanager/AuthenticatorSelectionCriteria.idl",
           "credentialmanager/CollectedClientData.idl",
           "credentialmanager/CredentialCreationOptions.idl",
@@ -630,6 +634,7 @@
           "webusb/USBDeviceFilter.idl",
           "webusb/USBDeviceRequestOptions.idl",
           "xr/XRFrameOfReferenceOptions.idl",
+          "xr/XRInputSourceEventInit.idl",
           "xr/XRSessionCreationOptions.idl",
           "xr/XRSessionEventInit.idl",
           "xr/XRWebGLLayerInit.idl",
diff --git a/third_party/WebKit/Source/modules/webaudio/BiquadDSPKernel.cpp b/third_party/WebKit/Source/modules/webaudio/BiquadDSPKernel.cpp
index acd30e4..15ff6de 100644
--- a/third_party/WebKit/Source/modules/webaudio/BiquadDSPKernel.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/BiquadDSPKernel.cpp
@@ -31,6 +31,21 @@
 
 namespace blink {
 
+static bool hasConstantValues(float* values, int frames_to_process) {
+  // TODO(rtoy): Use SIMD to optimize this.  This would speed up
+  // processing by a factor of 4 because we can process 4 floats at a
+  // time.
+  float value = values[0];
+
+  for (int k = 1; k < frames_to_process; ++k) {
+    if (values[k] != value) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 void BiquadDSPKernel::UpdateCoefficientsIfNecessary(int frames_to_process) {
   if (GetBiquadProcessor()->FilterCoefficientsDirty()) {
     float cutoff_frequency[AudioUtilities::kRenderQuantumFrames];
@@ -50,7 +65,18 @@
           gain, frames_to_process);
       GetBiquadProcessor()->Parameter4().CalculateSampleAccurateValues(
           detune, frames_to_process);
-      UpdateCoefficients(frames_to_process, cutoff_frequency, q, gain, detune);
+
+      // If all the values are actually constant for this render, we
+      // don't need to compute filter coefficients for each frame
+      // since they would be the same as the first.
+      bool isConstant =
+          hasConstantValues(cutoff_frequency, frames_to_process) &&
+          hasConstantValues(q, frames_to_process) &&
+          hasConstantValues(gain, frames_to_process) &&
+          hasConstantValues(detune, frames_to_process);
+
+      UpdateCoefficients(isConstant ? 1 : frames_to_process, cutoff_frequency,
+                         q, gain, detune);
     } else {
       cutoff_frequency[0] = GetBiquadProcessor()->Parameter1().Value();
       q[0] = GetBiquadProcessor()->Parameter2().Value();
diff --git a/third_party/WebKit/Source/modules/xr/BUILD.gn b/third_party/WebKit/Source/modules/xr/BUILD.gn
index 45ed3f0..0ce0f68 100644
--- a/third_party/WebKit/Source/modules/xr/BUILD.gn
+++ b/third_party/WebKit/Source/modules/xr/BUILD.gn
@@ -20,6 +20,12 @@
     "XRFrameProvider.h",
     "XRFrameRequestCallbackCollection.cpp",
     "XRFrameRequestCallbackCollection.h",
+    "XRInputPose.cpp",
+    "XRInputPose.h",
+    "XRInputSource.cpp",
+    "XRInputSource.h",
+    "XRInputSourceEvent.cpp",
+    "XRInputSourceEvent.h",
     "XRLayer.cpp",
     "XRLayer.h",
     "XRPresentationContext.cpp",
diff --git a/third_party/WebKit/Source/modules/xr/XRCoordinateSystem.h b/third_party/WebKit/Source/modules/xr/XRCoordinateSystem.h
index 0c3a347..4d90ca74 100644
--- a/third_party/WebKit/Source/modules/xr/XRCoordinateSystem.h
+++ b/third_party/WebKit/Source/modules/xr/XRCoordinateSystem.h
@@ -28,6 +28,9 @@
 
   virtual std::unique_ptr<TransformationMatrix> TransformBasePose(
       const TransformationMatrix& base_pose) = 0;
+  virtual std::unique_ptr<TransformationMatrix> TransformBaseInputPose(
+      const TransformationMatrix& base_input_pose,
+      const TransformationMatrix& base_pose) = 0;
 
   virtual void Trace(blink::Visitor*);
 
diff --git a/third_party/WebKit/Source/modules/xr/XRFrameOfReference.cpp b/third_party/WebKit/Source/modules/xr/XRFrameOfReference.cpp
index 3f422cd..a408179 100644
--- a/third_party/WebKit/Source/modules/xr/XRFrameOfReference.cpp
+++ b/third_party/WebKit/Source/modules/xr/XRFrameOfReference.cpp
@@ -81,6 +81,42 @@
   return nullptr;
 }
 
+// Serves the same purpose as TransformBasePose, but for input poses. Needs to
+// know the head pose so that cases like the headModel frame of reference can
+// properly adjust the input's relative position.
+std::unique_ptr<TransformationMatrix>
+XRFrameOfReference::TransformBaseInputPose(
+    const TransformationMatrix& base_input_pose,
+    const TransformationMatrix& base_pose) {
+  switch (type_) {
+    case kTypeHeadModel: {
+      std::unique_ptr<TransformationMatrix> 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();
+      float dy = head_model_pose->M42() - base_pose.M42();
+      float dz = head_model_pose->M43() - base_pose.M43();
+
+      // Translate the controller by the same delta so that it shows up in the
+      // right relative position.
+      std::unique_ptr<TransformationMatrix> pose(
+          TransformationMatrix::Create(base_input_pose));
+      pose->SetM41(pose->M41() + dx);
+      pose->SetM42(pose->M42() + dy);
+      pose->SetM43(pose->M43() + dz);
+
+      return pose;
+    } break;
+    case kTypeEyeLevel:
+    case kTypeStage:
+      return TransformBasePose(base_input_pose);
+      break;
+  }
+
+  return nullptr;
+}
+
 void XRFrameOfReference::Trace(blink::Visitor* visitor) {
   visitor->Trace(bounds_);
   XRCoordinateSystem::Trace(visitor);
diff --git a/third_party/WebKit/Source/modules/xr/XRFrameOfReference.h b/third_party/WebKit/Source/modules/xr/XRFrameOfReference.h
index 67a9edb3..9d4e102 100644
--- a/third_party/WebKit/Source/modules/xr/XRFrameOfReference.h
+++ b/third_party/WebKit/Source/modules/xr/XRFrameOfReference.h
@@ -27,6 +27,9 @@
 
   std::unique_ptr<TransformationMatrix> TransformBasePose(
       const TransformationMatrix& base_pose) override;
+  std::unique_ptr<TransformationMatrix> TransformBaseInputPose(
+      const TransformationMatrix& base_input_pose,
+      const TransformationMatrix& base_pose) override;
 
   XRStageBounds* bounds() const { return bounds_; }
   double emulatedHeight() const { return emulatedHeight_; }
diff --git a/third_party/WebKit/Source/modules/xr/XRFrameProvider.cpp b/third_party/WebKit/Source/modules/xr/XRFrameProvider.cpp
index bf806e3..d7beacb 100644
--- a/third_party/WebKit/Source/modules/xr/XRFrameProvider.cpp
+++ b/third_party/WebKit/Source/modules/xr/XRFrameProvider.cpp
@@ -105,6 +105,7 @@
     device::mojom::blink::VRRequestPresentOptionsPtr options =
         device::mojom::blink::VRRequestPresentOptions::New();
     options->preserve_drawing_buffer = false;
+    options->webxr_input = true;
 
     device_->xrDisplayHostPtr()->RequestPresent(
         frame_transport_->GetSubmitFrameClient(),
@@ -284,6 +285,11 @@
                frame_id_);
 
   if (exclusive_session_) {
+    if (frame_pose_ && frame_pose_->input_state.has_value()) {
+      exclusive_session_->OnInputStateChange(frame_id_,
+                                             frame_pose_->input_state.value());
+    }
+
     // If there's an exclusive session active only process it's frame.
     std::unique_ptr<TransformationMatrix> pose_matrix =
         getPoseMatrix(frame_pose_);
@@ -298,6 +304,12 @@
     // Inform sessions with a pending request of the new frame
     for (unsigned i = 0; i < processing_sessions.size(); ++i) {
       XRSession* session = processing_sessions.at(i).Get();
+
+      if (frame_pose_ && frame_pose_->input_state.has_value()) {
+        session->OnInputStateChange(frame_id_,
+                                    frame_pose_->input_state.value());
+      }
+
       std::unique_ptr<TransformationMatrix> pose_matrix =
           getPoseMatrix(frame_pose_);
       session->OnFrame(std::move(pose_matrix));
diff --git a/third_party/WebKit/Source/modules/xr/XRInputPose.cpp b/third_party/WebKit/Source/modules/xr/XRInputPose.cpp
new file mode 100644
index 0000000..b877268a
--- /dev/null
+++ b/third_party/WebKit/Source/modules/xr/XRInputPose.cpp
@@ -0,0 +1,53 @@
+// 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 "modules/xr/XRInputPose.h"
+
+namespace blink {
+
+namespace {
+
+DOMFloat32Array* transformationMatrixToFloat32Array(
+    const TransformationMatrix& matrix) {
+  float array[] = {
+      static_cast<float>(matrix.M11()), static_cast<float>(matrix.M12()),
+      static_cast<float>(matrix.M13()), static_cast<float>(matrix.M14()),
+      static_cast<float>(matrix.M21()), static_cast<float>(matrix.M22()),
+      static_cast<float>(matrix.M23()), static_cast<float>(matrix.M24()),
+      static_cast<float>(matrix.M31()), static_cast<float>(matrix.M32()),
+      static_cast<float>(matrix.M33()), static_cast<float>(matrix.M34()),
+      static_cast<float>(matrix.M41()), static_cast<float>(matrix.M42()),
+      static_cast<float>(matrix.M43()), static_cast<float>(matrix.M44())};
+
+  return DOMFloat32Array::Create(array, 16);
+}
+
+}  // namespace
+
+XRInputPose::XRInputPose(std::unique_ptr<TransformationMatrix> pointer_matrix,
+                         std::unique_ptr<TransformationMatrix> grip_matrix,
+                         bool emulated_position)
+    : pointer_matrix_(std::move(pointer_matrix)),
+      grip_matrix_(std::move(grip_matrix)),
+      emulated_position_(emulated_position) {}
+
+XRInputPose::~XRInputPose() {}
+
+DOMFloat32Array* XRInputPose::pointerMatrix() const {
+  if (!pointer_matrix_)
+    return nullptr;
+  return transformationMatrixToFloat32Array(*pointer_matrix_);
+}
+
+DOMFloat32Array* XRInputPose::gripMatrix() const {
+  if (!grip_matrix_)
+    return nullptr;
+  return transformationMatrixToFloat32Array(*grip_matrix_);
+}
+
+void XRInputPose::Trace(blink::Visitor* visitor) {
+  ScriptWrappable::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/modules/xr/XRInputPose.h b/third_party/WebKit/Source/modules/xr/XRInputPose.h
new file mode 100644
index 0000000..145e9205
--- /dev/null
+++ b/third_party/WebKit/Source/modules/xr/XRInputPose.h
@@ -0,0 +1,39 @@
+// 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 XRInputPose_h
+#define XRInputPose_h
+
+#include "core/typed_arrays/DOMTypedArray.h"
+#include "platform/bindings/ScriptWrappable.h"
+#include "platform/heap/Handle.h"
+#include "platform/transforms/TransformationMatrix.h"
+#include "platform/wtf/Forward.h"
+
+namespace blink {
+
+class XRInputPose final : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  XRInputPose(std::unique_ptr<TransformationMatrix> pointer_matrix,
+              std::unique_ptr<TransformationMatrix> grip_matrix,
+              bool emulated_position = false);
+  ~XRInputPose();
+
+  DOMFloat32Array* pointerMatrix() const;
+  DOMFloat32Array* gripMatrix() const;
+  bool emulatedPosition() const { return emulated_position_; }
+
+  virtual void Trace(blink::Visitor*);
+
+ private:
+  const std::unique_ptr<TransformationMatrix> pointer_matrix_;
+  const std::unique_ptr<TransformationMatrix> grip_matrix_;
+  const bool emulated_position_;
+};
+
+}  // namespace blink
+
+#endif  // XRInputPose_h
diff --git a/third_party/WebKit/Source/modules/xr/XRInputPose.idl b/third_party/WebKit/Source/modules/xr/XRInputPose.idl
new file mode 100644
index 0000000..9f256df6
--- /dev/null
+++ b/third_party/WebKit/Source/modules/xr/XRInputPose.idl
@@ -0,0 +1,12 @@
+// 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.
+
+[
+    SecureContext,
+    RuntimeEnabled=WebXR
+] interface XRInputPose {
+  readonly attribute Float32Array pointerMatrix;
+  readonly attribute Float32Array? gripMatrix;
+  readonly attribute boolean emulatedPosition;
+};
\ No newline at end of file
diff --git a/third_party/WebKit/Source/modules/xr/XRInputSource.cpp b/third_party/WebKit/Source/modules/xr/XRInputSource.cpp
new file mode 100644
index 0000000..986d13f3
--- /dev/null
+++ b/third_party/WebKit/Source/modules/xr/XRInputSource.cpp
@@ -0,0 +1,77 @@
+// 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 "modules/xr/XRInputSource.h"
+#include "modules/xr/XRSession.h"
+
+namespace blink {
+
+XRInputSource::XRInputSource(XRSession* session, uint32_t source_id)
+    : session_(session), source_id_(source_id) {
+  SetPointerOrigin(kOriginHead);
+  SetHandedness(kHandNone);
+}
+
+void XRInputSource::SetPointerOrigin(PointerOrigin pointer_origin) {
+  if (pointer_origin_ == pointer_origin)
+    return;
+
+  pointer_origin_ = pointer_origin;
+
+  switch (pointer_origin_) {
+    case kOriginHead:
+      pointer_origin_string_ = "head";
+      break;
+    case kOriginHand:
+      pointer_origin_string_ = "hand";
+      break;
+    case kOriginScreen:
+      pointer_origin_string_ = "screen";
+      break;
+    default:
+      NOTREACHED() << "Unknown pointer origin: " << pointer_origin_;
+  }
+}
+
+void XRInputSource::SetHandedness(Handedness handedness) {
+  if (handedness_ == handedness)
+    return;
+
+  handedness_ = handedness;
+
+  switch (handedness_) {
+    case kHandNone:
+      handedness_string_ = "";
+      break;
+    case kHandLeft:
+      handedness_string_ = "left";
+      break;
+    case kHandRight:
+      handedness_string_ = "right";
+      break;
+    default:
+      NOTREACHED() << "Unknown handedness: " << handedness_;
+  }
+}
+
+void XRInputSource::SetEmulatedPosition(bool emulated_position) {
+  emulated_position_ = emulated_position;
+}
+
+void XRInputSource::SetBasePoseMatrix(
+    std::unique_ptr<TransformationMatrix> base_pose_matrix) {
+  base_pose_matrix_ = std::move(base_pose_matrix);
+}
+
+void XRInputSource::SetPointerTransformMatrix(
+    std::unique_ptr<TransformationMatrix> pointer_transform_matrix) {
+  pointer_transform_matrix_ = std::move(pointer_transform_matrix);
+}
+
+void XRInputSource::Trace(blink::Visitor* visitor) {
+  visitor->Trace(session_);
+  ScriptWrappable::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/modules/xr/XRInputSource.h b/third_party/WebKit/Source/modules/xr/XRInputSource.h
new file mode 100644
index 0000000..88679f4
--- /dev/null
+++ b/third_party/WebKit/Source/modules/xr/XRInputSource.h
@@ -0,0 +1,71 @@
+// 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 XRInputSource_h
+#define XRInputSource_h
+
+#include "platform/bindings/ScriptWrappable.h"
+#include "platform/heap/Handle.h"
+#include "platform/transforms/TransformationMatrix.h"
+#include "platform/wtf/Forward.h"
+#include "platform/wtf/text/WTFString.h"
+
+namespace blink {
+
+class XRSession;
+
+class XRInputSource : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  enum Handedness { kHandNone = 0, kHandLeft = 1, kHandRight = 2 };
+  enum PointerOrigin { kOriginHead = 1, kOriginHand = 2, kOriginScreen = 3 };
+
+  XRInputSource(XRSession*, uint32_t source_id);
+  virtual ~XRInputSource() = default;
+
+  XRSession* session() const { return session_; }
+
+  const String& handedness() const { return handedness_string_; }
+  const String& pointerOrigin() const { return pointer_origin_string_; }
+  bool emulatedPosition() const { return emulated_position_; }
+
+  uint32_t source_id() const { return source_id_; }
+
+  void SetPointerOrigin(PointerOrigin);
+  void SetHandedness(Handedness);
+  void SetEmulatedPosition(bool emulated_position);
+  void SetBasePoseMatrix(std::unique_ptr<TransformationMatrix>);
+  void SetPointerTransformMatrix(std::unique_ptr<TransformationMatrix>);
+
+  virtual void Trace(blink::Visitor*);
+
+  int16_t active_frame_id = -1;
+  bool primary_input_pressed = false;
+  bool selection_cancelled = false;
+
+ private:
+  friend class XRPresentationFrame;
+
+  const Member<XRSession> session_;
+  const uint32_t source_id_;
+
+  Handedness handedness_;
+  String handedness_string_;
+
+  PointerOrigin pointer_origin_;
+  String pointer_origin_string_;
+
+  bool emulated_position_ = false;
+
+  std::unique_ptr<TransformationMatrix> base_pose_matrix_;
+
+  // This is the transform to apply to the base_pose_matrix_ to get the pointer
+  // matrix. In most cases it should be static.
+  std::unique_ptr<TransformationMatrix> pointer_transform_matrix_;
+};
+
+}  // namespace blink
+
+#endif  // XRInputSource_h
diff --git a/third_party/WebKit/Source/modules/xr/XRInputSource.idl b/third_party/WebKit/Source/modules/xr/XRInputSource.idl
new file mode 100644
index 0000000..a9171de
--- /dev/null
+++ b/third_party/WebKit/Source/modules/xr/XRInputSource.idl
@@ -0,0 +1,22 @@
+// 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.
+
+enum XRHandedness {
+  "left",
+  "right"
+};
+
+enum XRPointerOrigin {
+  "head",
+  "grip",
+  "screen"
+};
+
+[
+    SecureContext,
+    RuntimeEnabled=WebXR
+] interface XRInputSource {
+  readonly attribute XRHandedness handedness;
+  readonly attribute XRPointerOrigin pointerOrigin;
+};
\ No newline at end of file
diff --git a/third_party/WebKit/Source/modules/xr/XRInputSourceEvent.cpp b/third_party/WebKit/Source/modules/xr/XRInputSourceEvent.cpp
new file mode 100644
index 0000000..0a8cecea
--- /dev/null
+++ b/third_party/WebKit/Source/modules/xr/XRInputSourceEvent.cpp
@@ -0,0 +1,38 @@
+// 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 "modules/xr/XRInputSourceEvent.h"
+
+namespace blink {
+
+XRInputSourceEvent::XRInputSourceEvent() {}
+
+XRInputSourceEvent::XRInputSourceEvent(const AtomicString& type,
+                                       XRPresentationFrame* frame,
+                                       XRInputSource* input_source)
+    : Event(type, true, false), frame_(frame), input_source_(input_source) {}
+
+XRInputSourceEvent::XRInputSourceEvent(
+    const AtomicString& type,
+    const XRInputSourceEventInit& initializer)
+    : Event(type, initializer) {
+  if (initializer.hasFrame())
+    frame_ = initializer.frame();
+  if (initializer.hasInputSource())
+    input_source_ = initializer.inputSource();
+}
+
+XRInputSourceEvent::~XRInputSourceEvent() {}
+
+const AtomicString& XRInputSourceEvent::InterfaceName() const {
+  return EventNames::XRInputSourceEvent;
+}
+
+void XRInputSourceEvent::Trace(blink::Visitor* visitor) {
+  visitor->Trace(frame_);
+  visitor->Trace(input_source_);
+  Event::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/modules/xr/XRInputSourceEvent.h b/third_party/WebKit/Source/modules/xr/XRInputSourceEvent.h
new file mode 100644
index 0000000..63dadf5
--- /dev/null
+++ b/third_party/WebKit/Source/modules/xr/XRInputSourceEvent.h
@@ -0,0 +1,53 @@
+// 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 XRInputSourceEvent_h
+#define XRInputSourceEvent_h
+
+#include "modules/EventModules.h"
+#include "modules/xr/XRInputSource.h"
+#include "modules/xr/XRInputSourceEventInit.h"
+#include "modules/xr/XRPresentationFrame.h"
+
+namespace blink {
+
+class XRInputSourceEvent final : public Event {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static XRInputSourceEvent* Create() { return new XRInputSourceEvent; }
+  static XRInputSourceEvent* Create(const AtomicString& type,
+                                    XRPresentationFrame* frame,
+                                    XRInputSource* input_source) {
+    return new XRInputSourceEvent(type, frame, input_source);
+  }
+
+  static XRInputSourceEvent* Create(const AtomicString& type,
+                                    const XRInputSourceEventInit& initializer) {
+    return new XRInputSourceEvent(type, initializer);
+  }
+
+  ~XRInputSourceEvent() override;
+
+  XRPresentationFrame* frame() const { return frame_.Get(); }
+  XRInputSource* inputSource() const { return input_source_.Get(); }
+
+  const AtomicString& InterfaceName() const override;
+
+  virtual void Trace(blink::Visitor*);
+
+ private:
+  XRInputSourceEvent();
+  XRInputSourceEvent(const AtomicString& type,
+                     XRPresentationFrame*,
+                     XRInputSource*);
+  XRInputSourceEvent(const AtomicString&, const XRInputSourceEventInit&);
+
+  Member<XRPresentationFrame> frame_;
+  Member<XRInputSource> input_source_;
+};
+
+}  // namespace blink
+
+#endif  // XRInputSourceEvent_h
diff --git a/third_party/WebKit/Source/modules/xr/XRInputSourceEvent.idl b/third_party/WebKit/Source/modules/xr/XRInputSourceEvent.idl
new file mode 100644
index 0000000..062ebd2
--- /dev/null
+++ b/third_party/WebKit/Source/modules/xr/XRInputSourceEvent.idl
@@ -0,0 +1,12 @@
+// 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.
+
+[
+    SecureContext,
+    RuntimeEnabled=WebXR,
+    Constructor(DOMString type, XRInputSourceEventInit eventInitDict)
+] interface XRInputSourceEvent : Event {
+  readonly attribute XRPresentationFrame frame;
+  readonly attribute XRInputSource inputSource;
+};
diff --git a/third_party/WebKit/Source/modules/xr/XRInputSourceEventInit.idl b/third_party/WebKit/Source/modules/xr/XRInputSourceEventInit.idl
new file mode 100644
index 0000000..b24af31
--- /dev/null
+++ b/third_party/WebKit/Source/modules/xr/XRInputSourceEventInit.idl
@@ -0,0 +1,10 @@
+// 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.
+
+[
+    SecureContext
+] dictionary XRInputSourceEventInit : EventInit {
+  required XRPresentationFrame frame;
+  required XRInputSource inputSource;
+};
\ No newline at end of file
diff --git a/third_party/WebKit/Source/modules/xr/XRPresentationFrame.cpp b/third_party/WebKit/Source/modules/xr/XRPresentationFrame.cpp
index 27fa3879c..1c23973 100644
--- a/third_party/WebKit/Source/modules/xr/XRPresentationFrame.cpp
+++ b/third_party/WebKit/Source/modules/xr/XRPresentationFrame.cpp
@@ -6,6 +6,8 @@
 
 #include "modules/xr/XRCoordinateSystem.h"
 #include "modules/xr/XRDevicePose.h"
+#include "modules/xr/XRInputPose.h"
+#include "modules/xr/XRInputSource.h"
 #include "modules/xr/XRSession.h"
 #include "modules/xr/XRView.h"
 
@@ -41,9 +43,82 @@
   return new XRDevicePose(session(), std::move(pose));
 }
 
-void XRPresentationFrame::UpdateBasePose(
-    std::unique_ptr<TransformationMatrix> base_pose_matrix) {
-  base_pose_matrix_ = std::move(base_pose_matrix);
+XRInputPose* XRPresentationFrame::getInputPose(
+    XRInputSource* input_source,
+    XRCoordinateSystem* coordinate_system) const {
+  if (!input_source || !coordinate_system) {
+    return nullptr;
+  }
+
+  // Must use an input source and coordinate system from the same session.
+  if (input_source->session() != session_ ||
+      coordinate_system->session() != session_) {
+    return nullptr;
+  }
+
+  switch (input_source->pointer_origin_) {
+    case XRInputSource::kOriginScreen: {
+      // If the pointer origin is the screen we need the head's base pose and
+      // the pointer transform matrix to continue. The pointer transform will
+      // represent the point the canvas was clicked as an offset from the view.
+      if (!base_pose_matrix_ || !input_source->pointer_transform_matrix_) {
+        return nullptr;
+      }
+
+      // Multiply the head pose and pointer transform to get the final pointer.
+      std::unique_ptr<TransformationMatrix> pointer_pose =
+          coordinate_system->TransformBasePose(*base_pose_matrix_);
+      pointer_pose->Multiply(*(input_source->pointer_transform_matrix_));
+
+      return new XRInputPose(std::move(pointer_pose), nullptr);
+    }
+    case XRInputSource::kOriginHead: {
+      // If the pointer origin is the users head, this is a gaze cursor and the
+      // returned pointer is based on the device pose. If we don't have a valid
+      // base pose (most common when tracking is lost) return null.
+      if (!base_pose_matrix_) {
+        return nullptr;
+      }
+
+      // Just return the head pose as the pointer pose.
+      std::unique_ptr<TransformationMatrix> pointer_pose =
+          coordinate_system->TransformBasePose(*base_pose_matrix_);
+
+      return new XRInputPose(std::move(pointer_pose), nullptr,
+                             input_source->emulatedPosition());
+    }
+    case XRInputSource::kOriginHand: {
+      // If the input source doesn't have a base pose return null;
+      if (!input_source->base_pose_matrix_) {
+        return nullptr;
+      }
+
+      std::unique_ptr<TransformationMatrix> grip_pose =
+          coordinate_system->TransformBaseInputPose(
+              *(input_source->base_pose_matrix_), *base_pose_matrix_);
+
+      if (!grip_pose) {
+        return nullptr;
+      }
+
+      std::unique_ptr<TransformationMatrix> pointer_pose(
+          TransformationMatrix::Create(*grip_pose));
+
+      if (input_source->pointer_transform_matrix_) {
+        pointer_pose->Multiply(*(input_source->pointer_transform_matrix_));
+      }
+
+      return new XRInputPose(std::move(pointer_pose), std::move(grip_pose),
+                             input_source->emulatedPosition());
+    }
+  }
+
+  return nullptr;
+}
+
+void XRPresentationFrame::SetBasePoseMatrix(
+    const TransformationMatrix& base_pose_matrix) {
+  base_pose_matrix_ = TransformationMatrix::Create(base_pose_matrix);
 }
 
 void XRPresentationFrame::Trace(blink::Visitor* visitor) {
diff --git a/third_party/WebKit/Source/modules/xr/XRPresentationFrame.h b/third_party/WebKit/Source/modules/xr/XRPresentationFrame.h
index 95b86ed5..a69ac77 100644
--- a/third_party/WebKit/Source/modules/xr/XRPresentationFrame.h
+++ b/third_party/WebKit/Source/modules/xr/XRPresentationFrame.h
@@ -16,6 +16,8 @@
 
 class XRCoordinateSystem;
 class XRDevicePose;
+class XRInputPose;
+class XRInputSource;
 class XRSession;
 class XRView;
 
@@ -29,8 +31,9 @@
 
   const HeapVector<Member<XRView>>& views() const;
   XRDevicePose* getDevicePose(XRCoordinateSystem*) const;
+  XRInputPose* getInputPose(XRInputSource*, XRCoordinateSystem*) const;
 
-  void UpdateBasePose(std::unique_ptr<TransformationMatrix>);
+  void SetBasePoseMatrix(const TransformationMatrix&);
 
   virtual void Trace(blink::Visitor*);
 
diff --git a/third_party/WebKit/Source/modules/xr/XRPresentationFrame.idl b/third_party/WebKit/Source/modules/xr/XRPresentationFrame.idl
index 3a64182..8267428 100644
--- a/third_party/WebKit/Source/modules/xr/XRPresentationFrame.idl
+++ b/third_party/WebKit/Source/modules/xr/XRPresentationFrame.idl
@@ -11,4 +11,6 @@
   readonly attribute FrozenArray<XRView> views;
 
   XRDevicePose? getDevicePose(XRCoordinateSystem coordinateSystem);
+  XRInputPose? getInputPose(XRInputSource inputSource,
+                            XRCoordinateSystem coordinateSystem);
 };
diff --git a/third_party/WebKit/Source/modules/xr/XRSession.cpp b/third_party/WebKit/Source/modules/xr/XRSession.cpp
index 5f0f7d2..250f9a2 100644
--- a/third_party/WebKit/Source/modules/xr/XRSession.cpp
+++ b/third_party/WebKit/Source/modules/xr/XRSession.cpp
@@ -17,6 +17,7 @@
 #include "modules/xr/XRFrameOfReference.h"
 #include "modules/xr/XRFrameOfReferenceOptions.h"
 #include "modules/xr/XRFrameProvider.h"
+#include "modules/xr/XRInputSourceEvent.h"
 #include "modules/xr/XRLayer.h"
 #include "modules/xr/XRPresentationContext.h"
 #include "modules/xr/XRPresentationFrame.h"
@@ -265,13 +266,14 @@
   if (ended_)
     return;
 
+  base_pose_matrix_ = std::move(base_pose_matrix);
+
   // Don't allow frames to be processed if there's no layers attached to the
   // session. That would allow tracking with no associated visuals.
   if (!base_layer_)
     return;
 
-  XRPresentationFrame* presentation_frame = new XRPresentationFrame(this);
-  presentation_frame->UpdateBasePose(std::move(base_pose_matrix));
+  XRPresentationFrame* presentation_frame = CreatePresentationFrame();
 
   if (pending_frame_) {
     pending_frame_ = false;
@@ -290,6 +292,14 @@
   }
 }
 
+XRPresentationFrame* XRSession::CreatePresentationFrame() {
+  XRPresentationFrame* presentation_frame = new XRPresentationFrame(this);
+  if (base_pose_matrix_) {
+    presentation_frame->SetBasePoseMatrix(*base_pose_matrix_);
+  }
+  return presentation_frame;
+}
+
 // Called when the canvas element for this session's output context is resized.
 void XRSession::UpdateCanvasDimensions(Element* element) {
   DCHECK(element);
@@ -309,6 +319,164 @@
   }
 }
 
+void XRSession::OnInputStateChange(
+    int16_t frame_id,
+    const WTF::Vector<device::mojom::blink::XRInputSourceStatePtr>&
+        input_states) {
+  bool devices_changed = false;
+
+  // Update any input sources with new state information. Any updated input
+  // sources are marked as active.
+  for (const auto& input_state : input_states) {
+    XRInputSource* input_source = input_sources_.at(input_state->source_id);
+    if (!input_source) {
+      input_source = new XRInputSource(this, input_state->source_id);
+      input_sources_.Set(input_state->source_id, input_source);
+      devices_changed = true;
+    }
+    input_source->active_frame_id = frame_id;
+    UpdateInputSourceState(input_source, input_state);
+  }
+
+  // Remove any input sources that are inactive..
+  std::vector<uint32_t> inactive_sources;
+  for (const auto& input_source : input_sources_.Values()) {
+    if (input_source->active_frame_id != frame_id) {
+      inactive_sources.push_back(input_source->source_id());
+      devices_changed = true;
+    }
+  }
+
+  if (inactive_sources.size()) {
+    for (uint32_t source_id : inactive_sources) {
+      input_sources_.erase(source_id);
+    }
+  }
+
+  if (devices_changed) {
+    DispatchEvent(
+        XRSessionEvent::Create(EventTypeNames::inputsourceschange, this));
+  }
+}
+
+void XRSession::OnSelectStart(XRInputSource* input_source) {
+  // Discard duplicate events
+  if (input_source->primary_input_pressed)
+    return;
+
+  input_source->primary_input_pressed = true;
+  input_source->selection_cancelled = false;
+
+  XRInputSourceEvent* event =
+      CreateInputSourceEvent(EventTypeNames::selectstart, input_source);
+  DispatchEvent(event);
+
+  if (event->defaultPrevented())
+    input_source->selection_cancelled = true;
+}
+
+void XRSession::OnSelectEnd(XRInputSource* input_source) {
+  // Discard duplicate events
+  if (!input_source->primary_input_pressed)
+    return;
+
+  input_source->primary_input_pressed = false;
+
+  LocalFrame* frame = device_->xr()->GetFrame();
+  if (!frame)
+    return;
+
+  std::unique_ptr<UserGestureIndicator> gesture_indicator =
+      LocalFrame::CreateUserGesture(frame);
+
+  XRInputSourceEvent* event =
+      CreateInputSourceEvent(EventTypeNames::selectend, input_source);
+  DispatchEvent(event);
+
+  if (event->defaultPrevented())
+    input_source->selection_cancelled = true;
+}
+
+void XRSession::OnSelect(XRInputSource* input_source) {
+  // If a select was fired but we had not previously started the selection it
+  // indictes a sub-frame or instantanous select event, and we should fire a
+  // selectstart prior to the selectend.
+  if (!input_source->primary_input_pressed) {
+    OnSelectStart(input_source);
+  }
+
+  // Make sure we end the selection prior to firing the select event.
+  OnSelectEnd(input_source);
+
+  if (!input_source->selection_cancelled) {
+    XRInputSourceEvent* event =
+        CreateInputSourceEvent(EventTypeNames::select, input_source);
+    DispatchEvent(event);
+  }
+}
+
+void XRSession::UpdateInputSourceState(
+    XRInputSource* input_source,
+    const device::mojom::blink::XRInputSourceStatePtr& state) {
+  if (!input_source || !state)
+    return;
+
+  // Update the input source's description if this state update
+  // includes them.
+  if (state->description) {
+    const device::mojom::blink::XRInputSourceDescriptionPtr& desc =
+        state->description;
+
+    input_source->SetPointerOrigin(
+        static_cast<XRInputSource::PointerOrigin>(desc->pointer_origin));
+
+    input_source->SetHandedness(
+        static_cast<XRInputSource::Handedness>(desc->handedness));
+
+    input_source->SetEmulatedPosition(desc->emulated_position);
+
+    if (desc->pointer_offset) {
+      const WTF::Vector<float>& m = desc->pointer_offset->matrix.value();
+      std::unique_ptr<TransformationMatrix> pointer_matrix =
+          TransformationMatrix::Create(m[0], m[1], m[2], m[3], m[4], m[5], m[6],
+                                       m[7], m[8], m[9], m[10], m[11], m[12],
+                                       m[13], m[14], m[15]);
+      input_source->SetPointerTransformMatrix(std::move(pointer_matrix));
+    }
+  }
+
+  if (state->grip) {
+    const Vector<float>& m = state->grip->matrix.value();
+    std::unique_ptr<TransformationMatrix> grip_matrix =
+        TransformationMatrix::Create(m[0], m[1], m[2], m[3], m[4], m[5], m[6],
+                                     m[7], m[8], m[9], m[10], m[11], m[12],
+                                     m[13], m[14], m[15]);
+    input_source->SetBasePoseMatrix(std::move(grip_matrix));
+  }
+
+  // Handle state change of the primary input, which may fire events
+  if (state->primary_input_clicked)
+    OnSelect(input_source);
+
+  if (state->primary_input_pressed) {
+    OnSelectStart(input_source);
+  } else if (input_source->primary_input_pressed) {
+    // May get here if the input source was previously pressed but now isn't,
+    // but the input source did not set primary_input_clicked to true. We will
+    // treat this as a cancelled selection, firing the selectend event so the
+    // page stays in sync with the controller state but won't fire the
+    // usual select event.
+    OnSelectEnd(input_source);
+  }
+}
+
+XRInputSourceEvent* XRSession::CreateInputSourceEvent(
+    const AtomicString& type,
+    XRInputSource* input_source) {
+  XRPresentationFrame* presentation_frame = CreatePresentationFrame();
+  return XRInputSourceEvent::Create(type, presentation_frame, input_source);
+}
+
 const HeapVector<Member<XRView>>& XRSession::views() {
   // TODO(bajones): For now we assume that exclusive sessions render a stereo
   // pair of views and non-exclusive sessions render a single view. That doesn't
@@ -359,6 +527,7 @@
   visitor->Trace(output_context_);
   visitor->Trace(base_layer_);
   visitor->Trace(views_);
+  visitor->Trace(input_sources_);
   visitor->Trace(resize_observer_);
   visitor->Trace(callback_collection_);
   EventTargetWithInlineData::Trace(visitor);
@@ -366,6 +535,9 @@
 
 void XRSession::TraceWrappers(
     const blink::ScriptWrappableVisitor* visitor) const {
+  for (const auto& input_source : input_sources_.Values())
+    visitor->TraceWrappers(input_source);
+
   visitor->TraceWrappers(callback_collection_);
   EventTargetWithInlineData::TraceWrappers(visitor);
 }
diff --git a/third_party/WebKit/Source/modules/xr/XRSession.h b/third_party/WebKit/Source/modules/xr/XRSession.h
index 2a535b4..8381044f 100644
--- a/third_party/WebKit/Source/modules/xr/XRSession.h
+++ b/third_party/WebKit/Source/modules/xr/XRSession.h
@@ -7,7 +7,10 @@
 
 #include "bindings/core/v8/ScriptPromise.h"
 #include "core/dom/events/EventTarget.h"
+#include "device/vr/public/mojom/vr_service.mojom-blink.h"
 #include "modules/xr/XRFrameRequestCallbackCollection.h"
+#include "modules/xr/XRInputSource.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "platform/bindings/TraceWrapperMember.h"
 #include "platform/geometry/DoubleSize.h"
 #include "platform/heap/Handle.h"
@@ -21,6 +24,7 @@
 class V8XRFrameRequestCallback;
 class XRDevice;
 class XRFrameOfReferenceOptions;
+class XRInputSourceEvent;
 class XRLayer;
 class XRPresentationContext;
 class XRView;
@@ -52,6 +56,10 @@
   DEFINE_ATTRIBUTE_EVENT_LISTENER(resetpose);
   DEFINE_ATTRIBUTE_EVENT_LISTENER(end);
 
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(selectstart);
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(selectend);
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(select);
+
   ScriptPromise requestFrameOfReference(ScriptState*,
                                         const String& type,
                                         const XRFrameOfReferenceOptions&);
@@ -59,6 +67,17 @@
   int requestAnimationFrame(V8XRFrameRequestCallback*);
   void cancelAnimationFrame(int id);
 
+  using InputSourceMap =
+      HeapHashMap<uint32_t, TraceWrapperMember<XRInputSource>>;
+
+  HeapVector<Member<XRInputSource>> getInputSources() const {
+    HeapVector<Member<XRInputSource>> source_array;
+    for (const auto& input_source : input_sources_.Values()) {
+      source_array.push_back(input_source);
+    }
+    return source_array;
+  }
+
   // Called by JavaScript to manually end the session.
   ScriptPromise end(ScriptState*);
 
@@ -84,6 +103,9 @@
   void OnFocus();
   void OnBlur();
   void OnFrame(std::unique_ptr<TransformationMatrix>);
+  void OnInputStateChange(
+      int16_t frame_id,
+      const WTF::Vector<device::mojom::blink::XRInputSourceStatePtr>&);
 
   const HeapVector<Member<XRView>>& views();
 
@@ -93,16 +115,28 @@
  private:
   class XRSessionResizeObserverDelegate;
 
+  XRPresentationFrame* CreatePresentationFrame();
   void UpdateCanvasDimensions(Element*);
 
+  void UpdateInputSourceState(
+      XRInputSource*,
+      const device::mojom::blink::XRInputSourceStatePtr&);
+  void OnSelectStart(XRInputSource*);
+  void OnSelectEnd(XRInputSource*);
+  void OnSelect(XRInputSource*);
+  XRInputSourceEvent* CreateInputSourceEvent(const AtomicString&,
+                                             XRInputSource*);
+
   const Member<XRDevice> device_;
   const bool exclusive_;
   const Member<XRPresentationContext> output_context_;
   Member<XRLayer> base_layer_;
   HeapVector<Member<XRView>> views_;
+  InputSourceMap input_sources_;
   Member<ResizeObserver> resize_observer_;
 
   XRFrameRequestCallbackCollection callback_collection_;
+  std::unique_ptr<TransformationMatrix> base_pose_matrix_;
 
   double depth_near_ = 0.1;
   double depth_far_ = 1000.0;
diff --git a/third_party/WebKit/Source/modules/xr/XRSession.idl b/third_party/WebKit/Source/modules/xr/XRSession.idl
index 5061065..072ea78f 100644
--- a/third_party/WebKit/Source/modules/xr/XRSession.idl
+++ b/third_party/WebKit/Source/modules/xr/XRSession.idl
@@ -26,5 +26,7 @@
   long requestAnimationFrame(XRFrameRequestCallback callback);
   void cancelAnimationFrame(long handle);
 
+  FrozenArray<XRInputSource> getInputSources();
+
   [CallWith=ScriptState] Promise<void> end();
 };
diff --git a/third_party/WebKit/Source/modules/xr/XRSessionEvent.cpp b/third_party/WebKit/Source/modules/xr/XRSessionEvent.cpp
index 9201331..bef76272 100644
--- a/third_party/WebKit/Source/modules/xr/XRSessionEvent.cpp
+++ b/third_party/WebKit/Source/modules/xr/XRSessionEvent.cpp
@@ -9,7 +9,8 @@
 XRSessionEvent::XRSessionEvent() = default;
 
 XRSessionEvent::XRSessionEvent(const AtomicString& type, XRSession* session)
-    : Event(type, true, false), session_(session) {}
+    : Event(type, false /* can_bubble */, true /* cancellable */),
+      session_(session) {}
 
 XRSessionEvent::XRSessionEvent(const AtomicString& type,
                                const XRSessionEventInit& initializer)
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index 0fc92021..aca0f8b 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -958,8 +958,6 @@
     "graphics/DrawLooperBuilder.h",
     "graphics/FirstPaintInvalidationTracking.cpp",
     "graphics/FirstPaintInvalidationTracking.h",
-    "graphics/FrameData.cpp",
-    "graphics/FrameData.h",
     "graphics/GeneratedImage.cpp",
     "graphics/GeneratedImage.h",
     "graphics/GpuMemoryBufferImageCopy.cpp",
diff --git a/third_party/WebKit/Source/platform/DragImageTest.cpp b/third_party/WebKit/Source/platform/DragImageTest.cpp
index 13f41a5..e77496f 100644
--- a/third_party/WebKit/Source/platform/DragImageTest.cpp
+++ b/third_party/WebKit/Source/platform/DragImageTest.cpp
@@ -63,10 +63,7 @@
     return IntSize(image_->width(), image_->height());
   }
 
-  bool CurrentFrameKnownToBeOpaque(
-      MetadataMode = kUseCurrentMetadata) override {
-    return false;
-  }
+  bool CurrentFrameKnownToBeOpaque() override { return false; }
 
   void DestroyDecodedData() override {
     // Image pure virtual stub.
diff --git a/third_party/WebKit/Source/platform/LayoutTestSupport.cpp b/third_party/WebKit/Source/platform/LayoutTestSupport.cpp
index f41932b1..5b9f216 100644
--- a/third_party/WebKit/Source/platform/LayoutTestSupport.cpp
+++ b/third_party/WebKit/Source/platform/LayoutTestSupport.cpp
@@ -55,6 +55,7 @@
 static bool g_is_running_layout_test = false;
 static bool g_is_mock_theme_enabled = false;
 static bool g_is_font_antialiasing_enabled = false;
+static bool g_is_subpixel_positioning_allowed = true;
 
 bool LayoutTestSupport::IsRunningLayoutTest() {
   return g_is_running_layout_test;
@@ -81,4 +82,12 @@
   g_is_font_antialiasing_enabled = value;
 }
 
+bool LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest() {
+  return g_is_subpixel_positioning_allowed;
+}
+
+void LayoutTestSupport::SetTextSubpixelPositioningAllowedForTest(bool value) {
+  g_is_subpixel_positioning_allowed = value;
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/LayoutTestSupport.h b/third_party/WebKit/Source/platform/LayoutTestSupport.h
index 1a8315d..7dd3ffc 100644
--- a/third_party/WebKit/Source/platform/LayoutTestSupport.h
+++ b/third_party/WebKit/Source/platform/LayoutTestSupport.h
@@ -46,6 +46,8 @@
   PLATFORM_EXPORT static void SetMockThemeEnabledForTest(bool);
   PLATFORM_EXPORT static bool IsFontAntialiasingEnabledForTest();
   PLATFORM_EXPORT static void SetFontAntialiasingEnabledForTest(bool);
+  PLATFORM_EXPORT static bool IsTextSubpixelPositioningAllowedForTest();
+  PLATFORM_EXPORT static void SetTextSubpixelPositioningAllowedForTest(bool);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/bindings/ScriptWrappableMarkingVisitor.cpp b/third_party/WebKit/Source/platform/bindings/ScriptWrappableMarkingVisitor.cpp
index e27a426..875f358 100644
--- a/third_party/WebKit/Source/platform/bindings/ScriptWrappableMarkingVisitor.cpp
+++ b/third_party/WebKit/Source/platform/bindings/ScriptWrappableMarkingVisitor.cpp
@@ -253,9 +253,9 @@
 }
 
 void ScriptWrappableMarkingVisitor::Visit(
-    const WrapperDescriptor& wrapper_descriptor) const {
-  HeapObjectHeader* header = wrapper_descriptor.heap_object_header_callback(
-      wrapper_descriptor.traceable);
+    const TraceWrapperDescriptor& wrapper_descriptor) const {
+  HeapObjectHeader* header =
+      HeapObjectHeader::FromPayload(wrapper_descriptor.base_object_payload);
   if (header->IsWrapperHeaderMarked())
     return;
   MarkWrapperHeader(header);
diff --git a/third_party/WebKit/Source/platform/bindings/ScriptWrappableMarkingVisitor.h b/third_party/WebKit/Source/platform/bindings/ScriptWrappableMarkingVisitor.h
index fe437fc6..27fda44d 100644
--- a/third_party/WebKit/Source/platform/bindings/ScriptWrappableMarkingVisitor.h
+++ b/third_party/WebKit/Source/platform/bindings/ScriptWrappableMarkingVisitor.h
@@ -66,7 +66,8 @@
       return;
 
     // If the wrapper is already marked we can bail out here.
-    if (TraceTrait<T>::GetHeapObjectHeader(dst_object)->IsWrapperHeaderMarked())
+    if (TraceTrait<T>::GetHeapObjectHeader(const_cast<T*>(dst_object))
+            ->IsWrapperHeaderMarked())
       return;
 
     CurrentVisitor(thread_state->GetIsolate())
@@ -99,7 +100,7 @@
  protected:
   // ScriptWrappableVisitor interface.
   void Visit(const TraceWrapperV8Reference<v8::Value>&) const override;
-  void Visit(const WrapperDescriptor&) const override;
+  void Visit(const TraceWrapperDescriptor&) const override;
   void Visit(DOMWrapperMap<ScriptWrappable>*,
              const ScriptWrappable* key) const override;
 
@@ -108,20 +109,18 @@
  private:
   class MarkingDequeItem {
    public:
-    explicit MarkingDequeItem(const WrapperDescriptor& wrapper_descriptor)
-        : trace_wrappers_callback_(wrapper_descriptor.trace_wrappers_callback),
-          heap_object_header_callback_(
-              wrapper_descriptor.heap_object_header_callback),
-          raw_object_pointer_(wrapper_descriptor.traceable) {
-      DCHECK(trace_wrappers_callback_);
-      DCHECK(heap_object_header_callback_);
+    explicit MarkingDequeItem(const TraceWrapperDescriptor& wrapper_descriptor)
+        : raw_object_pointer_(wrapper_descriptor.base_object_payload),
+          trace_wrappers_callback_(wrapper_descriptor.trace_wrappers_callback) {
       DCHECK(raw_object_pointer_);
+      DCHECK(trace_wrappers_callback_);
     }
 
     // Traces wrappers if the underlying object has not yet been invalidated.
     inline void TraceWrappers(ScriptWrappableVisitor* visitor) const {
       if (raw_object_pointer_) {
-        trace_wrappers_callback_(visitor, raw_object_pointer_);
+        trace_wrappers_callback_(visitor,
+                                 const_cast<void*>(raw_object_pointer_));
       }
     }
 
@@ -139,13 +138,11 @@
 
    private:
     inline const HeapObjectHeader* GetHeapObjectHeader() {
-      DCHECK(raw_object_pointer_);
-      return heap_object_header_callback_(raw_object_pointer_);
+      return HeapObjectHeader::FromPayload(raw_object_pointer_);
     }
 
-    TraceWrappersCallback trace_wrappers_callback_;
-    HeapObjectHeaderCallback heap_object_header_callback_;
     const void* raw_object_pointer_;
+    TraceWrappersCallback trace_wrappers_callback_;
   };
 
   void MarkWrapperHeader(HeapObjectHeader*) const;
diff --git a/third_party/WebKit/Source/platform/bindings/ScriptWrappableVisitor.h b/third_party/WebKit/Source/platform/bindings/ScriptWrappableVisitor.h
index 6335b21..2ae0239 100644
--- a/third_party/WebKit/Source/platform/bindings/ScriptWrappableVisitor.h
+++ b/third_party/WebKit/Source/platform/bindings/ScriptWrappableVisitor.h
@@ -17,7 +17,6 @@
 
 template <typename T>
 class DOMWrapperMap;
-class HeapObjectHeader;
 class ScriptWrappable;
 class ScriptWrappableVisitor;
 template <typename T>
@@ -27,32 +26,16 @@
 template <typename T>
 class TraceWrapperV8Reference;
 
-using HeapObjectHeaderCallback = HeapObjectHeader* (*)(const void*);
-using MissedWriteBarrierCallback = void (*)();
-using TraceWrappersCallback = void (*)(const ScriptWrappableVisitor*,
-                                       const void* self);
-using NameCallback = const char* (*)(const void* self);
-
-#define DEFINE_TRAIT_FOR_TRACE_WRAPPERS(ClassName)                   \
-  template <>                                                        \
-  inline void TraceTrait<ClassName>::TraceMarkedWrapper(             \
-      const ScriptWrappableVisitor* visitor, const void* t) {        \
-    const ClassName* traceable = ToWrapperTracingType(t);            \
-    traceable->TraceWrappers(visitor);                               \
+#define DEFINE_TRAIT_FOR_TRACE_WRAPPERS(ClassName)                            \
+  template <>                                                                 \
+  inline void TraceTrait<ClassName>::TraceWrappers(                           \
+      ScriptWrappableVisitor* visitor, void* t) {                             \
+    static_assert(sizeof(ClassName), "type needs to be defined");             \
+    static_assert(IsGarbageCollectedType<ClassName>::value,                   \
+                  "only objects deriving from GarbageCollected can be used"); \
+    static_cast<ClassName*>(t)->TraceWrappers(visitor);                       \
   }
 
-// WrapperDescriptor contains enough information to visit a
-// ScriptWrappable without knowing its type statically.
-// It is passed to ScriptWrappableVisitor::Visit method.
-struct WrapperDescriptor {
-  STACK_ALLOCATED();
-  const void* traceable;
-  TraceWrappersCallback trace_wrappers_callback;
-  HeapObjectHeaderCallback heap_object_header_callback;
-  MissedWriteBarrierCallback missed_write_barrier_callback;
-  NameCallback name_callback;
-};
-
 // Abstract visitor for wrapper references in a ScriptWrappable.
 // Usage:
 // - Define a derived class that overrides Visit(..) methods.
@@ -62,6 +45,17 @@
 // wrapper references in traceable.
 class PLATFORM_EXPORT ScriptWrappableVisitor {
  public:
+  template <typename T>
+  static NOINLINE void MissedWriteBarrier() {
+    NOTREACHED();
+  }
+
+  template <typename T>
+  static const char* NameCallback(const void* traceable) {
+    // Mixns never inherit from TraceWrapperBase.
+    return NameInHeapSnapshot(static_cast<const T*>(traceable));
+  }
+
   // Trace all wrappers of |tracable|.
   //
   // If you cannot use TraceWrapperMember & the corresponding TraceWrappers()
@@ -119,24 +113,16 @@
   // The visitor interface. Derived visitors should override this
   // function to visit V8 references and ScriptWrappables.
   virtual void Visit(const TraceWrapperV8Reference<v8::Value>&) const = 0;
-  virtual void Visit(const WrapperDescriptor&) const = 0;
+  virtual void Visit(const TraceWrapperDescriptor&) const = 0;
   virtual void Visit(DOMWrapperMap<ScriptWrappable>*,
                      const ScriptWrappable* key) const = 0;
 
   template <typename T>
-  static WrapperDescriptor WrapperDescriptorFor(const T* traceable) {
-    return {traceable, TraceTrait<T>::TraceMarkedWrapper,
-            TraceTrait<T>::GetHeapObjectHeader,
-            ScriptWrappableVisitor::MissedWriteBarrier<T>,
-            ScriptWrappableVisitor::NameCallback<T>};
+  static TraceWrapperDescriptor WrapperDescriptorFor(const T* traceable) {
+    return TraceTrait<T>::GetTraceWrapperDescriptor(const_cast<T*>(traceable));
   }
 
  private:
-  template <typename T>
-  static NOINLINE void MissedWriteBarrier() {
-    NOTREACHED();
-  }
-
   static const char* NameInHeapSnapshot(const TraceWrapperBase* traceable) {
     return traceable->NameInHeapSnapshot();
   }
@@ -146,11 +132,6 @@
     return "InternalNode";
   }
 
-  template <typename T>
-  static const char* NameCallback(const void* traceable) {
-    return NameInHeapSnapshot(static_cast<const T*>(traceable));
-  }
-
   // Helper method to invoke the virtual Visit method with wrapper descriptor.
   template <typename T>
   void Visit(const T* traceable) const {
diff --git a/third_party/WebKit/Source/platform/bindings/ScriptWrappableVisitorVerifier.h b/third_party/WebKit/Source/platform/bindings/ScriptWrappableVisitorVerifier.h
index 5d198a41..eac22b8 100644
--- a/third_party/WebKit/Source/platform/bindings/ScriptWrappableVisitorVerifier.h
+++ b/third_party/WebKit/Source/platform/bindings/ScriptWrappableVisitorVerifier.h
@@ -15,10 +15,9 @@
 class ScriptWrappableVisitorVerifier final : public ScriptWrappableVisitor {
  protected:
   void Visit(const TraceWrapperV8Reference<v8::Value>&) const final {}
-  void Visit(const WrapperDescriptor& wrapper_descriptor) const final {
-    HeapObjectHeader* header = wrapper_descriptor.heap_object_header_callback(
-        wrapper_descriptor.traceable);
-    if (!header->IsWrapperHeaderMarked()) {
+  void Visit(const TraceWrapperDescriptor& descriptor) const final {
+    if (!HeapObjectHeader::FromPayload(descriptor.base_object_payload)
+             ->IsWrapperHeaderMarked()) {
       // If this branch is hit, it means that a white (not discovered by
       // traceWrappers) object was assigned as a member to a black object
       // (already processed by traceWrappers). Black object will not be
@@ -29,7 +28,7 @@
       // This means there is a write barrier missing somewhere. Check the
       // backtrace to see which types are causing this and review all the
       // places where white object is set to a black object.
-      wrapper_descriptor.missed_write_barrier_callback();
+      descriptor.missed_write_barrier_callback();
       NOTREACHED();
     }
   }
diff --git a/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp b/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
index 4b4395d3a..d9180771 100644
--- a/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
@@ -104,10 +104,6 @@
   RuntimeEnabledFeatures::SetCompositorTouchActionEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnableCompositorImageAnimations(bool enable) {
-  RuntimeEnabledFeatures::SetCompositorImageAnimationsEnabled(enable);
-}
-
 void WebRuntimeFeatures::EnableCSSHexAlphaColor(bool enable) {
   RuntimeEnabledFeatures::SetCSSHexAlphaColorEnabled(enable);
 }
diff --git a/third_party/WebKit/Source/platform/fonts/FontPlatformData.cpp b/third_party/WebKit/Source/platform/fonts/FontPlatformData.cpp
index d5ad9fa..6601940 100644
--- a/third_party/WebKit/Source/platform/fonts/FontPlatformData.cpp
+++ b/third_party/WebKit/Source/platform/fonts/FontPlatformData.cpp
@@ -149,10 +149,13 @@
   auto system_style = QuerySystemRenderStyle(
       family_, text_size_, paint_typeface_.ToSkTypeface()->fontStyle());
 
-  // In layout tests ignore system preference for subpixel positioning as it may
-  // be toggled by the tests.
+  // In layout tests, ignore system preference for subpixel positioning,
+  // or explicitly disable if requested.
   if (LayoutTestSupport::IsRunningLayoutTest()) {
-    system_style.use_subpixel_positioning = WebFontRenderStyle::kNoPreference;
+    system_style.use_subpixel_positioning =
+        LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest()
+            ? WebFontRenderStyle::kNoPreference
+            : 0;
   }
 
   style_.OverrideWith(system_style);
diff --git a/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm b/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm
index f6efeed..262a7a15c 100644
--- a/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm
+++ b/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm
@@ -85,9 +85,7 @@
 }
 
 static bool UseHinting() {
-  // Enable hinting when subpixel font scaling is disabled or
-  // when running the set of standard non-subpixel layout tests,
-  // otherwise use subpixel glyph positioning.
+  // Enable hinting only when antialiasing is disabled in layout tests.
   return (LayoutTestSupport::IsRunningLayoutTest() &&
           !LayoutTestSupport::IsFontAntialiasingEnabledForTest());
 }
diff --git a/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm b/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm
index 39c3657..e98e3385 100644
--- a/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm
+++ b/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm
@@ -117,6 +117,7 @@
                                       const Font* font) const {
   bool should_smooth_fonts = true;
   bool should_antialias = true;
+  bool should_subpixel_position = true;
 
   if (font) {
     switch (font->GetFontDescription().FontSmoothing()) {
@@ -140,6 +141,8 @@
     should_smooth_fonts = false;
     should_antialias = should_antialias &&
                        LayoutTestSupport::IsFontAntialiasingEnabledForTest();
+    should_subpixel_position =
+        LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest();
   }
 
   paint_font->SetAntiAlias(should_antialias);
@@ -150,7 +153,7 @@
   paint_font->SetFakeBoldText(synthetic_bold_);
   paint_font->SetTextSkewX(synthetic_italic_ ? -SK_Scalar1 / 4 : 0);
   paint_font->SetLcdRenderText(should_smooth_fonts);
-  paint_font->SetSubpixelText(true);
+  paint_font->SetSubpixelText(should_subpixel_position);
 
   // When rendering using CoreGraphics, disable hinting when
   // webkit-font-smoothing:antialiased or text-rendering:geometricPrecision is
diff --git a/third_party/WebKit/Source/platform/fonts/win/FontPlatformDataWin.cpp b/third_party/WebKit/Source/platform/fonts/win/FontPlatformDataWin.cpp
index f4d9c72e..dc5d268 100644
--- a/third_party/WebKit/Source/platform/fonts/win/FontPlatformDataWin.cpp
+++ b/third_party/WebKit/Source/platform/fonts/win/FontPlatformDataWin.cpp
@@ -72,6 +72,10 @@
   if (text_flags & SkPaint::kAntiAlias_Flag)
     flags |= SkPaint::kSubpixelText_Flag;
 
+  if (LayoutTestSupport::IsRunningLayoutTest() &&
+      !LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest())
+    flags &= ~SkPaint::kSubpixelText_Flag;
+
   SkASSERT(!(text_flags & ~kTextFlagsMask));
   flags |= text_flags;
 
diff --git a/third_party/WebKit/Source/platform/geometry/FloatRect.cpp b/third_party/WebKit/Source/platform/geometry/FloatRect.cpp
index c380c2d7..2e2e4b6 100644
--- a/third_party/WebKit/Source/platform/geometry/FloatRect.cpp
+++ b/third_party/WebKit/Source/platform/geometry/FloatRect.cpp
@@ -219,18 +219,9 @@
   return result;
 }
 
-IntRect EnclosingIntRect(const FloatRect& rect) {
-  // Compute the enclosing rect using float types directly rather than
-  // FlooredIntPoint(...) et.c to avoid triggering integer overflows.
-  FloatPoint location(floorf(rect.X()), floorf(rect.Y()));
-  FloatPoint max_point(ceilf(rect.MaxX()), ceilf(rect.MaxY()));
-  FloatRect enclosing_rect(location, max_point - location);
-  return IntRect(enclosing_rect);
-}
-
 IntRect EnclosedIntRect(const FloatRect& rect) {
   // Compute the enclosed rect using float types directly rather than
-  // FlooredIntPoint(...) et.c to avoid triggering integer overflows.
+  // FlooredIntPoint(...) etc. to avoid triggering integer overflows.
   FloatPoint location(ceilf(rect.X()), ceilf(rect.Y()));
   FloatPoint max_point(floorf(rect.MaxX()), floorf(rect.MaxY()));
   FloatSize size = max_point - location;
diff --git a/third_party/WebKit/Source/platform/geometry/FloatRect.h b/third_party/WebKit/Source/platform/geometry/FloatRect.h
index ec4734b9..054e82d 100644
--- a/third_party/WebKit/Source/platform/geometry/FloatRect.h
+++ b/third_party/WebKit/Source/platform/geometry/FloatRect.h
@@ -238,7 +238,15 @@
 }
 
 // Returns a IntRect containing the given FloatRect.
-PLATFORM_EXPORT IntRect EnclosingIntRect(const FloatRect&);
+inline IntRect EnclosingIntRect(const FloatRect& rect) {
+  // Compute the enclosing rect using float types directly rather than
+  // FlooredIntPoint(...) etc. to avoid triggering integer overflows.
+  FloatPoint location(floorf(rect.X()), floorf(rect.Y()));
+  FloatPoint max_point(ceilf(rect.MaxX()), ceilf(rect.MaxY()));
+  FloatSize size = max_point - location;
+  return IntRect(clampTo<int>(location.X()), clampTo<int>(location.Y()),
+                 clampTo<int>(size.Width()), clampTo<int>(size.Height()));
+}
 
 // Returns a valid IntRect contained within the given FloatRect.
 PLATFORM_EXPORT IntRect EnclosedIntRect(const FloatRect&);
diff --git a/third_party/WebKit/Source/platform/graphics/AcceleratedStaticBitmapImage.cpp b/third_party/WebKit/Source/platform/graphics/AcceleratedStaticBitmapImage.cpp
index 31fa82e..ddac600 100644
--- a/third_party/WebKit/Source/platform/graphics/AcceleratedStaticBitmapImage.cpp
+++ b/third_party/WebKit/Source/platform/graphics/AcceleratedStaticBitmapImage.cpp
@@ -272,9 +272,8 @@
   detach_thread_at_next_check_ = true;
 }
 
-bool AcceleratedStaticBitmapImage::CurrentFrameKnownToBeOpaque(
-    MetadataMode metadata_mode) {
-  return texture_holder_->CurrentFrameKnownToBeOpaque(metadata_mode);
+bool AcceleratedStaticBitmapImage::CurrentFrameKnownToBeOpaque() {
+  return texture_holder_->CurrentFrameKnownToBeOpaque();
 }
 
 void AcceleratedStaticBitmapImage::CheckThread() {
diff --git a/third_party/WebKit/Source/platform/graphics/AcceleratedStaticBitmapImage.h b/third_party/WebKit/Source/platform/graphics/AcceleratedStaticBitmapImage.h
index 6cd2775..cd058b5f 100644
--- a/third_party/WebKit/Source/platform/graphics/AcceleratedStaticBitmapImage.h
+++ b/third_party/WebKit/Source/platform/graphics/AcceleratedStaticBitmapImage.h
@@ -38,7 +38,7 @@
       base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
       IntSize mailbox_size);
 
-  bool CurrentFrameKnownToBeOpaque(MetadataMode = kUseCurrentMetadata) override;
+  bool CurrentFrameKnownToBeOpaque() override;
   IntSize Size() const override;
   bool IsTextureBacked() const override { return true; }
   scoped_refptr<StaticBitmapImage> MakeAccelerated(
diff --git a/third_party/WebKit/Source/platform/graphics/BitmapImage.cpp b/third_party/WebKit/Source/platform/graphics/BitmapImage.cpp
index c0c4b14a..bf939808 100644
--- a/third_party/WebKit/Source/platform/graphics/BitmapImage.cpp
+++ b/third_party/WebKit/Source/platform/graphics/BitmapImage.cpp
@@ -28,7 +28,6 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/time/default_tick_clock.h"
 #include "platform/Timer.h"
 #include "platform/geometry/FloatRect.h"
 #include "platform/graphics/BitmapImageMetrics.h"
@@ -70,27 +69,16 @@
 
 BitmapImage::BitmapImage(ImageObserver* observer, bool is_multipart)
     : Image(observer, is_multipart),
-      current_frame_index_(0),
-      cached_frame_index_(0),
       animation_policy_(kImageAnimationPolicyAllowed),
-      animation_finished_(false),
       all_data_received_(false),
       have_size_(false),
       size_available_(false),
       have_frame_count_(false),
       repetition_count_status_(kUnknown),
       repetition_count_(kAnimationNone),
-      repetitions_complete_(0),
-      frame_count_(0),
-      clock_(base::DefaultTickClock::GetInstance()),
-      task_runner_(Platform::Current()
-                       ->CurrentThread()
-                       ->Scheduler()
-                       ->CompositorTaskRunner()),
-      weak_factory_(this) {}
+      frame_count_(0) {}
 
 BitmapImage::~BitmapImage() {
-  StopAnimation();
 }
 
 bool BitmapImage::CurrentFrameHasSingleSecurityOrigin() const {
@@ -99,8 +87,6 @@
 
 void BitmapImage::DestroyDecodedData() {
   cached_frame_ = PaintImage();
-  for (size_t i = 0; i < frames_.size(); ++i)
-    frames_[i].Clear(true);
   if (decoder_)
     decoder_->ClearCacheExceptFrame(kNotFound);
   NotifyMemoryChanged();
@@ -116,66 +102,34 @@
 }
 
 size_t BitmapImage::TotalFrameBytes() {
-  size_t total_bytes = 0;
-  for (size_t i = 0; i < frames_.size(); ++i)
-    total_bytes += frames_[i].frame_bytes_;
-  return total_bytes;
+  if (cached_frame_)
+    return Size().Area() * sizeof(ImageFrame::PixelData);
+  return 0u;
 }
 
-PaintImage BitmapImage::CreateAndCacheFrame(size_t index) {
+PaintImage BitmapImage::PaintImageForTesting(size_t frame_index) {
+  return CreatePaintImage(frame_index);
+}
+
+PaintImage BitmapImage::CreatePaintImage(size_t index) {
   sk_sp<PaintImageGenerator> generator =
       decoder_ ? decoder_->CreateGenerator(index) : nullptr;
   if (!generator)
     return PaintImage();
 
-  size_t num_frames = FrameCount();
-  if (frames_.size() < num_frames)
-    frames_.Grow(num_frames);
-
-  frames_[index].orientation_ = decoder_->OrientationAtIndex(index);
-  frames_[index].have_metadata_ = true;
-  frames_[index].is_complete_ = decoder_->FrameIsReceivedAtIndex(index);
-  if (RepetitionCount() != kAnimationNone)
-    frames_[index].duration_ = decoder_->FrameDurationAtIndex(index);
-  frames_[index].has_alpha_ = decoder_->FrameHasAlphaAtIndex(index);
-  frames_[index].frame_bytes_ =
-      decoder_->Size().Area() * sizeof(ImageFrame::PixelData);
-
   auto completion_state = all_data_received_
                               ? PaintImage::CompletionState::DONE
                               : PaintImage::CompletionState::PARTIALLY_DONE;
-
-  // When requesting more than a single loop, repetition count is one less than
-  // the actual number of loops requested.
-  // TODO(khushalsagar): Fix this in RepetitionCount itself when removing code
-  // here for animations.
-  int repetition_count = RepetitionCount();
-  if (repetition_count > 0)
-    repetition_count++;
-
   auto builder =
       CreatePaintImageBuilder()
           .set_paint_image_generator(std::move(generator))
           .set_frame_index(index)
           .set_repetition_count(GetRepetitionCountWithPolicyOverride(
-              repetition_count, animation_policy_))
+              RepetitionCount(), animation_policy_))
           .set_completion_state(completion_state)
           .set_reset_animation_sequence_id(reset_animation_sequence_id_);
 
-  // We are caching frame snapshots.  This is OK even for partially decoded
-  // frames, as they are cleared by dataChanged() when new data arrives.
-  cached_frame_ = builder.TakePaintImage();
-  cached_frame_index_ = index;
-
-  // Create the SkImage backing for this PaintImage here to ensure that copies
-  // of the PaintImage share the same SkImage. Skia's caching of the decoded
-  // output of this image is tied to the lifetime of the SkImage. So we create
-  // the SkImage here and cache the PaintImage to keep the decode alive in
-  // skia's cache.
-  cached_frame_.GetSkImage();
-
-  NotifyMemoryChanged();
-  return cached_frame_;
+  return builder.TakePaintImage();
 }
 
 void BitmapImage::UpdateSize() const {
@@ -232,59 +186,15 @@
 Image::SizeAvailability BitmapImage::DataChanged(bool all_data_received) {
   TRACE_EVENT0("blink", "BitmapImage::dataChanged");
 
-  // Clear all partially-decoded frames. For most image formats, there is only
-  // one frame, but at least GIF and ICO can have more. With GIFs, the frames
-  // come in order and we ask to decode them in order, waiting to request a
-  // subsequent frame until the prior one is complete. Given that we clear
-  // incomplete frames here, this means there is at most one incomplete frame
-  // (even if we use destroyDecodedData() -- since it doesn't reset the
-  // metadata), and it is after all the complete frames.
-  //
-  // With ICOs, on the other hand, we may ask for arbitrary frames at
-  // different times (e.g. because we're displaying a higher-resolution image
-  // in the content area and using a lower-resolution one for the favicon),
-  // and the frames aren't even guaranteed to appear in the file in the same
-  // order as in the directory, so an arbitrary number of the frames might be
-  // incomplete (if we ask for frames for which we've not yet reached the
-  // start of the frame data), and any or none of them might be the particular
-  // frame affected by appending new data here. Thus we have to clear all the
-  // incomplete frames to be safe.
-  for (size_t i = 0; i < frames_.size(); ++i) {
-    // NOTE: Don't call frameIsCompleteAtIndex() here, that will try to
-    // decode any uncached (i.e. never-decoded or
-    // cleared-on-a-previous-pass) frames!
-    if (frames_[i].have_metadata_ && !frames_[i].is_complete_) {
-      frames_[i].Clear(true);
-      if (i == cached_frame_index_)
-        cached_frame_ = PaintImage();
-    }
-  }
-
-  // If the image is being animated by the compositor, clear the cached_frame_
-  // on a data update to push it to the compositor. Since we never advance the
-  // animation here, the |cached_frame_index_| is always the first frame and the
-  // |cached_frame_| might have not have been cleared in the loop above.
-  if (RuntimeEnabledFeatures::CompositorImageAnimationsEnabled()
-      && MaybeAnimated())
-    cached_frame_ = PaintImage();
+  // If the data was updated, clear the |cached_frame_| to push it to the
+  // compositor thread. Its necessary to clear the frame since more data
+  // requires a new PaintImageGenerator instance.
+  cached_frame_ = PaintImage();
 
   // Feed all the data we've seen so far to the image decoder.
   all_data_received_ = all_data_received;
-
   have_frame_count_ = false;
 
-  // Reset the cached image if the metadata has changed.
-  if (cached_frame_) {
-    PaintImage::CompletionState new_completion_state =
-        all_data_received_ ? PaintImage::CompletionState::DONE
-                           : PaintImage::CompletionState::PARTIALLY_DONE;
-    const bool metadata_changed =
-        cached_frame_.repetition_count() != RepetitionCount() ||
-        cached_frame_.completion_state() != new_completion_state;
-    if (metadata_changed)
-      cached_frame_ = PaintImage();
-  }
-
   return IsSizeAvailable() ? kSizeAvailable : kSizeUnavailable;
 }
 
@@ -325,7 +235,7 @@
 
   ImageOrientation orientation = kDefaultImageOrientation;
   if (should_respect_image_orientation == kRespectImageOrientation)
-    orientation = FrameOrientationAtIndex(current_frame_index_);
+    orientation = CurrentFrameOrientation();
 
   PaintCanvasAutoRestore auto_restore(canvas, false);
   FloatRect adjusted_dst_rect = dst_rect;
@@ -389,40 +299,26 @@
   return size_available_;
 }
 
-PaintImage BitmapImage::FrameAtIndex(size_t index) {
-  if (index >= FrameCount())
-    return PaintImage();
-
-  if (index == cached_frame_index_ && cached_frame_)
+PaintImage BitmapImage::PaintImageForCurrentFrame() {
+  if (cached_frame_)
     return cached_frame_;
 
-  return CreateAndCacheFrame(index);
-}
+  cached_frame_ = CreatePaintImage(PaintImage::kDefaultFrameIndex);
 
-bool BitmapImage::FrameIsReceivedAtIndex(size_t index) const {
-  if (index < frames_.size() && frames_[index].have_metadata_ &&
-      frames_[index].is_complete_)
-    return true;
+  // Create the SkImage backing for this PaintImage here to ensure that copies
+  // of the PaintImage share the same SkImage. Skia's caching of the decoded
+  // output of this image is tied to the lifetime of the SkImage. So we create
+  // the SkImage here and cache the PaintImage to keep the decode alive in
+  // skia's cache.
+  cached_frame_.GetSkImage();
+  NotifyMemoryChanged();
 
-  return decoder_ && decoder_->FrameIsReceivedAtIndex(index);
-}
-
-TimeDelta BitmapImage::FrameDurationAtIndex(size_t index) const {
-  if (index < frames_.size() && frames_[index].have_metadata_)
-    return frames_[index].duration_;
-
-  if (!decoder_)
-    return TimeDelta();
-  return decoder_->FrameDurationAtIndex(index);
-}
-
-PaintImage BitmapImage::PaintImageForCurrentFrame() {
-  return FrameAtIndex(current_frame_index_);
+  return cached_frame_;
 }
 
 scoped_refptr<Image> BitmapImage::ImageForDefaultFrame() {
   if (FrameCount() > 1) {
-    PaintImage paint_image = FrameAtIndex(PaintImage::kDefaultFrameIndex);
+    PaintImage paint_image = PaintImageForCurrentFrame();
     if (!paint_image)
       return nullptr;
 
@@ -440,35 +336,27 @@
   return Image::ImageForDefaultFrame();
 }
 
-bool BitmapImage::FrameHasAlphaAtIndex(size_t index) {
-  if (frames_.size() <= index)
-    return true;
-
-  if (frames_[index].have_metadata_ && !frames_[index].has_alpha_)
+bool BitmapImage::CurrentFrameKnownToBeOpaque() {
+  // If the image is animated, it is being animated by the compositor and we
+  // don't know what the current frame is.
+  // TODO(khushalsagar): We could say the image is opaque if none of the frames
+  // have alpha.
+  if (MaybeAnimated())
     return false;
 
-  // has_alpha may change after have_metadata_ is set to true, so always ask
-  // |decoder_| for the value if the cached value is the default value.
-  bool has_alpha = !decoder_ || decoder_->FrameHasAlphaAtIndex(index);
-
-  if (frames_[index].have_metadata_)
-    frames_[index].has_alpha_ = has_alpha;
-
-  return has_alpha;
-}
-
-bool BitmapImage::CurrentFrameKnownToBeOpaque(MetadataMode metadata_mode) {
-  if (metadata_mode == kPreCacheMetadata) {
-    // frameHasAlphaAtIndex() conservatively returns false for uncached frames.
-    // To increase the chance of an accurate answer, pre-cache the current frame
-    // metadata.
-    FrameAtIndex(current_frame_index_);
-  }
-  return !FrameHasAlphaAtIndex(current_frame_index_);
+  // We ask the decoder whether the image has alpha because in some cases the
+  // the correct value is known after decoding. The DeferredImageDecoder caches
+  // the accurate value from the decoded result.
+  const bool frame_has_alpha =
+      decoder_ ? decoder_->FrameHasAlphaAtIndex(PaintImage::kDefaultFrameIndex)
+               : true;
+  return !frame_has_alpha;
 }
 
 bool BitmapImage::CurrentFrameIsComplete() {
-  return FrameIsReceivedAtIndex(current_frame_index_);
+  return decoder_
+             ? decoder_->FrameIsReceivedAtIndex(PaintImage::kDefaultFrameIndex)
+             : false;
 }
 
 bool BitmapImage::CurrentFrameIsLazyDecoded() {
@@ -477,17 +365,8 @@
 }
 
 ImageOrientation BitmapImage::CurrentFrameOrientation() {
-  return FrameOrientationAtIndex(current_frame_index_);
-}
-
-ImageOrientation BitmapImage::FrameOrientationAtIndex(size_t index) {
-  if (!decoder_ || frames_.size() <= index)
-    return kDefaultImageOrientation;
-
-  if (frames_[index].have_metadata_)
-    return frames_[index].orientation_;
-
-  return decoder_->OrientationAtIndex(index);
+  return decoder_ ? decoder_->OrientationAtIndex(PaintImage::kDefaultFrameIndex)
+                  : kDefaultImageOrientation;
 }
 
 int BitmapImage::RepetitionCount() {
@@ -498,6 +377,12 @@
     // decoder will default to cAnimationLoopOnce, and we'll try and read
     // the count again once the whole image is decoded.
     repetition_count_ = decoder_ ? decoder_->RepetitionCount() : kAnimationNone;
+
+    // When requesting more than a single loop, repetition count is one less
+    // than the actual number of loops.
+    if (repetition_count_ > 0)
+      repetition_count_++;
+
     repetition_count_status_ =
         (all_data_received_ || repetition_count_ == kAnimationNone)
             ? kCertain
@@ -506,239 +391,18 @@
   return repetition_count_;
 }
 
-bool BitmapImage::ShouldAnimate() {
-  if (RuntimeEnabledFeatures::CompositorImageAnimationsEnabled())
-    return false;
-
-  bool animated = RepetitionCount() != kAnimationNone && !animation_finished_ &&
-                  GetImageObserver();
-  if (animated && animation_policy_ == kImageAnimationPolicyNoAnimation)
-    animated = false;
-  return animated;
-}
-
-void BitmapImage::StartAnimation() {
-  last_num_frames_skipped_ = StartAnimationInternal(clock_->NowTicks());
-  if (!last_num_frames_skipped_.has_value())
-    return;
-
-  UMA_HISTOGRAM_COUNTS_100000("AnimatedImage.NumOfFramesSkipped.Main",
-                              last_num_frames_skipped_.value());
-}
-
-Optional<size_t> BitmapImage::StartAnimationInternal(TimeTicks time) {
-  // If the |frame_timer_| is set, it indicates that a task is already pending
-  // to advance the current frame of the animation. We don't need to schedule
-  // a task to advance the animation in that case.
-  if (frame_timer_ || !ShouldAnimate() || FrameCount() <= 1)
-    return WTF::nullopt;
-
-  // If we aren't already animating, set now as the animation start time.
-  if (desired_frame_start_time_.is_null())
-    desired_frame_start_time_ = time;
-
-  // Don't advance the animation to an incomplete frame.
-  size_t next_frame = (current_frame_index_ + 1) % FrameCount();
-  if (!FrameIsReceivedAtIndex(next_frame))
-    return WTF::nullopt;
-
-  // Don't advance past the last frame if we haven't decoded the whole image
-  // yet and our repetition count is potentially unset.  The repetition count
-  // in a GIF can potentially come after all the rest of the image data, so
-  // wait on it.
-  if (!all_data_received_ &&
-      (RepetitionCount() == kAnimationLoopOnce ||
-       animation_policy_ == kImageAnimationPolicyAnimateOnce) &&
-      current_frame_index_ >= (FrameCount() - 1))
-    return WTF::nullopt;
-
-  // Determine time for next frame to start.  By ignoring paint and timer lag
-  // in this calculation, we make the animation appear to run at its desired
-  // rate regardless of how fast it's being repainted.
-  TimeDelta current_duration = FrameDurationAtIndex(current_frame_index_);
-  desired_frame_start_time_ += current_duration;
-
-  // When an animated image is more than five minutes out of date, the
-  // user probably doesn't care about resyncing and we could burn a lot of
-  // time looping through frames below.  Just reset the timings.
-  constexpr TimeDelta kCAnimationResyncCutoff = TimeDelta::FromMinutes(5);
-  if ((time - desired_frame_start_time_) > kCAnimationResyncCutoff)
-    desired_frame_start_time_ = time + current_duration;
-
-  // The image may load more slowly than it's supposed to animate, so that by
-  // the time we reach the end of the first repetition, we're well behind.
-  // Clamp the desired frame start time in this case, so that we don't skip
-  // frames (or whole iterations) trying to "catch up".  This is a tradeoff:
-  // It guarantees users see the whole animation the second time through and
-  // don't miss any repetitions, and is closer to what other browsers do; on
-  // the other hand, it makes animations "less accurate" for pages that try to
-  // sync an image and some other resource (e.g. audio), especially if users
-  // switch tabs (and thus stop drawing the animation, which will pause it)
-  // during that initial loop, then switch back later.
-  if (next_frame == 0 && repetitions_complete_ == 0 &&
-      desired_frame_start_time_ < time)
-    desired_frame_start_time_ = time;
-
-  if (time < desired_frame_start_time_) {
-    // Haven't yet reached time for next frame to start; delay until then
-    frame_timer_ = WTF::WrapUnique(new TaskRunnerTimer<BitmapImage>(
-        task_runner_, this, &BitmapImage::AdvanceAnimation));
-    frame_timer_->StartOneShot(
-        std::max(desired_frame_start_time_ - time, TimeDelta()), FROM_HERE);
-
-    // No frames needed to be skipped to advance to the next frame.
-    return Optional<size_t>(0u);
-  }
-
-  // We've already reached or passed the time for the next frame to start.
-  // See if we've also passed the time for frames after that to start, in
-  // case we need to skip some frames entirely.  Remember not to advance
-  // to an incomplete frame.
-  // Note that |desired_frame_start_time_| is always set to the time at which
-  // |next_frame| should be displayed.
-  size_t frames_advanced = 0u;
-  for (; FrameIsReceivedAtIndex(next_frame);
-       next_frame = (current_frame_index_ + 1) % FrameCount()) {
-    // Should we skip the next frame?
-    // TODO(vmpstr): This function can probably deal in TimeTicks/TimeDelta
-    // instead.
-    if (time < desired_frame_start_time_)
-      break;
-
-    // Skip the next frame by advancing the animation forward one frame.
-    if (!InternalAdvanceAnimation(kSkipFramesToCatchUp)) {
-      DCHECK(animation_finished_);
-
-      // No frames skipped, we simply marked the animation as finished on the
-      // first attempt to advance it.
-      if (frames_advanced == 0u)
-        return WTF::nullopt;
-
-      // Don't include the |current_frame_index_|, the last frame we will be
-      // painting when finishing this animation, in the number of frames
-      // skipped.
-      return Optional<size_t>(frames_advanced - 1);
-    }
-
-    DCHECK_EQ(current_frame_index_, next_frame);
-    frames_advanced++;
-    desired_frame_start_time_ += FrameDurationAtIndex(current_frame_index_);
-  }
-
-  DCHECK_GT(frames_advanced, 0u);
-
-  // Since we just advanced a bunch of frames during catch up, post a
-  // notification to the observers. Note this has to be async because the
-  // animation can happen during painting and this invalidation is required
-  // after the current paint.
-  task_runner_->PostTask(
-      FROM_HERE, WTF::Bind(&BitmapImage::NotifyObserversOfAnimationAdvance,
-                           weak_factory_.GetWeakPtr(), nullptr));
-
-  // Reset the |desired_frame_start_time_| to the time for starting the
-  // |current_frame_index_|. Whenever StartAnimationInternal decides to schedule
-  // the task for the next frame (which may not happen in the call below), it
-  // always updates the |desired_frame_start_time_| based on the current frame
-  // duration.
-  desired_frame_start_time_ -= FrameDurationAtIndex(current_frame_index_);
-
-  // Don't include the |current_frame_index_|, which will be used on the next
-  // paint, in the number of frames skipped.
-  return Optional<size_t>(frames_advanced - 1);
-}
-
-void BitmapImage::StopAnimation() {
-  // This timer is used to animate all occurrences of this image.  Don't
-  // invalidate the timer unless all renderers have stopped drawing.
-  frame_timer_.reset();
-}
-
 void BitmapImage::ResetAnimation() {
-  StopAnimation();
-  current_frame_index_ = 0;
-  repetitions_complete_ = 0;
-  desired_frame_start_time_ = TimeTicks();
-  animation_finished_ = false;
   cached_frame_ = PaintImage();
   reset_animation_sequence_id_++;
 }
 
 bool BitmapImage::MaybeAnimated() {
-  if (animation_finished_)
-    return false;
   if (FrameCount() > 1)
     return true;
 
   return decoder_ && decoder_->RepetitionCount() != kAnimationNone;
 }
 
-void BitmapImage::AdvanceTime(TimeDelta delta) {
-  if (!desired_frame_start_time_.is_null())
-    desired_frame_start_time_ -= delta;
-  else
-    desired_frame_start_time_ = clock_->NowTicks() - delta;
-}
-
-void BitmapImage::AdvanceAnimation(TimerBase*) {
-  InternalAdvanceAnimation();
-  // At this point the image region has been marked dirty, and if it's
-  // onscreen, we'll soon make a call to draw(), which will call
-  // startAnimation() again to keep the animation moving.
-}
-
-bool BitmapImage::InternalAdvanceAnimation(AnimationAdvancement advancement) {
-  // Stop the animation.
-  StopAnimation();
-
-  if (!GetImageObserver())
-    return false;
-
-  // See if anyone is still paying attention to this animation.  If not, we
-  // don't advance, and will remain suspended at the current frame until the
-  // animation is resumed.
-  if (advancement != kSkipFramesToCatchUp &&
-      GetImageObserver()->ShouldPauseAnimation(this))
-    return false;
-
-  if (current_frame_index_ + 1 < FrameCount()) {
-    current_frame_index_++;
-  } else {
-    repetitions_complete_++;
-
-    // We don't need to special-case cAnimationLoopOnce here because it is
-    // 0 (see comments on its declaration in ImageAnimation.h).
-    if ((RepetitionCount() != kAnimationLoopInfinite &&
-         repetitions_complete_ > repetition_count_) ||
-        animation_policy_ == kImageAnimationPolicyAnimateOnce) {
-      animation_finished_ = true;
-      desired_frame_start_time_ = TimeTicks();
-
-      // We skipped to the last frame and cannot advance further. The
-      // observer will not receive animationAdvanced notifications while
-      // skipping but we still need to notify the observer to draw the
-      // last frame. Skipping frames occurs while painting so we do not
-      // synchronously notify the observer which could cause a layout.
-      if (advancement == kSkipFramesToCatchUp) {
-        frame_timer_ = WTF::WrapUnique(new TaskRunnerTimer<BitmapImage>(
-            task_runner_, this,
-            &BitmapImage::NotifyObserversOfAnimationAdvance));
-        frame_timer_->StartOneShot(TimeDelta(), FROM_HERE);
-      }
-
-      return false;
-    }
-
-    // Loop the animation back to the first frame.
-    current_frame_index_ = 0;
-  }
-
-  // We need to draw this frame if we advanced to it while not skipping.
-  if (advancement != kSkipFramesToCatchUp)
-    GetImageObserver()->AnimationAdvanced(this);
-
-  return true;
-}
-
 void BitmapImage::SetAnimationPolicy(ImageAnimationPolicy policy) {
   if (animation_policy_ == policy)
     return;
@@ -747,11 +411,6 @@
   ResetAnimation();
 }
 
-void BitmapImage::NotifyObserversOfAnimationAdvance(TimerBase*) {
-  if (GetImageObserver())
-    GetImageObserver()->AnimationAdvanced(this);
-}
-
 STATIC_ASSERT_ENUM(WebSettings::kImageAnimationPolicyAllowed,
                    kImageAnimationPolicyAllowed);
 STATIC_ASSERT_ENUM(WebSettings::kImageAnimationPolicyAnimateOnce,
diff --git a/third_party/WebKit/Source/platform/graphics/BitmapImage.h b/third_party/WebKit/Source/platform/graphics/BitmapImage.h
index df35612..bd0a583 100644
--- a/third_party/WebKit/Source/platform/graphics/BitmapImage.h
+++ b/third_party/WebKit/Source/platform/graphics/BitmapImage.h
@@ -34,7 +34,6 @@
 #include "platform/geometry/IntSize.h"
 #include "platform/graphics/Color.h"
 #include "platform/graphics/DeferredImageDecoder.h"
-#include "platform/graphics/FrameData.h"
 #include "platform/graphics/Image.h"
 #include "platform/graphics/ImageAnimationPolicy.h"
 #include "platform/graphics/ImageOrientation.h"
@@ -44,10 +43,6 @@
 #include "platform/wtf/Time.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 
-namespace base {
-class TickClock;
-}
-
 namespace blink {
 
 class PLATFORM_EXPORT BitmapImage final : public Image {
@@ -86,39 +81,34 @@
 
   void SetAnimationPolicy(ImageAnimationPolicy) override;
   ImageAnimationPolicy AnimationPolicy() override { return animation_policy_; }
-  void AdvanceTime(TimeDelta) override;
 
   scoped_refptr<Image> ImageForDefaultFrame() override;
 
-  bool CurrentFrameKnownToBeOpaque(MetadataMode = kUseCurrentMetadata) override;
+  // TODO(khushalsagar): These names are bogus, we don't know what the current
+  // frame is.
+  bool CurrentFrameKnownToBeOpaque() override;
   bool CurrentFrameIsComplete() override;
   bool CurrentFrameIsLazyDecoded() override;
   size_t FrameCount() override;
-
+  PaintImage PaintImageForCurrentFrame() override;
   ImageOrientation CurrentFrameOrientation();
 
-  // Advance the image animation by one frame.
-  void AdvanceAnimationForTesting() override { InternalAdvanceAnimation(); }
-
-  PaintImage PaintImageForCurrentFrame() override;
-
+  PaintImage PaintImageForTesting(size_t frame_index);
+  void AdvanceAnimationForTesting() override {
+    NOTREACHED() << "Supported only with svgs";
+  }
   void SetDecoderForTesting(std::unique_ptr<DeferredImageDecoder> decoder) {
     decoder_ = std::move(decoder);
   }
-  void SetTaskRunnerForTesting(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-    task_runner_ = task_runner;
-  }
-
-  Optional<size_t> last_num_frames_skipped_for_testing() const {
-    return last_num_frames_skipped_;
-  }
-
-  void SetTickClockForTesting(base::TickClock* clock) { clock_ = clock; }
 
  protected:
   bool IsSizeAvailable() override;
 
+  // TODO(khushalsagar): This is only used by MemoryCache to evict images based
+  // on whether they are caching decoded data. Since the decodes are already
+  // in unlocked discardable in cc/skia, this is unnecessary.
+  size_t TotalFrameBytes();
+
  private:
   enum RepetitionCountStatus : uint8_t {
     kUnknown,    // We haven't checked the source's repetition count.
@@ -139,20 +129,9 @@
             ImageClampingMode,
             ImageDecodingMode) override;
 
-  PaintImage FrameAtIndex(size_t);
-
-  bool FrameIsReceivedAtIndex(size_t) const;
-  TimeDelta FrameDurationAtIndex(size_t) const;
-  bool FrameHasAlphaAtIndex(size_t);
-  ImageOrientation FrameOrientationAtIndex(size_t);
-
-  PaintImage CreateAndCacheFrame(size_t index);
+  PaintImage CreatePaintImage(size_t index);
   void UpdateSize() const;
 
-  // Returns the total number of bytes allocated for all framebuffers, i.e.
-  // the sum of m_source.frameBytesAtIndex(...) for all frames.
-  size_t TotalFrameBytes();
-
   // Called to wipe out the entire frame buffer cache and tell the image
   // source to destroy everything; this is used when e.g. we want to free
   // some room in the image cache.
@@ -163,54 +142,21 @@
   // Notifies observers that the memory footprint has changed.
   void NotifyMemoryChanged();
 
-  // Animation.
-  // We start and stop animating lazily.  Animation starts when the image is
-  // rendered, and automatically stops once no observer wants to render the
-  // image.
-
   int RepetitionCount();
 
-  bool ShouldAnimate();
-  void StartAnimation() override;
-  // Starts the animation by scheduling a task to advance to the next desired
-  // frame, if possible, and catching up any frames if the time to display them
-  // is in the past.
-  Optional<size_t> StartAnimationInternal(TimeTicks);
-  void StopAnimation();
-  void AdvanceAnimation(TimerBase*);
-
-  // This function does the real work of advancing the animation. When
-  // skipping frames to catch up, we're in the middle of a loop trying to skip
-  // over a bunch of animation frames, so we should not do things like decode
-  // each one or notify our observers.
-  // Returns whether the animation was advanced.
-  enum AnimationAdvancement { kNormal, kSkipFramesToCatchUp };
-  bool InternalAdvanceAnimation(AnimationAdvancement = kNormal);
-
-  void NotifyObserversOfAnimationAdvance(TimerBase*);
-
   std::unique_ptr<DeferredImageDecoder> decoder_;
   mutable IntSize size_;  // The size to use for the overall image (will just
                           // be the size of the first image).
   mutable IntSize size_respecting_orientation_;
 
-  size_t current_frame_index_;   // The index of the current frame of animation.
-  Vector<FrameData, 1> frames_;  // An array of the cached frames of the
-                                 // animation. We have to ref frames to pin
-                                 // them in the cache.
-
-  PaintImage
-      cached_frame_;  // A cached copy of the most recently-accessed frame.
-  size_t cached_frame_index_;  // Index of the frame that is cached.
-
-  std::unique_ptr<TaskRunnerTimer<BitmapImage>> frame_timer_;
+  // This caches the PaintImage created with the last updated encoded data to
+  // ensure re-use of generated decodes. This is cleared each time the encoded
+  // data is updated in DataChanged.
+  PaintImage cached_frame_;
 
   ImageAnimationPolicy
       animation_policy_;  // Whether or not we can play animation.
 
-  bool animation_finished_ : 1;  // Whether we've completed the entire
-                                 // animation.
-
   bool all_data_received_ : 1;  // Whether we've received all our data.
   mutable bool have_size_ : 1;  // Whether our |m_size| member variable has the
                                 // final overall image size yet.
@@ -218,28 +164,16 @@
                                 // image frame from ImageIO yet.
   bool have_frame_count_ : 1;
 
+  bool default_frame_has_alpha_ : 1;
+
   RepetitionCountStatus repetition_count_status_;
   int repetition_count_;  // How many total animation loops we should do.  This
                           // will be cAnimationNone if this image type is
                           // incapable of animation.
-  int repetitions_complete_;  // How many repetitions we've finished.
-
-  TimeTicks desired_frame_start_time_;  // The system time at which we hope to
-                                        // see the next call to
-                                        // startAnimation().
 
   size_t frame_count_;
 
   PaintImage::AnimationSequenceId reset_animation_sequence_id_ = 0;
-
-  base::TickClock* clock_;
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
-  // Value used in UMA tracking for the number of animation frames skipped
-  // during catch-up.
-  Optional<size_t> last_num_frames_skipped_ = 0u;
-
-  base::WeakPtrFactory<BitmapImage> weak_factory_;
 };
 
 DEFINE_IMAGE_TYPE_CASTS(BitmapImage);
diff --git a/third_party/WebKit/Source/platform/graphics/BitmapImageTest.cpp b/third_party/WebKit/Source/platform/graphics/BitmapImageTest.cpp
index a507316f6..d6800df2 100644
--- a/third_party/WebKit/Source/platform/graphics/BitmapImageTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/BitmapImageTest.cpp
@@ -87,35 +87,17 @@
   // Accessors to BitmapImage's protected methods.
   void DestroyDecodedData() { image_->DestroyDecodedData(); }
   size_t FrameCount() { return image_->FrameCount(); }
-  void FrameAtIndex(size_t index) { image_->FrameAtIndex(index); }
-  void SetCurrentFrame(size_t frame) { image_->current_frame_index_ = frame; }
-  size_t FrameDecodedSize(size_t frame) {
-    return image_->frames_[frame].frame_bytes_;
-  }
-  size_t DecodedFramesCount() const { return image_->frames_.size(); }
-  void StartAnimation() { image_->StartAnimation(); }
 
-  void SetFirstFrameNotComplete() { image_->frames_[0].is_complete_ = false; }
-
-  void LoadImage(const char* file_name, bool load_all_frames = true) {
+  void LoadImage(const char* file_name) {
     scoped_refptr<SharedBuffer> image_data = ReadFile(file_name);
     ASSERT_TRUE(image_data.get());
 
     image_->SetData(image_data, true);
-    EXPECT_EQ(0u, DecodedSize());
-
-    size_t frame_count = image_->FrameCount();
-    if (load_all_frames) {
-      for (size_t i = 0; i < frame_count; ++i)
-        FrameAtIndex(i);
-    }
   }
 
   SkBitmap GenerateBitmap(size_t frame_index) {
     CHECK_GE(image_->FrameCount(), frame_index);
-    for (size_t i = 0; i < frame_index; ++i)
-      AdvanceAnimation();
-    auto paint_image = image_->PaintImageForCurrentFrame();
+    auto paint_image = image_->PaintImageForTesting(frame_index);
     CHECK(paint_image);
     CHECK_EQ(paint_image.frame_index(), frame_index);
 
@@ -188,27 +170,10 @@
     }
   }
 
-  size_t DecodedSize() {
-    // In the context of this test, the following loop will give the correct
-    // result, but only because the test forces all frames to be decoded in
-    // loadImage() above. There is no general guarantee that frameDecodedSize()
-    // is up to date. Because of how multi frame images (like GIF) work,
-    // requesting one frame to be decoded may require other previous frames to
-    // be decoded as well. In those cases frameDecodedSize() wouldn't return the
-    // correct thing for the previous frame because the decoded size wouldn't
-    // have propagated upwards to the BitmapImage frame cache.
-    size_t size = 0;
-    for (size_t i = 0; i < DecodedFramesCount(); ++i)
-      size += FrameDecodedSize(i);
-    return size;
-  }
-
-  void AdvanceAnimation() { image_->AdvanceAnimation(nullptr); }
+  size_t DecodedSize() { return image_->TotalFrameBytes(); }
 
   int RepetitionCount() { return image_->RepetitionCount(); }
 
-  int AnimationFinished() { return image_->animation_finished_; }
-
   scoped_refptr<Image> ImageForDefaultFrame() {
     return image_->ImageForDefaultFrame();
   }
@@ -233,6 +198,7 @@
 
 TEST_F(BitmapImageTest, destroyDecodedData) {
   LoadImage("/LayoutTests/images/resources/animated-10color.gif");
+  image_->PaintImageForCurrentFrame();
   size_t total_size = DecodedSize();
   EXPECT_GT(total_size, 0u);
   DestroyDecodedData();
@@ -242,26 +208,7 @@
 
 TEST_F(BitmapImageTest, maybeAnimated) {
   LoadImage("/LayoutTests/images/resources/gif-loop-count.gif");
-  for (size_t i = 0; i < FrameCount(); ++i) {
-    EXPECT_TRUE(image_->MaybeAnimated());
-    AdvanceAnimation();
-  }
-  EXPECT_FALSE(image_->MaybeAnimated());
-}
-
-TEST_F(BitmapImageTest, animationRepetitions) {
-  LoadImage("/LayoutTests/images/resources/full2loop.gif");
-  int expected_repetition_count = 2;
-  EXPECT_EQ(expected_repetition_count, RepetitionCount());
-
-  // We actually loop once more than stored repetition count.
-  for (int repeat = 0; repeat < expected_repetition_count + 1; ++repeat) {
-    for (size_t i = 0; i < FrameCount(); ++i) {
-      EXPECT_FALSE(AnimationFinished());
-      AdvanceAnimation();
-    }
-  }
-  EXPECT_TRUE(AnimationFinished());
+  EXPECT_TRUE(image_->MaybeAnimated());
 }
 
 TEST_F(BitmapImageTest, isAllDataReceived) {
@@ -287,14 +234,14 @@
 
 TEST_F(BitmapImageTest, noColorProfile) {
   LoadImage("/LayoutTests/images/resources/green.jpg");
-  EXPECT_EQ(1u, DecodedFramesCount());
+  image_->PaintImageForCurrentFrame();
   EXPECT_EQ(1024u, DecodedSize());
   EXPECT_FALSE(image_->HasColorProfile());
 }
 
 TEST_F(BitmapImageTest, jpegHasColorProfile) {
   LoadImage("/LayoutTests/images/resources/icc-v2-gbr.jpg");
-  EXPECT_EQ(1u, DecodedFramesCount());
+  image_->PaintImageForCurrentFrame();
   EXPECT_EQ(227700u, DecodedSize());
   EXPECT_TRUE(image_->HasColorProfile());
 }
@@ -303,14 +250,14 @@
   LoadImage(
       "/LayoutTests/images/resources/"
       "palatted-color-png-gamma-one-color-profile.png");
-  EXPECT_EQ(1u, DecodedFramesCount());
+  image_->PaintImageForCurrentFrame();
   EXPECT_EQ(65536u, DecodedSize());
   EXPECT_TRUE(image_->HasColorProfile());
 }
 
 TEST_F(BitmapImageTest, webpHasColorProfile) {
   LoadImage("/LayoutTests/images/resources/webp-color-profile-lossy.webp");
-  EXPECT_EQ(1u, DecodedFramesCount());
+  image_->PaintImageForCurrentFrame();
   EXPECT_EQ(2560000u, DecodedSize());
   EXPECT_TRUE(image_->HasColorProfile());
 }
@@ -323,8 +270,8 @@
 
 TEST_F(BitmapImageTest, correctDecodedDataSize) {
   // Requesting any one frame shouldn't result in decoding any other frames.
-  LoadImage("/LayoutTests/images/resources/anim_none.gif", false);
-  FrameAtIndex(1);
+  LoadImage("/LayoutTests/images/resources/anim_none.gif");
+  image_->PaintImageForCurrentFrame();
   int frame_size =
       static_cast<int>(image_->Size().Area() * sizeof(ImageFrame::PixelData));
   EXPECT_EQ(frame_size, LastDecodedSizeChange());
@@ -332,7 +279,7 @@
 
 TEST_F(BitmapImageTest, recachingFrameAfterDataChanged) {
   LoadImage("/LayoutTests/images/resources/green.jpg");
-  SetFirstFrameNotComplete();
+  image_->PaintImageForCurrentFrame();
   EXPECT_GT(LastDecodedSizeChange(), 0);
   image_observer_->last_decoded_size_changed_delta_ = 0;
 
@@ -407,7 +354,7 @@
 }
 
 TEST_F(BitmapImageTest, ImageForDefaultFrame_MultiFrame) {
-  LoadImage("/LayoutTests/images/resources/anim_none.gif", false);
+  LoadImage("/LayoutTests/images/resources/anim_none.gif");
 
   // Multi-frame images create new StaticBitmapImages for each call.
   auto default_image1 = image_->ImageForDefaultFrame();
@@ -555,7 +502,6 @@
  public:
   void SetUp() override {
     BitmapImageTest::SetUp();
-    image_->SetTickClockForTesting(&clock_);
 
     auto decoder = MockImageDecoder::Create(this);
     decoder->SetSize(10, 10);
@@ -574,17 +520,11 @@
   int RepetitionCount() const override { return repetition_count_; }
   TimeDelta FrameDuration() const override { return duration_; }
 
-  void SetClock(int seconds) {
-    clock_.SetNowTicks(base::TimeTicks() + TimeDelta::FromSeconds(seconds));
-  }
-
  protected:
   TimeDelta duration_;
   int repetition_count_;
   size_t frame_count_;
   bool last_frame_complete_;
-
-  base::SimpleTestTickClock clock_;
 };
 
 TEST_F(BitmapImageTestWithMockDecoder, ImageMetadataTracking) {
@@ -656,141 +596,7 @@
   EXPECT_EQ(image.repetition_count(), repetition_count_);
 }
 
-TEST_F(BitmapImageTestWithMockDecoder, DontAdvanceToIncompleteFrame) {
-  ScopedCompositorImageAnimationsForTest compositor_image_animations(false);
-
-  repetition_count_ = kAnimationLoopOnce;
-  frame_count_ = 3u;
-  last_frame_complete_ = false;
-  duration_ = TimeDelta::FromSeconds(10);
-  SetClock(10);
-
-  // Last frame of the image is incomplete for a completely loaded image. We
-  // still won't advance it.
-  image_->SetData(SharedBuffer::Create("data", sizeof("data")), true);
-
-  scoped_refptr<scheduler::FakeTaskRunner> task_runner =
-      base::MakeRefCounted<scheduler::FakeTaskRunner>();
-  image_->SetTaskRunnerForTesting(task_runner);
-  task_runner->SetTime(10);
-
-  // Start the animation at 10s. This should schedule a timer for the next frame
-  // after 10 seconds.
-  StartAnimation();
-  EXPECT_EQ(image_->PaintImageForCurrentFrame().frame_index(), 0u);
-
-  // Run the timer task, image advanced to the second frame.
-  SetClock(20);
-  task_runner->AdvanceTimeAndRun(10);
-  EXPECT_EQ(image_->PaintImageForCurrentFrame().frame_index(), 1u);
-
-  // Go past the desired time for the next frame, call StartAnimation. The frame
-  // still isn't advanced because its incomplete.
-  SetClock(45);
-  StartAnimation();
-  EXPECT_EQ(image_->PaintImageForCurrentFrame().frame_index(), 1u);
-}
-
-TEST_F(BitmapImageTestWithMockDecoder, FrameSkipTracking) {
-  ScopedCompositorImageAnimationsForTest compositor_image_animations(false);
-
-  repetition_count_ = kAnimationLoopOnce;
-  frame_count_ = 7u;
-  last_frame_complete_ = false;
-  duration_ = TimeDelta::FromSeconds(10);
-  SetClock(10);
-
-  // Start with an image that is incomplete, and the last frame is not fully
-  // received.
-  image_->SetData(SharedBuffer::Create("data", sizeof("data")), false);
-
-  scoped_refptr<scheduler::FakeTaskRunner> task_runner =
-      base::MakeRefCounted<scheduler::FakeTaskRunner>();
-  image_->SetTaskRunnerForTesting(task_runner);
-  task_runner->SetTime(10);
-
-  // Start the animation at 10s. This should schedule a timer for the next frame
-  // after 10 seconds.
-  EXPECT_FALSE(image_observer_->animation_advanced_);
-  StartAnimation();
-  EXPECT_EQ(image_->PaintImageForCurrentFrame().frame_index(), 0u);
-
-  // No frames skipped since we just started the animation.
-  EXPECT_EQ(image_->last_num_frames_skipped_for_testing().value(), 0u);
-
-  // Advance the time to 15s. The frame is still at 0u because the posted task
-  // should run at 20s.
-  image_observer_->animation_advanced_ = false;
-  task_runner->AdvanceTimeAndRun(5);
-  EXPECT_EQ(image_->PaintImageForCurrentFrame().frame_index(), 0u);
-  EXPECT_FALSE(image_observer_->animation_advanced_);
-
-  // Advance the time to 20s. The task runs and advances the animation forward
-  // to 1u.
-  task_runner->AdvanceTimeAndRun(5);
-  EXPECT_TRUE(image_observer_->animation_advanced_);
-
-  // If we were to paint now, we should use the second frame.
-  EXPECT_EQ(image_->PaintImageForCurrentFrame().frame_index(), 1u);
-
-  // Now assume painting the next frame was delayed to 41 seconds. We should
-  // first draw with the |current_frame_index_| and then call StartAnimation to
-  // advance the frame.
-  // Since the animation started at 10s, and each frame has a duration of 10s,
-  // we should see the fourth frame at 41 seconds.
-  SetClock(41);
-  task_runner->SetTime(41);
-  StartAnimation();
-
-  // Since we just skipped frames while advancing this animation, we should see
-  // a notification to dirty immediately.
-  image_observer_->animation_advanced_ = false;
-  task_runner->AdvanceTimeAndRun(0);
-  EXPECT_TRUE(image_observer_->animation_advanced_);
-
-  // On the next draw, we should see the fourth frame.
-  EXPECT_EQ(image_->PaintImageForCurrentFrame().frame_index(), 3u);
-  EXPECT_EQ(image_->last_num_frames_skipped_for_testing().value(), 1u);
-
-  // At 70s, we would want to display the last frame and would skip 2 frames.
-  // But because its incomplete, we advanced to the sixth frame and only skipped
-  // 1 frame.
-  SetClock(71);
-  task_runner->SetTime(71);
-  StartAnimation();
-  EXPECT_EQ(image_->PaintImageForCurrentFrame().frame_index(), 5u);
-  EXPECT_EQ(image_->last_num_frames_skipped_for_testing().value(), 1u);
-
-  // We should still see a notification to move to the sixth frame.
-  image_observer_->animation_advanced_ = false;
-  task_runner->AdvanceTimeAndRun(0);
-  EXPECT_TRUE(image_observer_->animation_advanced_);
-
-  // Run any pending tasks and try to animate again. Can't advance the animation
-  // because the last frame is not complete.
-  task_runner->AdvanceTimeAndRun(0);
-  StartAnimation();
-  EXPECT_EQ(image_->PaintImageForCurrentFrame().frame_index(), 5u);
-  EXPECT_FALSE(image_->last_num_frames_skipped_for_testing().has_value());
-
-  // Finish the load and kick the animation again. It finishes during catch up.
-  // But no frame skipped because we just advanced to the last frame.
-  last_frame_complete_ = true;
-  image_->SetData(SharedBuffer::Create("data", sizeof("data")), true);
-
-  StartAnimation();
-  EXPECT_EQ(image_->PaintImageForCurrentFrame().frame_index(), 6u);
-  EXPECT_EQ(image_->last_num_frames_skipped_for_testing().value(), 0u);
-
-  // Finishing the animation always notifies observers async.
-  image_observer_->animation_advanced_ = false;
-  task_runner->AdvanceTimeAndRun(0);
-  EXPECT_TRUE(image_observer_->animation_advanced_);
-}
-
 TEST_F(BitmapImageTestWithMockDecoder, ResetAnimation) {
-  ScopedCompositorImageAnimationsForTest compositor_image_animations(false);
-
   repetition_count_ = kAnimationLoopInfinite;
   frame_count_ = 4u;
   last_frame_complete_ = true;
@@ -804,8 +610,6 @@
 }
 
 TEST_F(BitmapImageTestWithMockDecoder, PaintImageForStaticBitmapImage) {
-  ScopedCompositorImageAnimationsForTest compositor_image_animations(false);
-
   repetition_count_ = kAnimationLoopInfinite;
   frame_count_ = 5;
   last_frame_complete_ = true;
diff --git a/third_party/WebKit/Source/platform/graphics/FrameData.cpp b/third_party/WebKit/Source/platform/graphics/FrameData.cpp
deleted file mode 100644
index be5b6b0..0000000
--- a/third_party/WebKit/Source/platform/graphics/FrameData.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
- * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "platform/graphics/FrameData.h"
-#include "third_party/skia/include/core/SkImage.h"
-
-namespace blink {
-
-FrameData::FrameData()
-    : orientation_(kDefaultImageOrientation),
-      have_metadata_(false),
-      is_complete_(false),
-      has_alpha_(true),
-      frame_bytes_(0) {}
-
-FrameData::~FrameData() {
-  Clear(true);
-}
-
-void FrameData::Clear(bool clear_metadata) {
-  if (clear_metadata)
-    have_metadata_ = false;
-
-  orientation_ = kDefaultImageOrientation;
-  frame_bytes_ = 0;
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/FrameData.h b/third_party/WebKit/Source/platform/graphics/FrameData.h
deleted file mode 100644
index 1018cf9b..0000000
--- a/third_party/WebKit/Source/platform/graphics/FrameData.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
- * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.  All rights reserved.
- * Copyright (C) 2008-2009 Torch Mobile, Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef FrameData_h
-#define FrameData_h
-
-#include "base/memory/scoped_refptr.h"
-#include "platform/graphics/ImageOrientation.h"
-#include "platform/graphics/paint/PaintImage.h"
-#include "platform/wtf/Allocator.h"
-#include "platform/wtf/Noncopyable.h"
-#include "platform/wtf/Time.h"
-#include "platform/wtf/VectorTraits.h"
-
-namespace blink {
-
-struct FrameData {
-  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
-  WTF_MAKE_NONCOPYABLE(FrameData);
-
- public:
-  FrameData();
-  ~FrameData();
-
-  // Clear the cached image data on the frame, and (optionally) the metadata.
-  // Returns whether there was cached image data to clear.
-  void Clear(bool clear_metadata);
-
-  ImageOrientation orientation_;
-  TimeDelta duration_;
-  bool have_metadata_ : 1;
-  bool is_complete_ : 1;
-  bool has_alpha_ : 1;
-  size_t frame_bytes_;
-};
-
-}  // namespace blink
-
-namespace WTF {
-template <>
-struct VectorTraits<blink::FrameData>
-    : public SimpleClassVectorTraits<blink::FrameData> {
-  STATIC_ONLY(VectorTraits);
-  static const bool kCanInitializeWithMemset =
-      false;  // Not all FrameData members initialize to 0.
-};
-}
-
-#endif  // FrameData_h
diff --git a/third_party/WebKit/Source/platform/graphics/GeneratedImage.h b/third_party/WebKit/Source/platform/graphics/GeneratedImage.h
index cb77604e..05d2851a 100644
--- a/third_party/WebKit/Source/platform/graphics/GeneratedImage.h
+++ b/third_party/WebKit/Source/platform/graphics/GeneratedImage.h
@@ -56,10 +56,7 @@
                    const FloatSize& repeat_spacing) final;
 
   // FIXME: Implement this to be less conservative.
-  bool CurrentFrameKnownToBeOpaque(
-      MetadataMode = kUseCurrentMetadata) override {
-    return false;
-  }
+  bool CurrentFrameKnownToBeOpaque() override { return false; }
 
   GeneratedImage(const FloatSize& size) : size_(size) {}
 
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
index 425edb8..9735a3a 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
@@ -1434,7 +1434,7 @@
         *web_display_item_list->GetCcDisplayItemList());
   } else {
     paint_controller.GetPaintArtifact().AppendToWebDisplayItemList(
-        OffsetFromLayoutObjectWithSubpixelAccumulation(),
+        FloatSize(OffsetFromLayoutObjectWithSubpixelAccumulation()),
         web_display_item_list);
   }
 
diff --git a/third_party/WebKit/Source/platform/graphics/Image.h b/third_party/WebKit/Source/platform/graphics/Image.h
index ab3537f6..3a583ab 100644
--- a/third_party/WebKit/Source/platform/graphics/Image.h
+++ b/third_party/WebKit/Source/platform/graphics/Image.h
@@ -83,14 +83,7 @@
   virtual bool IsStaticBitmapImage() const { return false; }
   virtual bool IsPlaceholderImage() const { return false; }
 
-  // To increase accuracy of currentFrameKnownToBeOpaque() it may,
-  // for applicable image types, be told to pre-cache metadata for
-  // the current frame. Since this may initiate a deferred image
-  // decoding, PreCacheMetadata requires a InspectorPaintImageEvent
-  // during call.
-  enum MetadataMode { kUseCurrentMetadata, kPreCacheMetadata };
-  virtual bool CurrentFrameKnownToBeOpaque(
-      MetadataMode = kUseCurrentMetadata) = 0;
+  virtual bool CurrentFrameKnownToBeOpaque() = 0;
 
   virtual bool CurrentFrameIsComplete() { return false; }
   virtual bool CurrentFrameIsLazyDecoded() { return false; }
@@ -153,7 +146,6 @@
   virtual ImageAnimationPolicy AnimationPolicy() {
     return kImageAnimationPolicyAllowed;
   }
-  virtual void AdvanceTime(TimeDelta delta) {}
 
   // Advances an animated image. For BitmapImage (e.g., animated gifs) this
   // will advance to the next frame. For SVGImage, this will trigger an
diff --git a/third_party/WebKit/Source/platform/graphics/ImageLayerChromiumTest.cpp b/third_party/WebKit/Source/platform/graphics/ImageLayerChromiumTest.cpp
index 571bed6..d54f8d6 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageLayerChromiumTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/ImageLayerChromiumTest.cpp
@@ -44,10 +44,7 @@
     return base::AdoptRef(new TestImage(size, opaque));
   }
 
-  bool CurrentFrameKnownToBeOpaque(
-      MetadataMode = kUseCurrentMetadata) override {
-    return image_->isOpaque();
-  }
+  bool CurrentFrameKnownToBeOpaque() override { return image_->isOpaque(); }
 
   IntSize Size() const override { return size_; }
 
diff --git a/third_party/WebKit/Source/platform/graphics/MailboxTextureHolder.h b/third_party/WebKit/Source/platform/graphics/MailboxTextureHolder.h
index 0405ba7..0ea59ed 100644
--- a/third_party/WebKit/Source/platform/graphics/MailboxTextureHolder.h
+++ b/third_party/WebKit/Source/platform/graphics/MailboxTextureHolder.h
@@ -22,7 +22,7 @@
   bool IsSkiaTextureHolder() final { return false; }
   bool IsMailboxTextureHolder() final { return true; }
   IntSize Size() const final { return size_; }
-  bool CurrentFrameKnownToBeOpaque(Image::MetadataMode) final { return false; }
+  bool CurrentFrameKnownToBeOpaque() final { return false; }
   bool IsValid() const final;
 
   const gpu::Mailbox& GetMailbox() const final { return mailbox_; }
diff --git a/third_party/WebKit/Source/platform/graphics/PlaceholderImage.cpp b/third_party/WebKit/Source/platform/graphics/PlaceholderImage.cpp
index c3057d27..d1867d5 100644
--- a/third_party/WebKit/Source/platform/graphics/PlaceholderImage.cpp
+++ b/third_party/WebKit/Source/platform/graphics/PlaceholderImage.cpp
@@ -201,7 +201,7 @@
   return true;
 }
 
-bool PlaceholderImage::CurrentFrameKnownToBeOpaque(MetadataMode) {
+bool PlaceholderImage::CurrentFrameKnownToBeOpaque() {
   // Placeholder images are translucent.
   return false;
 }
diff --git a/third_party/WebKit/Source/platform/graphics/PlaceholderImage.h b/third_party/WebKit/Source/platform/graphics/PlaceholderImage.h
index 71f1b8ea..7a887bf 100644
--- a/third_party/WebKit/Source/platform/graphics/PlaceholderImage.h
+++ b/third_party/WebKit/Source/platform/graphics/PlaceholderImage.h
@@ -62,7 +62,7 @@
 
   bool CurrentFrameHasSingleSecurityOrigin() const override;
 
-  bool CurrentFrameKnownToBeOpaque(MetadataMode) override;
+  bool CurrentFrameKnownToBeOpaque() override;
 
   void DrawPattern(GraphicsContext&,
                    const FloatRect& src_rect,
diff --git a/third_party/WebKit/Source/platform/graphics/SkiaTextureHolder.h b/third_party/WebKit/Source/platform/graphics/SkiaTextureHolder.h
index 95ffdde..0b90889 100644
--- a/third_party/WebKit/Source/platform/graphics/SkiaTextureHolder.h
+++ b/third_party/WebKit/Source/platform/graphics/SkiaTextureHolder.h
@@ -24,9 +24,7 @@
     return IntSize(image_->width(), image_->height());
   }
   bool IsValid() const final;
-  bool CurrentFrameKnownToBeOpaque(Image::MetadataMode) final {
-    return image_->isOpaque();
-  }
+  bool CurrentFrameKnownToBeOpaque() final { return image_->isOpaque(); }
   sk_sp<SkImage> GetSkImage() final { return image_; }
   void Abandon() final;
 
diff --git a/third_party/WebKit/Source/platform/graphics/TextureHolder.h b/third_party/WebKit/Source/platform/graphics/TextureHolder.h
index 7eccb72..a1ac64c 100644
--- a/third_party/WebKit/Source/platform/graphics/TextureHolder.h
+++ b/third_party/WebKit/Source/platform/graphics/TextureHolder.h
@@ -26,7 +26,7 @@
   virtual bool IsSkiaTextureHolder() = 0;
   virtual bool IsMailboxTextureHolder() = 0;
   virtual IntSize Size() const = 0;
-  virtual bool CurrentFrameKnownToBeOpaque(Image::MetadataMode) = 0;
+  virtual bool CurrentFrameKnownToBeOpaque() = 0;
   virtual bool IsValid() const = 0;
 
   // Methods overrided by MailboxTextureHolder
diff --git a/third_party/WebKit/Source/platform/graphics/UnacceleratedStaticBitmapImage.cpp b/third_party/WebKit/Source/platform/graphics/UnacceleratedStaticBitmapImage.cpp
index 5e875011..b22804f 100644
--- a/third_party/WebKit/Source/platform/graphics/UnacceleratedStaticBitmapImage.cpp
+++ b/third_party/WebKit/Source/platform/graphics/UnacceleratedStaticBitmapImage.cpp
@@ -71,7 +71,7 @@
       std::move(gpu_skimage), std::move(context_wrapper));
 }
 
-bool UnacceleratedStaticBitmapImage::CurrentFrameKnownToBeOpaque(MetadataMode) {
+bool UnacceleratedStaticBitmapImage::CurrentFrameKnownToBeOpaque() {
   return paint_image_.GetSkImage()->isOpaque();
 }
 
diff --git a/third_party/WebKit/Source/platform/graphics/UnacceleratedStaticBitmapImage.h b/third_party/WebKit/Source/platform/graphics/UnacceleratedStaticBitmapImage.h
index 4f0cb8b..09422cf 100644
--- a/third_party/WebKit/Source/platform/graphics/UnacceleratedStaticBitmapImage.h
+++ b/third_party/WebKit/Source/platform/graphics/UnacceleratedStaticBitmapImage.h
@@ -17,7 +17,7 @@
   static scoped_refptr<UnacceleratedStaticBitmapImage> Create(sk_sp<SkImage>);
   static scoped_refptr<UnacceleratedStaticBitmapImage> Create(PaintImage);
 
-  bool CurrentFrameKnownToBeOpaque(MetadataMode = kUseCurrentMetadata) override;
+  bool CurrentFrameKnownToBeOpaque() override;
   IntSize Size() const override;
   bool IsPremultiplied() const override;
   scoped_refptr<StaticBitmapImage> MakeAccelerated(
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp
index c591826..110601f 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp
@@ -468,7 +468,7 @@
       if (record && record->size() != 0)
         cc_list_.push<cc::DrawRecordOp>(std::move(record));
       cc_list_.EndPaintOfUnpaired(PaintChunksToCcLayer::MapRectFromChunkToLayer(
-          FloatRect(item.VisualRect()), chunk, layer_state_, layer_offset_));
+          item.VisualRect(), chunk, layer_state_, layer_offset_));
     }
     if (transformed)
       AppendRestore(1);
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ClipDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/ClipDisplayItem.cpp
index c94a2e2..4de3ba24 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ClipDisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/ClipDisplayItem.cpp
@@ -26,7 +26,7 @@
 }
 
 void ClipDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   WebVector<SkRRect> web_rounded_rects(rounded_rect_clips_.size());
   for (size_t i = 0; i < rounded_rect_clips_.size(); ++i)
@@ -40,7 +40,7 @@
 }
 
 void EndClipDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   list->AppendEndClipItem();
 }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ClipDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/ClipDisplayItem.h
index 1c2527d..31c10a9 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ClipDisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/ClipDisplayItem.h
@@ -33,7 +33,7 @@
   }
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
  private:
@@ -60,7 +60,7 @@
   }
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
  private:
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp
index 2ac6077..cce09dc 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp
@@ -17,7 +17,7 @@
 }
 
 void BeginClipPathDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   list->AppendClipPathItem(clip_path_, true);
 }
@@ -27,7 +27,7 @@
 }
 
 void EndClipPathDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   list->AppendEndClipPathItem();
 }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.h
index db9ab85..94a9173 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.h
@@ -21,7 +21,7 @@
         clip_path_(clip_path.GetSkPath()) {}
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
  private:
@@ -44,7 +44,7 @@
       : PairedEndDisplayItem(client, kEndClipPath, sizeof(*this)) {}
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
  private:
diff --git a/third_party/WebKit/Source/platform/graphics/paint/CompositingDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/CompositingDisplayItem.cpp
index 86959a0..7143bc6e 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/CompositingDisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/CompositingDisplayItem.cpp
@@ -16,7 +16,7 @@
 }
 
 void BeginCompositingDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   SkRect bounds = bounds_;
   list->AppendCompositingItem(
@@ -40,7 +40,7 @@
 }
 
 void EndCompositingDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   list->AppendEndCompositingItem();
 }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/CompositingDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/CompositingDisplayItem.h
index fc0bfd3..c46e3f0 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/CompositingDisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/CompositingDisplayItem.h
@@ -30,7 +30,7 @@
   }
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
  private:
@@ -66,7 +66,7 @@
       : PairedEndDisplayItem(client, kEndCompositing, sizeof(*this)) {}
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
  private:
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
index 01a4cf0..6a9eeb78 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
@@ -234,7 +234,7 @@
   json.SetString("id", GetId().ToString());
   json.SetString("visualRect", VisualRect().ToString());
   if (OutsetForRasterEffects())
-    json.SetDouble("outset", OutsetForRasterEffects().ToDouble());
+    json.SetDouble("outset", OutsetForRasterEffects());
   if (skipped_cache_)
     json.SetBoolean("skippedCache", true);
 }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
index 8f4bea091..6be647f 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
@@ -6,6 +6,7 @@
 #define DisplayItem_h
 
 #include "platform/PlatformExport.h"
+#include "platform/geometry/FloatRect.h"
 #include "platform/graphics/ContiguousContainer.h"
 #include "platform/graphics/paint/DisplayItemClient.h"
 #include "platform/runtime_enabled_features.h"
@@ -21,7 +22,7 @@
 namespace blink {
 
 class GraphicsContext;
-class LayoutSize;
+class FloatSize;
 enum class PaintPhase;
 class WebDisplayItemList;
 
@@ -231,16 +232,14 @@
   // This equals to Client().VisualRect() as long as the client is alive and is
   // not invalidated. Otherwise it saves the previous visual rect of the client.
   // See DisplayItemClient::VisualRect() about its coordinate space.
-  const LayoutRect& VisualRect() const { return visual_rect_; }
-  LayoutUnit OutsetForRasterEffects() const {
-    return outset_for_raster_effects_;
-  }
+  const FloatRect& VisualRect() const { return visual_rect_; }
+  float OutsetForRasterEffects() const { return outset_for_raster_effects_; }
 
   // Visual rect can change without needing invalidation of the client, e.g.
   // when ancestor clip changes. This is called from PaintController::
   // UseCachedDrawingIfPossible() to update the visual rect of a cached display
   // item.
-  void UpdateVisualRect() { visual_rect_ = client_->VisualRect(); }
+  void UpdateVisualRect() { visual_rect_ = FloatRect(client_->VisualRect()); }
 
   Type GetType() const { return static_cast<Type>(type_); }
 
@@ -267,7 +266,7 @@
   // |visual_rect_offset| is the offset between the space of the GraphicsLayer
   // which owns the display item and the coordinate space of VisualRect().
   // TODO(wangxianzhu): Remove the parameter for slimming paint v2.
-  virtual void AppendToWebDisplayItemList(const LayoutSize& visual_rect_offset,
+  virtual void AppendToWebDisplayItemList(const FloatSize& visual_rect_offset,
                                           WebDisplayItemList*) const {}
 
 // See comments of enum Type for usage of the following macros.
@@ -385,8 +384,8 @@
   DisplayItem() : is_tombstone_(true) {}
 
   const DisplayItemClient* client_;
-  LayoutRect visual_rect_;
-  LayoutUnit outset_for_raster_effects_;
+  FloatRect visual_rect_;
+  float outset_for_raster_effects_;
 
   static_assert(kTypeLast < (1 << 8), "DisplayItem::Type should fit in 8 bits");
   unsigned type_ : 8;
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
index db54700..0298878 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
@@ -44,9 +44,7 @@
   // The outset will be used to inflate visual rect after the visual rect is
   // mapped into the space of the composited layer, for any special raster
   // effects that might expand the rastered pixel area.
-  virtual LayoutUnit VisualRectOutsetForRasterEffects() const {
-    return LayoutUnit();
-  }
+  virtual float VisualRectOutsetForRasterEffects() const { return 0; }
 
   // The rect that needs to be invalidated partially in this client. It's in the
   // same coordinate space as VisualRect().
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.cpp
index d2c9be17..54a2c71 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.cpp
@@ -19,11 +19,11 @@
 }
 
 void DrawingDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize& visual_rect_offset,
+    const FloatSize& visual_rect_offset,
     WebDisplayItemList* list) const {
   if (record_) {
     // Convert visual rect into the GraphicsLayer's coordinate space.
-    LayoutRect visual_rect = VisualRect();
+    auto visual_rect = VisualRect();
     visual_rect.Move(-visual_rect_offset);
     list->AppendDrawingItem(EnclosingIntRect(visual_rect), record_);
   }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.h
index 1e940ffae..94afa8a 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.h
@@ -34,7 +34,7 @@
                      bool known_to_be_opaque);
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize& visual_rect_offset,
+  void AppendToWebDisplayItemList(const FloatSize& visual_rect_offset,
                                   WebDisplayItemList*) const override;
   bool DrawsContent() const override;
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItemTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItemTest.cpp
index 81bb36e..ae7019b 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItemTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItemTest.cpp
@@ -57,15 +57,15 @@
 
   DrawingDisplayItem item(client_, DisplayItem::Type::kDocumentBackground,
                           CreateRectRecord(record_bounds));
-  EXPECT_EQ(drawing_bounds, item.VisualRect());
+  EXPECT_EQ(FloatRect(drawing_bounds), item.VisualRect());
 
   MockWebDisplayItemList list1;
   WebRect expected_rect = EnclosingIntRect(drawing_bounds);
   EXPECT_CALL(list1, AppendDrawingItem(expected_rect, _)).Times(1);
-  item.AppendToWebDisplayItemList(LayoutSize(), &list1);
+  item.AppendToWebDisplayItemList(FloatSize(), &list1);
 
-  LayoutSize offset(LayoutUnit(2.1), LayoutUnit(3.6));
-  LayoutRect visual_rect_with_offset = drawing_bounds;
+  FloatSize offset(2.1, 3.6);
+  FloatRect visual_rect_with_offset(drawing_bounds);
   visual_rect_with_offset.Move(-offset);
   WebRect expected_visual_rect = EnclosingIntRect(visual_rect_with_offset);
   MockWebDisplayItemList list2;
diff --git a/third_party/WebKit/Source/platform/graphics/paint/FilterDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/FilterDisplayItem.cpp
index 9c79503f..cbab65a 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/FilterDisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/FilterDisplayItem.cpp
@@ -20,7 +20,7 @@
 }
 
 void BeginFilterDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   list->AppendFilterItem(compositor_filter_operations_.AsCcFilterOperations(),
                          bounds_, origin_);
@@ -45,7 +45,7 @@
 }
 
 void EndFilterDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   list->AppendEndFilterItem();
 }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/FilterDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/FilterDisplayItem.h
index a8ade8e..21fa000 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/FilterDisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/FilterDisplayItem.h
@@ -28,7 +28,7 @@
         origin_(origin) {}
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
   bool DrawsContent() const override;
 
@@ -61,7 +61,7 @@
       : PairedEndDisplayItem(client, kEndFilter, sizeof(*this)) {}
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
  private:
diff --git a/third_party/WebKit/Source/platform/graphics/paint/FloatClipDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/FloatClipDisplayItem.cpp
index 54460e0..bf3acf0 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/FloatClipDisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/FloatClipDisplayItem.cpp
@@ -16,7 +16,7 @@
 }
 
 void FloatClipDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   list->AppendFloatClipItem(clip_rect_);
 }
@@ -26,7 +26,7 @@
 }
 
 void EndFloatClipDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   list->AppendEndFloatClipItem();
 }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/FloatClipDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/FloatClipDisplayItem.h
index 3ea61d02..2770f34 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/FloatClipDisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/FloatClipDisplayItem.h
@@ -23,7 +23,7 @@
   }
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
  private:
@@ -48,7 +48,7 @@
   }
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
  private:
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ForeignLayerDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/ForeignLayerDisplayItem.cpp
index b1765588..9f8b10b3 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ForeignLayerDisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/ForeignLayerDisplayItem.cpp
@@ -35,7 +35,7 @@
 }
 
 void ForeignLayerDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList*) const {
   NOTREACHED();
 }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ForeignLayerDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/ForeignLayerDisplayItem.h
index df289a6..e4e1dd9 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ForeignLayerDisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/ForeignLayerDisplayItem.h
@@ -39,7 +39,7 @@
 
   // DisplayItem
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
   bool DrawsContent() const override;
   bool Equals(const DisplayItem&) const override;
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.cpp
index 7ea52116..21ff8516 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.cpp
@@ -24,18 +24,15 @@
     FloatRect bounds;
     SkRegion known_to_be_opaque_region;
     for (const DisplayItem& item : display_items.ItemsInPaintChunk(chunk)) {
-      bounds.Unite(FloatRect(item.Client().VisualRect()));
+      bounds.Unite(item.VisualRect());
       if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled() ||
           !item.IsDrawing())
         continue;
       const auto& drawing = static_cast<const DrawingDisplayItem&>(item);
       if (drawing.GetPaintRecord() && drawing.KnownToBeOpaque()) {
-        // TODO(wkorman): Confirm the visual rect is in the right
-        // space. It looks correct now, and was perhaps incorrect
-        // previously?
-        LayoutRect visual_rect = drawing.VisualRect();
-        known_to_be_opaque_region.op(SkIRect(IntRect(visual_rect)),
-                                     SkRegion::kUnion_Op);
+        known_to_be_opaque_region.op(
+            SkIRect(EnclosedIntRect(drawing.VisualRect())),
+            SkRegion::kUnion_Op);
       }
     }
     chunk.bounds = bounds;
@@ -113,7 +110,7 @@
 
 DISABLE_CFI_PERF
 void PaintArtifact::AppendToWebDisplayItemList(
-    const LayoutSize& visual_rect_offset,
+    const FloatSize& visual_rect_offset,
     WebDisplayItemList* list) const {
   TRACE_EVENT0("blink,benchmark", "PaintArtifact::appendToWebDisplayItemList");
   for (const DisplayItem& item : display_item_list_)
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.h b/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.h
index 5825042..4764082 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.h
@@ -77,7 +77,7 @@
               const IntPoint& offset = IntPoint()) const;
 
   // Writes the paint artifact into a WebDisplayItemList.
-  void AppendToWebDisplayItemList(const LayoutSize& visual_rect_offset,
+  void AppendToWebDisplayItemList(const FloatSize& visual_rect_offset,
                                   WebDisplayItemList*) const;
 
  private:
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintChunkerTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintChunkerTest.cpp
index 99189ff..d4a2a17 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintChunkerTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintChunkerTest.cpp
@@ -40,7 +40,7 @@
       : DisplayItem(client, type, sizeof(*this)) {}
 
   void Replay(GraphicsContext&) const final { NOTREACHED(); }
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const final {
     NOTREACHED();
   }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
index 65805912..d31d8ca 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
@@ -297,7 +297,7 @@
 
     last_chunk.outset_for_raster_effects =
         std::max(last_chunk.outset_for_raster_effects,
-                 display_item.OutsetForRasterEffects().ToFloat());
+                 display_item.OutsetForRasterEffects());
   }
 
 #if DCHECK_IS_ON()
@@ -541,7 +541,8 @@
     // Visual rect change should not happen in a cached subsequence.
     // However, because of different method of pixel snapping in different
     // paths, there are false positives. Just log an error.
-    if (cached_item->VisualRect() != cached_item->Client().VisualRect()) {
+    if (cached_item->VisualRect() !=
+        FloatRect(cached_item->Client().VisualRect())) {
       LOG(ERROR) << "Visual rect changed in a cached subsequence: "
                  << cached_item->Client().DebugName()
                  << " old=" << cached_item->VisualRect().ToString()
@@ -845,7 +846,7 @@
           PaintChunk& moved_to_chunk =
               new_paint_chunks_.FindChunkByDisplayItemIndex(moved_to_index);
           AddRasterInvalidation(new_item.Client(), moved_to_chunk,
-                                FloatRect(new_item.VisualRect()),
+                                new_item.VisualRect(),
                                 PaintInvalidationReason::kAppeared);
           // And invalidate the old visual rect in this chunk.
           client_to_invalidate_old_visual_rect = &new_item.Client();
@@ -894,7 +895,7 @@
     const DisplayItem* new_item) {
   if (!new_item || new_item->VisualRect().IsEmpty()) {
     if (old_item && !old_item->VisualRect().IsEmpty()) {
-      AddRasterInvalidation(client, chunk, FloatRect(old_item->VisualRect()),
+      AddRasterInvalidation(client, chunk, old_item->VisualRect(),
                             PaintInvalidationReason::kDisappeared);
     }
     return;
@@ -902,7 +903,7 @@
 
   DCHECK(&client == &new_item->Client());
   if (!old_item || old_item->VisualRect().IsEmpty()) {
-    AddRasterInvalidation(client, chunk, FloatRect(new_item->VisualRect()),
+    AddRasterInvalidation(client, chunk, new_item->VisualRect(),
                           PaintInvalidationReason::kAppeared);
     return;
   }
@@ -910,9 +911,9 @@
   if (client.IsJustCreated()) {
     // The old client has been deleted and the new client happens to be at the
     // same address. They have no relationship.
-    AddRasterInvalidation(client, chunk, FloatRect(old_item->VisualRect()),
+    AddRasterInvalidation(client, chunk, old_item->VisualRect(),
                           PaintInvalidationReason::kDisappeared);
-    AddRasterInvalidation(client, chunk, FloatRect(new_item->VisualRect()),
+    AddRasterInvalidation(client, chunk, new_item->VisualRect(),
                           PaintInvalidationReason::kAppeared);
     return;
   }
@@ -1025,19 +1026,15 @@
 
 #ifndef NDEBUG
   const PaintRecord* new_record = nullptr;
-  LayoutRect new_bounds;
   if (new_item.IsDrawing()) {
     new_record =
         static_cast<const DrawingDisplayItem&>(new_item).GetPaintRecord().get();
-    new_bounds = static_cast<const DrawingDisplayItem&>(new_item).VisualRect();
   }
   const PaintRecord* old_record = nullptr;
-  LayoutRect old_bounds;
   if (old_item->IsDrawing()) {
     old_record = static_cast<const DrawingDisplayItem*>(old_item)
                      ->GetPaintRecord()
                      .get();
-    old_bounds = static_cast<const DrawingDisplayItem&>(new_item).VisualRect();
   }
   LOG(INFO) << "new record:\n"
             << (new_record ? RecordAsDebugString(*new_record).Utf8().data()
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.h b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.h
index 996ef24..616615a8 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.h
@@ -86,7 +86,7 @@
       : DisplayItem(client, type, sizeof(*this)) {}
 
   void Replay(GraphicsContext&) const final { NOTREACHED(); }
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const final {
     NOTREACHED();
   }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ScrollDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/ScrollDisplayItem.cpp
index 0190a08..3ac6d47d 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ScrollDisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/ScrollDisplayItem.cpp
@@ -16,7 +16,7 @@
 }
 
 void BeginScrollDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   WebDisplayItemList::ScrollContainerId scroll_container_id = &Client();
   list->AppendScrollItem(current_offset_, scroll_container_id);
@@ -34,7 +34,7 @@
 }
 
 void EndScrollDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   list->AppendEndScrollItem();
 }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ScrollDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/ScrollDisplayItem.h
index a8bc15b..62d859cb 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ScrollDisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/ScrollDisplayItem.h
@@ -23,7 +23,7 @@
   }
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
   const IntSize& CurrentOffset() const { return current_offset_; }
@@ -49,7 +49,7 @@
   }
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
  private:
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ScrollHitTestDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/ScrollHitTestDisplayItem.cpp
index 8b04433..19e9ffff 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ScrollHitTestDisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/ScrollHitTestDisplayItem.cpp
@@ -29,7 +29,7 @@
 }
 
 void ScrollHitTestDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList*) const {
   NOTREACHED();
 }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ScrollHitTestDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/ScrollHitTestDisplayItem.h
index 238c45d..21df1cc2 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ScrollHitTestDisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/ScrollHitTestDisplayItem.h
@@ -36,7 +36,7 @@
 
   // DisplayItem
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
   bool Equals(const DisplayItem&) const override;
 #if DCHECK_IS_ON()
diff --git a/third_party/WebKit/Source/platform/graphics/paint/Transform3DDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/Transform3DDisplayItem.cpp
index dfc3467..9eb25af 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/Transform3DDisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/Transform3DDisplayItem.cpp
@@ -18,7 +18,7 @@
 }
 
 void BeginTransform3DDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   // TODO(jbroman): The compositor will need the transform origin separately.
   TransformationMatrix transform(transform_);
@@ -39,7 +39,7 @@
 }
 
 void EndTransform3DDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   list->AppendEndTransformItem();
 }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/Transform3DDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/Transform3DDisplayItem.h
index 649ea769..7899ac48 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/Transform3DDisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/Transform3DDisplayItem.h
@@ -26,7 +26,7 @@
   }
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
   const TransformationMatrix& Transform() const { return transform_; }
@@ -58,7 +58,7 @@
   }
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
  private:
diff --git a/third_party/WebKit/Source/platform/graphics/paint/TransformDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/TransformDisplayItem.cpp
index c55009063..0ce0fead 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/TransformDisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/TransformDisplayItem.cpp
@@ -16,7 +16,7 @@
 }
 
 void BeginTransformDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   list->AppendTransformItem(AffineTransformToSkMatrix(transform_));
 }
@@ -33,7 +33,7 @@
 }
 
 void EndTransformDisplayItem::AppendToWebDisplayItemList(
-    const LayoutSize&,
+    const FloatSize&,
     WebDisplayItemList* list) const {
   list->AppendEndTransformItem();
 }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/TransformDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/TransformDisplayItem.h
index 8e8b464..60eecf6 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/TransformDisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/TransformDisplayItem.h
@@ -19,7 +19,7 @@
         transform_(transform) {}
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
   const AffineTransform& Transform() const { return transform_; }
@@ -44,7 +44,7 @@
       : PairedEndDisplayItem(client, kEndTransform, sizeof(*this)) {}
 
   void Replay(GraphicsContext&) const override;
-  void AppendToWebDisplayItemList(const LayoutSize&,
+  void AppendToWebDisplayItemList(const FloatSize&,
                                   WebDisplayItemList*) const override;
 
  private:
diff --git a/third_party/WebKit/Source/platform/graphics/test/StubImage.h b/third_party/WebKit/Source/platform/graphics/test/StubImage.h
index 62536b0..25c398f 100644
--- a/third_party/WebKit/Source/platform/graphics/test/StubImage.h
+++ b/third_party/WebKit/Source/platform/graphics/test/StubImage.h
@@ -13,7 +13,7 @@
  public:
   StubImage() = default;
 
-  bool CurrentFrameKnownToBeOpaque(MetadataMode) override { return false; }
+  bool CurrentFrameKnownToBeOpaque() override { return false; }
   IntSize Size() const override { return IntSize(10, 10); }
   void DestroyDecodedData() override {}
   PaintImage PaintImageForCurrentFrame() override { return PaintImage(); }
diff --git a/third_party/WebKit/Source/platform/heap/BlinkGC.h b/third_party/WebKit/Source/platform/heap/BlinkGC.h
index cee5c09b3..5f99da29 100644
--- a/third_party/WebKit/Source/platform/heap/BlinkGC.h
+++ b/third_party/WebKit/Source/platform/heap/BlinkGC.h
@@ -16,6 +16,7 @@
 
 class MarkingVisitor;
 class Visitor;
+class ScriptWrappableVisitor;
 
 using Address = uint8_t*;
 
@@ -23,8 +24,11 @@
 using VisitorCallback = void (*)(Visitor*, void*);
 using MarkingVisitorCallback = void (*)(MarkingVisitor*, void*);
 using TraceCallback = VisitorCallback;
+using TraceWrappersCallback = void (*)(ScriptWrappableVisitor*, void*);
 using WeakCallback = VisitorCallback;
 using EphemeronCallback = VisitorCallback;
+using MissedWriteBarrierCallback = void (*)();
+using NameCallback = const char* (*)(const void* self);
 
 // Simple alias to avoid heap compaction type signatures turning into
 // a sea of generic |void*|s.
diff --git a/third_party/WebKit/Source/platform/heap/GarbageCollected.h b/third_party/WebKit/Source/platform/heap/GarbageCollected.h
index 21fcf87..54aabb44 100644
--- a/third_party/WebKit/Source/platform/heap/GarbageCollected.h
+++ b/third_party/WebKit/Source/platform/heap/GarbageCollected.h
@@ -16,7 +16,6 @@
 template <typename T>
 class GarbageCollected;
 class HeapObjectHeader;
-class ScriptWrappableVisitor;
 
 // GC_PLUGIN_IGNORE is used to make the plugin ignore a particular class or
 // field when checking for proper usage.  When using GC_PLUGIN_IGNORE
@@ -54,6 +53,14 @@
   bool can_trace_eagerly;
 };
 
+struct TraceWrapperDescriptor {
+  STACK_ALLOCATED();
+  void* base_object_payload;
+  TraceWrappersCallback trace_wrappers_callback;
+  MissedWriteBarrierCallback missed_write_barrier_callback;
+  NameCallback name_callback;
+};
+
 // The GarbageCollectedMixin interface and helper macro
 // USING_GARBAGE_COLLECTED_MIXIN can be used to automatically define
 // TraceTrait/ObjectAliveTrait on non-leftmost deriving classes
@@ -71,43 +78,41 @@
 // };
 //
 // With the helper, as long as we are using Member<B>, TypeTrait<B> will
-// dispatch adjustAndMark dynamically to find collect addr of the object header.
+// dispatch dynamically to retrieve the necessary tracing and header methods.
 // Note that this is only enabled for Member<B>. For Member<A> which we can
-// compute the object header addr statically, this dynamic dispatch is not used.
-//
+// compute the necessary methods and pointers statically and this dynamic
+// dispatch is not used.
 class PLATFORM_EXPORT GarbageCollectedMixin {
  public:
   typedef int IsGarbageCollectedMixinMarker;
   virtual void Trace(Visitor*) {}
-  virtual TraceDescriptor GetTraceDescriptor() const = 0;
   virtual HeapObjectHeader* GetHeapObjectHeader() const = 0;
-  virtual void AdjustAndTraceMarkedWrapper(
-      const ScriptWrappableVisitor*) const = 0;
+  virtual TraceDescriptor GetTraceDescriptor() const = 0;
+  virtual TraceWrapperDescriptor GetTraceWrapperDescriptor() const = 0;
 };
 
-#define DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(TYPE)                          \
- public:                                                                      \
-  TraceDescriptor GetTraceDescriptor() const override {                       \
-    typedef WTF::IsSubclassOfTemplate<typename std::remove_const<TYPE>::type, \
-                                      blink::GarbageCollected>                \
-        IsSubclassOfGarbageCollected;                                         \
-    static_assert(                                                            \
-        IsSubclassOfGarbageCollected::value,                                  \
-        "only garbage collected objects can have garbage collected mixins");  \
-    return {const_cast<TYPE*>(static_cast<const TYPE*>(this)),                \
-            TraceTrait<TYPE>::Trace, TraceEagerlyTrait<TYPE>::value};         \
-  }                                                                           \
-                                                                              \
-  void AdjustAndTraceMarkedWrapper(const ScriptWrappableVisitor* visitor)     \
-      const override {                                                        \
-    const TYPE* base = static_cast<const TYPE*>(this);                        \
-    TraceTrait<TYPE>::TraceMarkedWrapper(visitor, base);                      \
-  }                                                                           \
-                                                                              \
-  HeapObjectHeader* GetHeapObjectHeader() const override {                    \
-    return HeapObjectHeader::FromPayload(static_cast<const TYPE*>(this));     \
-  }                                                                           \
-                                                                              \
+#define DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(TYPE)                         \
+ public:                                                                     \
+  HeapObjectHeader* GetHeapObjectHeader() const override {                   \
+    static_assert(                                                           \
+        WTF::IsSubclassOfTemplate<typename std::remove_const<TYPE>::type,    \
+                                  blink::GarbageCollected>::value,           \
+        "only garbage collected objects can have garbage collected mixins"); \
+    return HeapObjectHeader::FromPayload(static_cast<const TYPE*>(this));    \
+  }                                                                          \
+                                                                             \
+  TraceDescriptor GetTraceDescriptor() const override {                      \
+    return {const_cast<TYPE*>(static_cast<const TYPE*>(this)),               \
+            TraceTrait<TYPE>::Trace, TraceEagerlyTrait<TYPE>::value};        \
+  }                                                                          \
+                                                                             \
+  TraceWrapperDescriptor GetTraceWrapperDescriptor() const override {        \
+    return {const_cast<TYPE*>(static_cast<const TYPE*>(this)),               \
+            TraceTrait<TYPE>::TraceWrappers,                                 \
+            ScriptWrappableVisitor::MissedWriteBarrier<TYPE>,                \
+            ScriptWrappableVisitor::NameCallback<TYPE>};                     \
+  }                                                                          \
+                                                                             \
  private:
 
 // A C++ object's vptr will be initialized to its leftmost base's vtable after
@@ -212,14 +217,13 @@
 //    // ambiguous. USING_GARBAGE_COLLECTED_MIXIN(TYPE) overrides them later
 //    // and provides the implementations.
 //  };
-#define MERGE_GARBAGE_COLLECTED_MIXINS()                          \
- public:                                                          \
-  TraceDescriptor GetTraceDescriptor() const override = 0;        \
-  HeapObjectHeader* GetHeapObjectHeader() const override = 0;     \
-  void AdjustAndTraceMarkedWrapper(const ScriptWrappableVisitor*) \
-      const override = 0;                                         \
-                                                                  \
- private:                                                         \
+#define MERGE_GARBAGE_COLLECTED_MIXINS()                                 \
+ public:                                                                 \
+  HeapObjectHeader* GetHeapObjectHeader() const override = 0;            \
+  TraceDescriptor GetTraceDescriptor() const override = 0;               \
+  TraceWrapperDescriptor GetTraceWrapperDescriptor() const override = 0; \
+                                                                         \
+ private:                                                                \
   using merge_garbage_collected_mixins_requires_semicolon = void
 
 // Base class for objects allocated in the Blink garbage-collected heap.
diff --git a/third_party/WebKit/Source/platform/heap/TraceTraits.h b/third_party/WebKit/Source/platform/heap/TraceTraits.h
index 85590c9..60cad6b 100644
--- a/third_party/WebKit/Source/platform/heap/TraceTraits.h
+++ b/third_party/WebKit/Source/platform/heap/TraceTraits.h
@@ -54,20 +54,18 @@
     return {self, TraceTrait<T>::Trace, TraceEagerlyTrait<T>::value};
   }
 
-  static HeapObjectHeader* GetHeapObjectHeader(const T* self) {
+  static TraceWrapperDescriptor GetTraceWrapperDescriptor(void* self) {
+    return {self, TraceTrait<T>::TraceWrappers,
+            ScriptWrappableVisitor::MissedWriteBarrier<T>,
+            ScriptWrappableVisitor::NameCallback<T>};
+  }
+
+  static HeapObjectHeader* GetHeapObjectHeader(void* self) {
 #if DCHECK_IS_ON()
     HeapObjectHeader::CheckFromPayload(self);
 #endif
     return HeapObjectHeader::FromPayload(self);
   }
-
-  static void TraceMarkedWrapper(const ScriptWrappableVisitor* visitor,
-                                 const T* self) {
-    // The term *mark* is misleading here as we effectively trace through the
-    // API boundary, i.e., tell V8 that an object is alive. Actual marking
-    // will be done in V8.
-    visitor->DispatchTraceWrappers(self);
-  }
 };
 
 template <typename T>
@@ -75,18 +73,19 @@
   STATIC_ONLY(AdjustAndMarkTrait);
 
  public:
-  static HeapObjectHeader* GetHeapObjectHeader(const T* self) {
-    return self->GetHeapObjectHeader();
-  }
-
-  static TraceDescriptor GetTraceDescriptor(T* self) {
+  static TraceDescriptor GetTraceDescriptor(const T* self) {
     DCHECK(self);
     return self->GetTraceDescriptor();
   }
 
-  static void TraceMarkedWrapper(const ScriptWrappableVisitor* visitor,
-                                 const T* self) {
-    self->AdjustAndTraceMarkedWrapper(visitor);
+  static TraceWrapperDescriptor GetTraceWrapperDescriptor(const T* self) {
+    DCHECK(self);
+    return self->GetTraceWrapperDescriptor();
+  }
+
+  static HeapObjectHeader* GetHeapObjectHeader(const T* self) {
+    DCHECK(self);
+    return self->GetHeapObjectHeader();
   }
 };
 
@@ -173,10 +172,11 @@
   }
 };
 
-// The TraceTrait is used to specify how to mark an object pointer and
-// how to trace all of the pointers in the object.
+// The TraceTrait is used to specify how to trace and object for Oilpan and
+// wrapper tracing.
 //
-// By default, the 'trace' method implemented on an object itself is
+//
+// By default, the 'Trace' method implemented on an object itself is
 // used to trace the pointers to other heap objects inside the object.
 //
 // However, the TraceTrait can be specialized to use a different
@@ -193,17 +193,17 @@
     return AdjustAndMarkTrait<T>::GetTraceDescriptor(static_cast<T*>(self));
   }
 
-  static void Trace(Visitor*, void* self);
-  static void TraceMarkedWrapper(const ScriptWrappableVisitor*, const void*);
-  static HeapObjectHeader* GetHeapObjectHeader(const void*);
-
- private:
-  static const T* ToWrapperTracingType(const void* t) {
-    static_assert(sizeof(T), "type needs to be defined");
-    static_assert(IsGarbageCollectedType<T>::value,
-                  "only objects deriving from GarbageCollected can be used");
-    return reinterpret_cast<const T*>(t);
+  static TraceWrapperDescriptor GetTraceWrapperDescriptor(void* self) {
+    return AdjustAndMarkTrait<T>::GetTraceWrapperDescriptor(
+        static_cast<T*>(self));
   }
+
+  static HeapObjectHeader* GetHeapObjectHeader(void* self) {
+    return AdjustAndMarkTrait<T>::GetHeapObjectHeader(static_cast<T*>(self));
+  }
+
+  static void Trace(Visitor*, void* self);
+  static void TraceWrappers(ScriptWrappableVisitor*, void*);
 };
 
 template <typename T>
@@ -216,16 +216,13 @@
 }
 
 template <typename T>
-void TraceTrait<T>::TraceMarkedWrapper(const ScriptWrappableVisitor* visitor,
-                                       const void* t) {
-  const T* traceable = ToWrapperTracingType(t);
-  AdjustAndMarkTrait<T>::TraceMarkedWrapper(visitor, traceable);
+void TraceTrait<T>::TraceWrappers(ScriptWrappableVisitor* visitor, void* self) {
+  static_assert(sizeof(T), "type needs to be defined");
+  static_assert(IsGarbageCollectedType<T>::value,
+                "only objects deriving from GarbageCollected can be used");
+  visitor->DispatchTraceWrappers(static_cast<T*>(self));
 }
 
-template <typename T>
-HeapObjectHeader* TraceTrait<T>::GetHeapObjectHeader(const void* t) {
-  return AdjustAndMarkTrait<T>::GetHeapObjectHeader(ToWrapperTracingType(t));
-}
 
 template <typename T, typename Traits>
 struct TraceTrait<HeapVectorBacking<T, Traits>> {
diff --git a/third_party/WebKit/Source/platform/runtime_enabled_features.json5 b/third_party/WebKit/Source/platform/runtime_enabled_features.json5
index 7b3ff5f..5003c6f 100644
--- a/third_party/WebKit/Source/platform/runtime_enabled_features.json5
+++ b/third_party/WebKit/Source/platform/runtime_enabled_features.json5
@@ -186,10 +186,6 @@
       status: "stable",
     },
     {
-      name: "CompositorImageAnimations",
-      status: "test",
-    },
-    {
       name: "CompositorTouchAction",
       status: "test",
     },
diff --git a/third_party/WebKit/public/platform/WebRuntimeFeatures.h b/third_party/WebKit/public/platform/WebRuntimeFeatures.h
index 562a705..3223d50a 100644
--- a/third_party/WebKit/public/platform/WebRuntimeFeatures.h
+++ b/third_party/WebKit/public/platform/WebRuntimeFeatures.h
@@ -67,8 +67,6 @@
 
   BLINK_PLATFORM_EXPORT static void EnableCompositorTouchAction(bool);
 
-  BLINK_PLATFORM_EXPORT static void EnableCompositorImageAnimations(bool);
-
   BLINK_PLATFORM_EXPORT static void EnableOriginTrials(bool);
   BLINK_PLATFORM_EXPORT static bool IsOriginTrialsEnabled();
 
diff --git a/third_party/WebKit/public/platform/modules/webauth/authenticator.mojom b/third_party/WebKit/public/platform/modules/webauth/authenticator.mojom
index 5d87d394..39082f5a8 100644
--- a/third_party/WebKit/public/platform/modules/webauth/authenticator.mojom
+++ b/third_party/WebKit/public/platform/modules/webauth/authenticator.mojom
@@ -53,10 +53,17 @@
   // Cryptographic signature proving possession of the credential private key.
   array<uint8> signature;
 
-   // Only supported by CTAP devices, not by U2F devices.
-   // Equivalent of the `user.id` passed into create().
-   // Maximum 64 bytes.
-   array<uint8>? user_handle;
+  // Only supported by CTAP devices, not by U2F devices.
+  // Equivalent of the `user.id` passed into create().
+  // Maximum 64 bytes.
+  array<uint8>? user_handle;
+
+  // True if getClientExtensionResults() called on the returned
+  // PublicKeyCredential instance should yield an
+  // AuthenticationExtensionsClientOutputs dictionary that contains the `appid:
+  // true` extension output, which indicates to the relying party that the
+  // `appid` extension was acted upon.
+  bool echo_appid_extension;
 };
 
 // Information about the relying party. These fields take arbitrary input.
diff --git a/third_party/blink/tools/move_blink_source.py b/third_party/blink/tools/move_blink_source.py
index 9807d229..54bc933c 100755
--- a/third_party/blink/tools/move_blink_source.py
+++ b/third_party/blink/tools/move_blink_source.py
@@ -529,7 +529,7 @@
         return '#%s "%s"' % (include_or_import, path)
 
     def _update_cpp_includes(self, content):
-        pattern = re.compile(r'#(include|import)\s+"((bindings|core|modules|platform|public|' +
+        pattern = re.compile(r'#(include|import)\s+"((bindings|controller||core|modules|platform|public|' +
                              r'third_party/WebKit/(Source|common|public))/[-_\w/.]+)"')
         return pattern.sub(self._replace_include_path, content)
 
@@ -551,10 +551,10 @@
                       partial(self._replace_basename_only_include, subdir, source_path), content)
 
     def _update_include_guard(self, content, source_path):
-        current_guard = re.sub(r'[.]', '_', self._fs.basename(source_path))
+        current_guard = re.sub(r'[-.]', '_', self._fs.basename(source_path))
         new_path = relative_dest(self._fs, self._fs.relpath(
             source_path, start=self._fs.join(self._repo_root, 'third_party', 'WebKit')))
-        new_guard = 'THIRD_PARTY_BLINK_' + re.sub(r'[\\/.]', '_', new_path.upper()) + '_'
+        new_guard = 'THIRD_PARTY_BLINK_' + re.sub(r'[-\\/.]', '_', new_path.upper()) + '_'
         content = re.sub(r'#ifndef\s+(WTF_)?' + current_guard, '#ifndef ' + new_guard, content);
         content = re.sub(r'#define\s+(WTF_)?' + current_guard, '#define ' + new_guard, content);
         content = re.sub(r'#endif\s+//\s+(WTF_)?' + current_guard, '#endif  // ' + new_guard, content);
diff --git a/third_party/blink/tools/plan_blink_move.py b/third_party/blink/tools/plan_blink_move.py
index 3a2b59d..4d2642e 100755
--- a/third_party/blink/tools/plan_blink_move.py
+++ b/third_party/blink/tools/plan_blink_move.py
@@ -38,10 +38,6 @@
                 or basename == 'PerformanceMonitor'
                 or basename == 'PlatformTraceEventsAgent'):
             return dest
-        # Skip CSSProperty*. Some files are generated, and some files are
-        # checked-in. It's hard to handle them automatically.
-        if re.search(r'css[\\/]properties$', dirname):
-            return dest
         if filename.endswith('.cpp'):
             ext = '.cc'
         # WebKit.h should be renamed to blink.h.
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index 3101da5..dc3244d 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -37,3 +37,9 @@
 Local Modifications:
  - codereview.settings has been excluded.
  - thread_log_messages.cc (using ThreadLocalStorage::Slot instead of StaticSlot)
+ - util/linux/memory_map.cc
+ - util/mach/symbolic_constants_mach.cc
+ - util/posix/symbolic_constants_posix.cc
+ - util/stdlib/string_number_conversion.h
+ - util/stdlib/string_number_conversion.cc
+ - util/stdlib/string_number_conversion_test.cc
diff --git a/third_party/crashpad/crashpad/util/linux/memory_map.cc b/third_party/crashpad/crashpad/util/linux/memory_map.cc
index cb7bfe1..f2df865a 100644
--- a/third_party/crashpad/crashpad/util/linux/memory_map.cc
+++ b/third_party/crashpad/crashpad/util/linux/memory_map.cc
@@ -36,7 +36,7 @@
 // Simply adding a StringToNumber for longs doesn't work since sometimes long
 // and int64_t are actually the same type, resulting in a redefinition error.
 template <typename Type>
-bool LocalStringToNumber(const base::StringPiece& string, Type* number) {
+bool LocalStringToNumber(const std::string& string, Type* number) {
   static_assert(sizeof(Type) == sizeof(int) || sizeof(Type) == sizeof(int64_t),
                 "Unexpected Type size");
 
diff --git a/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc b/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc
index b7296be..8f95915e 100644
--- a/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc
+++ b/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc
@@ -239,7 +239,8 @@
   }
 
   if (options & kAllowNumber) {
-    return StringToNumber(string, reinterpret_cast<unsigned int*>(exception));
+    return StringToNumber(std::string(string.data(), string.length()),
+                          reinterpret_cast<unsigned int*>(exception));
   }
 
   return false;
@@ -352,7 +353,7 @@
   }
 
   if (options & kAllowNumber) {
-    return StringToNumber(string,
+    return StringToNumber(std::string(string.data(), string.length()),
                           reinterpret_cast<unsigned int*>(exception_mask));
   }
 
@@ -452,7 +453,8 @@
 
   if (options & kAllowNumber) {
     exception_behavior_t temp_behavior;
-    if (!StringToNumber(sp, reinterpret_cast<unsigned int*>(&temp_behavior))) {
+    if (!StringToNumber(std::string(sp.data(), sp.length()),
+                        reinterpret_cast<unsigned int*>(&temp_behavior))) {
       return false;
     }
     build_behavior |= temp_behavior;
@@ -539,7 +541,8 @@
   }
 
   if (options & kAllowNumber) {
-    return StringToNumber(string, reinterpret_cast<unsigned int*>(flavor));
+    return StringToNumber(std::string(string.data(), string.length()),
+                          reinterpret_cast<unsigned int*>(flavor));
   }
 
   return false;
diff --git a/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc
index 51cc583..c973c14 100644
--- a/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc
+++ b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc
@@ -161,7 +161,7 @@
   }
 
   if (options & kAllowNumber) {
-    return StringToNumber(string, signal);
+    return StringToNumber(std::string(string.data(), string.length()), signal);
   }
 
   return false;
diff --git a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc
index c3352be..859a83335 100644
--- a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc
+++ b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc
@@ -116,7 +116,7 @@
 };
 
 template <typename Traits>
-bool StringToIntegerInternal(const base::StringPiece& string,
+bool StringToIntegerInternal(const std::string& string,
                              typename Traits::IntType* number) {
   using IntType = typename Traits::IntType;
   using LongType = typename Traits::LongType;
@@ -127,14 +127,6 @@
     return false;
   }
 
-  if (string[string.length()] != '\0') {
-    // The implementations use the C standard library’s conversion routines,
-    // which rely on the strings having a trailing NUL character. std::string
-    // will NUL-terminate.
-    std::string terminated_string(string.data(), string.length());
-    return StringToIntegerInternal<Traits>(terminated_string, number);
-  }
-
   errno = 0;
   char* end;
   LongType result = Traits::Convert(string.data(), &end, 0);
@@ -152,19 +144,19 @@
 
 namespace crashpad {
 
-bool StringToNumber(const base::StringPiece& string, int* number) {
+bool StringToNumber(const std::string& string, int* number) {
   return StringToIntegerInternal<StringToIntTraits>(string, number);
 }
 
-bool StringToNumber(const base::StringPiece& string, unsigned int* number) {
+bool StringToNumber(const std::string& string, unsigned int* number) {
   return StringToIntegerInternal<StringToUnsignedIntTraits>(string, number);
 }
 
-bool StringToNumber(const base::StringPiece& string, int64_t* number) {
+bool StringToNumber(const std::string& string, int64_t* number) {
   return StringToIntegerInternal<StringToInt64Traits>(string, number);
 }
 
-bool StringToNumber(const base::StringPiece& string, uint64_t* number) {
+bool StringToNumber(const std::string& string, uint64_t* number) {
   return StringToIntegerInternal<StringToUnsignedInt64Traits>(string, number);
 }
 
diff --git a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h
index b7bdcce..b5f1d44a 100644
--- a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h
+++ b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h
@@ -15,7 +15,7 @@
 #ifndef CRASHPAD_UTIL_STDLIB_STRING_NUMBER_CONVERSION_H_
 #define CRASHPAD_UTIL_STDLIB_STRING_NUMBER_CONVERSION_H_
 
-#include "base/strings/string_piece.h"
+#include <string>
 
 namespace crashpad {
 
@@ -54,10 +54,10 @@
 //!     allow arbitrary bases based on whether the string begins with a prefix
 //!     indicating its base. The functions here are provided for situations
 //!     where such prefix recognition is desirable.
-bool StringToNumber(const base::StringPiece& string, int* number);
-bool StringToNumber(const base::StringPiece& string, unsigned int* number);
-bool StringToNumber(const base::StringPiece& string, int64_t* number);
-bool StringToNumber(const base::StringPiece& string, uint64_t* number);
+bool StringToNumber(const std::string& string, int* number);
+bool StringToNumber(const std::string& string, unsigned int* number);
+bool StringToNumber(const std::string& string, int64_t* number);
+bool StringToNumber(const std::string& string, uint64_t* number);
 //! \}
 
 }  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc
index dd17c00d..d855c8d 100644
--- a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc
+++ b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc
@@ -114,13 +114,9 @@
   // is split to avoid MSVC warning:
   //   "decimal digit terminates octal escape sequence".
   static constexpr char input[] = "6\000" "6";
-  base::StringPiece input_string(input, arraysize(input) - 1);
+  std::string input_string(input, arraysize(input) - 1);
   int output;
   EXPECT_FALSE(StringToNumber(input_string, &output));
-
-  // Ensure that a NUL is not required at the end of the string.
-  EXPECT_TRUE(StringToNumber(base::StringPiece("66", 1), &output));
-  EXPECT_EQ(output, 6);
 }
 
 TEST(StringNumberConversion, StringToUnsignedInt) {
@@ -212,13 +208,9 @@
   // is split to avoid MSVC warning:
   //   "decimal digit terminates octal escape sequence".
   static constexpr char input[] = "6\000" "6";
-  base::StringPiece input_string(input, arraysize(input) - 1);
+  std::string input_string(input, arraysize(input) - 1);
   unsigned int output;
   EXPECT_FALSE(StringToNumber(input_string, &output));
-
-  // Ensure that a NUL is not required at the end of the string.
-  EXPECT_TRUE(StringToNumber(base::StringPiece("66", 1), &output));
-  EXPECT_EQ(output, 6u);
 }
 
 TEST(StringNumberConversion, StringToInt64) {
diff --git a/third_party/custom_tabs_client/BUILD.gn b/third_party/custom_tabs_client/BUILD.gn
index 812a97f9..6f3da34 100644
--- a/third_party/custom_tabs_client/BUILD.gn
+++ b/third_party/custom_tabs_client/BUILD.gn
@@ -16,7 +16,7 @@
 android_resources("custom_tabs_support_resources") {
   resource_dirs = [ "src/customtabs/res" ]
   android_manifest = "src/customtabs/AndroidManifest.xml"
-  custom_package = "android.support.customtabs.browseractions"
+  custom_package = "android.support.customtabs"
 }
 
 android_apk("custom_tabs_client_example_apk") {
diff --git a/third_party/libusb/src/libusb/os/windows_usb.c b/third_party/libusb/src/libusb/os/windows_usb.c
index bc4def6073..48ea08ca 100644
--- a/third_party/libusb/src/libusb/os/windows_usb.c
+++ b/third_party/libusb/src/libusb/os/windows_usb.c
@@ -65,20 +65,6 @@
 static int winusbx_abort_control(int sub_api, struct usbi_transfer *itransfer);
 static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_handle);
 static int winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size);
-// HID API prototypes
-static int hid_init(int sub_api, struct libusb_context *ctx);
-static int hid_exit(int sub_api);
-static int hid_open(int sub_api, struct libusb_device_handle *dev_handle);
-static void hid_close(int sub_api, struct libusb_device_handle *dev_handle);
-static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
-static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
-static int hid_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting);
-static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer);
-static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer);
-static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint);
-static int hid_abort_transfers(int sub_api, struct usbi_transfer *itransfer);
-static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle);
-static int hid_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size);
 // Composite API prototypes
 static int composite_init(int sub_api, struct libusb_context *ctx);
 static int composite_exit(int sub_api);
@@ -117,8 +103,6 @@
 	if (!WinUSBX[sub_api].initialized) return LIBUSB_ERROR_ACCESS; } while(0)
 static struct winusb_interface WinUSBX[SUB_API_MAX];
 const char* sub_api_name[SUB_API_MAX] = WINUSBX_DRV_NAMES;
-bool api_hid_available = false;
-#define CHECK_HID_AVAILABLE do { if (!api_hid_available) return LIBUSB_ERROR_ACCESS; } while (0)
 
 static inline BOOLEAN guid_eq(const GUID *guid1, const GUID *guid2) {
 	if ((guid1 != NULL) && (guid2 != NULL)) {
@@ -733,7 +717,6 @@
 
 	switch(api_type) {
 	case USB_API_WINUSBX:
-	case USB_API_HID:
 		break;
 	default:
 		return LIBUSB_ERROR_INVALID_PARAM;
@@ -1277,13 +1260,6 @@
 	}
 
 	if (priv->usb_interface[interface_number].path != NULL) {
-		if (api == USB_API_HID) {
-			// HID devices can have multiple collections (COL##) for each MI_## interface
-			usbi_dbg("interface[%d] already set - ignoring HID collection: %s",
-				interface_number, device_id);
-			return LIBUSB_ERROR_ACCESS;
-		}
-		// In other cases, just use the latest data
 		safe_free(priv->usb_interface[interface_number].path);
 	}
 
@@ -1291,43 +1267,10 @@
 	priv->usb_interface[interface_number].path = dev_interface_path;
 	priv->usb_interface[interface_number].apib = &usb_api_backend[api];
 	priv->usb_interface[interface_number].sub_api = sub_api;
-	if ((api == USB_API_HID) && (priv->hid == NULL)) {
-		priv->hid = (struct hid_device_priv*) calloc(1, sizeof(struct hid_device_priv));
-		if (priv->hid == NULL)
-			return LIBUSB_ERROR_NO_MEM;
-	}
 
 	return LIBUSB_SUCCESS;
 }
 
-static int set_hid_interface(struct libusb_context* ctx, struct libusb_device* dev,
-							char* dev_interface_path)
-{
-	int i;
-	struct windows_device_priv *priv = _device_priv(dev);
-
-	if (priv->hid == NULL) {
-		usbi_err(ctx, "program assertion failed: parent is not HID");
-		return LIBUSB_ERROR_NO_DEVICE;
-	}
-	if (priv->hid->nb_interfaces == USB_MAXINTERFACES) {
-		usbi_err(ctx, "program assertion failed: max USB interfaces reached for HID device");
-		return LIBUSB_ERROR_NO_DEVICE;
-	}
-	for (i=0; i<priv->hid->nb_interfaces; i++) {
-		if (safe_strcmp(priv->usb_interface[i].path, dev_interface_path) == 0) {
-			usbi_dbg("interface[%d] already set to %s", i, dev_interface_path);
-			return LIBUSB_SUCCESS;
-		}
-	}
-
-	priv->usb_interface[priv->hid->nb_interfaces].path = dev_interface_path;
-	priv->usb_interface[priv->hid->nb_interfaces].apib = &usb_api_backend[USB_API_HID];
-	usbi_dbg("interface[%d] = %s", priv->hid->nb_interfaces, dev_interface_path);
-	priv->hid->nb_interfaces++;
-	return LIBUSB_SUCCESS;
-}
-
 /*
  * get_device_list: libusbx backend device enumeration function
  */
@@ -1338,14 +1281,12 @@
 	const char* usb_class[] = {"USB", "NUSB3", "IUSB3"};
 	SP_DEVINFO_DATA dev_info_data = { 0 };
 	SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL;
-	GUID hid_guid;
 #define MAX_ENUM_GUIDS 64
 	const GUID* guid[MAX_ENUM_GUIDS];
 #define HCD_PASS 0
 #define HUB_PASS 1
 #define GEN_PASS 2
 #define DEV_PASS 3
-#define HID_PASS 4
 	int r = LIBUSB_SUCCESS;
 	int api, sub_api;
 	size_t class_index = 0;
@@ -1372,17 +1313,14 @@
 	// PASS 3 : (re)enumerate generic USB devices (including driverless)
 	//           and list additional USB device interface GUIDs to explore
 	// PASS 4 : (re)enumerate master USB devices that have a device interface
-	// PASS 5+: (re)enumerate device interfaced GUIDs (including HID) and
-	//           set the device interfaces.
+	// PASS 5+: (re)enumerate device interfaced GUIDs and set the device interfaces.
 
 	// Init the GUID table
 	guid[HCD_PASS] = &GUID_DEVINTERFACE_USB_HOST_CONTROLLER;
 	guid[HUB_PASS] = &GUID_DEVINTERFACE_USB_HUB;
 	guid[GEN_PASS] = NULL;
 	guid[DEV_PASS] = &GUID_DEVINTERFACE_USB_DEVICE;
-	HidD_GetHidGuid(&hid_guid);
-	guid[HID_PASS] = &hid_guid;
-	nb_guids = HID_PASS+1;
+	nb_guids = DEV_PASS+1;
 
 	unref_list = (libusb_device**) calloc(unref_size, sizeof(libusb_device*));
 	if (unref_list == NULL) {
@@ -1392,8 +1330,8 @@
 	for (pass = 0; ((pass < nb_guids) && (r == LIBUSB_SUCCESS)); pass++) {
 //#define ENUM_DEBUG
 #ifdef ENUM_DEBUG
-		const char *passname[] = { "HCD", "HUB", "GEN", "DEV", "HID", "EXT" };
-		usbi_dbg("\n#### PROCESSING %ss %s", passname[(pass<=HID_PASS)?pass:HID_PASS+1],
+		const char *passname[] = { "HCD", "HUB", "GEN", "DEV", "EXT" };
+		usbi_dbg("\n#### PROCESSING %ss %s", passname[(pass<=DEV_PASS)?pass:DEV_PASS+1],
 			(pass!=GEN_PASS)?guid_to_string(guid[pass]):"");
 #endif
 		for (i = 0; ; i++) {
@@ -1501,9 +1439,6 @@
 					}
 				}
 				break;
-			case HID_PASS:
-				api = USB_API_HID;
-				break;
 			default:
 				// Get the API type (after checking that the driver installation is OK)
 				if ( (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_INSTALL_STATE,
@@ -1606,13 +1541,6 @@
 				case USB_API_COMPOSITE:
 				case USB_API_HUB:
 					break;
-				case USB_API_HID:
-					priv->hid = calloc(1, sizeof(struct hid_device_priv));
-					if (priv->hid == NULL) {
-						LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
-					}
-					priv->hid->nb_interfaces = 0;
-					break;
 				default:
 					// For other devices, the first interface is the same as the device
 					priv->usb_interface[0].path = (char*) calloc(safe_strlen(priv->path)+1, 1);
@@ -1644,13 +1572,8 @@
 					r = LIBUSB_SUCCESS;
 				}
 				break;
-			default:	// HID_PASS and later
-				if (parent_priv->apib->id == USB_API_HID) {
-					usbi_dbg("setting HID interface for [%lX]:", parent_dev->session_data);
-					r = set_hid_interface(ctx, parent_dev, dev_interface_path);
-					if (r != LIBUSB_SUCCESS) LOOP_BREAK(r);
-					dev_interface_path = NULL;
-				} else if (parent_priv->apib->id == USB_API_COMPOSITE) {
+			default:	// later passes
+				if (parent_priv->apib->id == USB_API_COMPOSITE) {
 					usbi_dbg("setting composite interface for [%lX]:", parent_dev->session_data);
 					switch (set_composite_interface(ctx, parent_dev, dev_interface_path, dev_id_path, api, sub_api)) {
 					case LIBUSB_SUCCESS:
@@ -1670,7 +1593,7 @@
 	}
 
 	// Free any additional GUIDs
-	for (pass = HID_PASS+1; pass < nb_guids; pass++) {
+	for (pass = DEV_PASS+1; pass < nb_guids; pass++) {
 		safe_free(guid[pass]);
 	}
 
@@ -1927,7 +1850,6 @@
 	struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
 
 	usbi_free_fd(&transfer_priv->pollable_fd);
-	safe_free(transfer_priv->hid_buffer);
 	// When auto claim is in use, attempt to release the auto-claimed interface
 	auto_release(itransfer);
 }
@@ -2388,7 +2310,6 @@
 const char* hub_driver_names[] = {"USBHUB", "USBHUB3", "NUSB3HUB", "RUSB3HUB", "FLXHCIH", "TIHUB3", "ETRONHUB3", "VIAHUB3", "ASMTHUB3", "IUSB3HUB"};
 const char* composite_driver_names[] = {"USBCCGP"};
 const char* winusbx_driver_names[] = WINUSBX_DRV_NAMES;
-const char* hid_driver_names[] = {"HIDUSB", "MOUHID", "KBDHID"};
 const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = {
 	{
 		USB_API_UNSUPPORTED,
@@ -2474,27 +2395,6 @@
 		winusbx_abort_control,
 		winusbx_abort_transfers,
 		winusbx_copy_transfer_data,
-	}, {
-		USB_API_HID,
-		"HID API",
-		hid_driver_names,
-		ARRAYSIZE(hid_driver_names),
-		hid_init,
-		hid_exit,
-		hid_open,
-		hid_close,
-		common_configure_endpoints,
-		hid_claim_interface,
-		hid_set_interface_altsetting,
-		hid_release_interface,
-		hid_clear_halt,
-		hid_reset_device,
-		hid_submit_bulk_transfer,
-		unsupported_submit_iso_transfer,
-		hid_submit_control_transfer,
-		hid_abort_transfers,
-		hid_abort_transfers,
-		hid_copy_transfer_data,
 	},
 };
 
@@ -2825,7 +2725,7 @@
 	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
 	int i;
 
-	if ((api_id < USB_API_WINUSBX) || (api_id > USB_API_HID)) {
+	if ((api_id < USB_API_WINUSBX) || (api_id >= USB_API_MAX)) {
 		usbi_dbg("unsupported API ID");
 		return -1;
 	}
@@ -3153,1043 +3053,6 @@
 }
 
 /*
- * Internal HID Support functions (from libusb-win32)
- * Note that functions that complete data transfer synchronously must return
- * LIBUSB_COMPLETED instead of LIBUSB_SUCCESS
- */
-static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size);
-static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size);
-
-static int _hid_wcslen(WCHAR *str)
-{
-	int i = 0;
-	while (str[i] && (str[i] != 0x409)) {
-		i++;
-	}
-	return i;
-}
-
-static int _hid_get_device_descriptor(struct hid_device_priv* dev, void *data, size_t *size)
-{
-	struct libusb_device_descriptor d;
-
-	d.bLength = LIBUSB_DT_DEVICE_SIZE;
-	d.bDescriptorType = LIBUSB_DT_DEVICE;
-	d.bcdUSB = 0x0200; /* 2.00 */
-	d.bDeviceClass = 0;
-	d.bDeviceSubClass = 0;
-	d.bDeviceProtocol = 0;
-	d.bMaxPacketSize0 = 64; /* fix this! */
-	d.idVendor = (uint16_t)dev->vid;
-	d.idProduct = (uint16_t)dev->pid;
-	d.bcdDevice = 0x0100;
-	d.iManufacturer = dev->string_index[0];
-	d.iProduct = dev->string_index[1];
-	d.iSerialNumber = dev->string_index[2];
-	d.bNumConfigurations = 1;
-
-	if (*size > LIBUSB_DT_DEVICE_SIZE)
-		*size = LIBUSB_DT_DEVICE_SIZE;
-	memcpy(data, &d, *size);
-	return LIBUSB_COMPLETED;
-}
-
-static int _hid_get_config_descriptor(struct hid_device_priv* dev, void *data, size_t *size)
-{
-	char num_endpoints = 0;
-	size_t config_total_len = 0;
-	char tmp[HID_MAX_CONFIG_DESC_SIZE];
-	struct libusb_config_descriptor *cd;
-	struct libusb_interface_descriptor *id;
-	struct libusb_hid_descriptor *hd;
-	struct libusb_endpoint_descriptor *ed;
-	size_t tmp_size;
-
-	if (dev->input_report_size)
-		num_endpoints++;
-	if (dev->output_report_size)
-		num_endpoints++;
-
-	config_total_len = LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE
-		+ LIBUSB_DT_HID_SIZE + num_endpoints * LIBUSB_DT_ENDPOINT_SIZE;
-
-
-	cd = (struct libusb_config_descriptor *)tmp;
-	id = (struct libusb_interface_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE);
-	hd = (struct libusb_hid_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE
-		+ LIBUSB_DT_INTERFACE_SIZE);
-	ed = (struct libusb_endpoint_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE
-		+ LIBUSB_DT_INTERFACE_SIZE
-		+ LIBUSB_DT_HID_SIZE);
-
-	cd->bLength = LIBUSB_DT_CONFIG_SIZE;
-	cd->bDescriptorType = LIBUSB_DT_CONFIG;
-	cd->wTotalLength = (uint16_t) config_total_len;
-	cd->bNumInterfaces = 1;
-	cd->bConfigurationValue = 1;
-	cd->iConfiguration = 0;
-	cd->bmAttributes = 1 << 7; /* bus powered */
-	cd->MaxPower = 50;
-
-	id->bLength = LIBUSB_DT_INTERFACE_SIZE;
-	id->bDescriptorType = LIBUSB_DT_INTERFACE;
-	id->bInterfaceNumber = 0;
-	id->bAlternateSetting = 0;
-	id->bNumEndpoints = num_endpoints;
-	id->bInterfaceClass = 3;
-	id->bInterfaceSubClass = 0;
-	id->bInterfaceProtocol = 0;
-	id->iInterface = 0;
-
-	tmp_size = LIBUSB_DT_HID_SIZE;
-	_hid_get_hid_descriptor(dev, hd, &tmp_size);
-
-	if (dev->input_report_size) {
-		ed->bLength = LIBUSB_DT_ENDPOINT_SIZE;
-		ed->bDescriptorType = LIBUSB_DT_ENDPOINT;
-		ed->bEndpointAddress = HID_IN_EP;
-		ed->bmAttributes = 3;
-		ed->wMaxPacketSize = dev->input_report_size - 1;
-		ed->bInterval = 10;
-		ed = (struct libusb_endpoint_descriptor *)((char*)ed + LIBUSB_DT_ENDPOINT_SIZE);
-	}
-
-	if (dev->output_report_size) {
-		ed->bLength = LIBUSB_DT_ENDPOINT_SIZE;
-		ed->bDescriptorType = LIBUSB_DT_ENDPOINT;
-		ed->bEndpointAddress = HID_OUT_EP;
-		ed->bmAttributes = 3;
-		ed->wMaxPacketSize = dev->output_report_size - 1;
-		ed->bInterval = 10;
-	}
-
-	if (*size > config_total_len)
-		*size = config_total_len;
-	memcpy(data, tmp, *size);
-	return LIBUSB_COMPLETED;
-}
-
-static int _hid_get_string_descriptor(struct hid_device_priv* dev, int _index,
-									  void *data, size_t *size)
-{
-	void *tmp = NULL;
-	size_t tmp_size = 0;
-	int i;
-
-	/* language ID, EN-US */
-	char string_langid[] = {
-		0x09,
-		0x04
-	};
-
-	if ((*size < 2) || (*size > 255)) {
-		return LIBUSB_ERROR_OVERFLOW;
-	}
-
-	if (_index == 0) {
-		tmp = string_langid;
-		tmp_size = sizeof(string_langid)+2;
-	} else {
-		for (i=0; i<3; i++) {
-			if (_index == (dev->string_index[i])) {
-				tmp = dev->string[i];
-				tmp_size = (_hid_wcslen(dev->string[i])+1) * sizeof(WCHAR);
-				break;
-			}
-		}
-		if (i == 3) {	// not found
-			return LIBUSB_ERROR_INVALID_PARAM;
-		}
-	}
-
-	if(!tmp_size) {
-		return LIBUSB_ERROR_INVALID_PARAM;
-	}
-
-	if (tmp_size < *size) {
-		*size = tmp_size;
-	}
-	// 2 byte header
-	((uint8_t*)data)[0] = (uint8_t)*size;
-	((uint8_t*)data)[1] = LIBUSB_DT_STRING;
-	memcpy((uint8_t*)data+2, tmp, *size-2);
-	return LIBUSB_COMPLETED;
-}
-
-static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size)
-{
-	struct libusb_hid_descriptor d;
-	uint8_t tmp[MAX_HID_DESCRIPTOR_SIZE];
-	size_t report_len = MAX_HID_DESCRIPTOR_SIZE;
-
-	_hid_get_report_descriptor(dev, tmp, &report_len);
-
-	d.bLength = LIBUSB_DT_HID_SIZE;
-	d.bDescriptorType = LIBUSB_DT_HID;
-	d.bcdHID = 0x0110; /* 1.10 */
-	d.bCountryCode = 0;
-	d.bNumDescriptors = 1;
-	d.bClassDescriptorType = LIBUSB_DT_REPORT;
-	d.wClassDescriptorLength = (uint16_t)report_len;
-
-	if (*size > LIBUSB_DT_HID_SIZE)
-		*size = LIBUSB_DT_HID_SIZE;
-	memcpy(data, &d, *size);
-	return LIBUSB_COMPLETED;
-}
-
-static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size)
-{
-	uint8_t d[MAX_HID_DESCRIPTOR_SIZE];
-	size_t i = 0;
-
-	/* usage page (0xFFA0 == vendor defined) */
-	d[i++] = 0x06; d[i++] = 0xA0; d[i++] = 0xFF;
-	/* usage (vendor defined) */
-	d[i++] = 0x09; d[i++] = 0x01;
-	/* start collection (application) */
-	d[i++] = 0xA1; d[i++] = 0x01;
-	/* input report */
-	if (dev->input_report_size) {
-		/* usage (vendor defined) */
-		d[i++] = 0x09; d[i++] = 0x01;
-		/* logical minimum (0) */
-		d[i++] = 0x15; d[i++] = 0x00;
-		/* logical maximum (255) */
-		d[i++] = 0x25; d[i++] = 0xFF;
-		/* report size (8 bits) */
-		d[i++] = 0x75; d[i++] = 0x08;
-		/* report count */
-		d[i++] = 0x95; d[i++] = (uint8_t)dev->input_report_size - 1;
-		/* input (data, variable, absolute) */
-		d[i++] = 0x81; d[i++] = 0x00;
-	}
-	/* output report */
-	if (dev->output_report_size) {
-		/* usage (vendor defined) */
-		d[i++] = 0x09; d[i++] = 0x02;
-		/* logical minimum (0) */
-		d[i++] = 0x15; d[i++] = 0x00;
-		/* logical maximum (255) */
-		d[i++] = 0x25; d[i++] = 0xFF;
-		/* report size (8 bits) */
-		d[i++] = 0x75; d[i++] = 0x08;
-		/* report count */
-		d[i++] = 0x95; d[i++] = (uint8_t)dev->output_report_size - 1;
-		/* output (data, variable, absolute) */
-		d[i++] = 0x91; d[i++] = 0x00;
-	}
-	/* feature report */
-	if (dev->feature_report_size) {
-		/* usage (vendor defined) */
-		d[i++] = 0x09; d[i++] = 0x03;
-		/* logical minimum (0) */
-		d[i++] = 0x15; d[i++] = 0x00;
-		/* logical maximum (255) */
-		d[i++] = 0x25; d[i++] = 0xFF;
-		/* report size (8 bits) */
-		d[i++] = 0x75; d[i++] = 0x08;
-		/* report count */
-		d[i++] = 0x95; d[i++] = (uint8_t)dev->feature_report_size - 1;
-		/* feature (data, variable, absolute) */
-		d[i++] = 0xb2; d[i++] = 0x02; d[i++] = 0x01;
-	}
-
-	/* end collection */
-	d[i++] = 0xC0;
-
-	if (*size > i)
-		*size = i;
-	memcpy(data, d, *size);
-	return LIBUSB_COMPLETED;
-}
-
-static int _hid_get_descriptor(struct hid_device_priv* dev, HANDLE hid_handle, int recipient,
-							   int type, int _index, void *data, size_t *size)
-{
-	switch(type) {
-	case LIBUSB_DT_DEVICE:
-		usbi_dbg("LIBUSB_DT_DEVICE");
-		return _hid_get_device_descriptor(dev, data, size);
-	case LIBUSB_DT_CONFIG:
-		usbi_dbg("LIBUSB_DT_CONFIG");
-		if (!_index)
-			return _hid_get_config_descriptor(dev, data, size);
-		return LIBUSB_ERROR_INVALID_PARAM;
-	case LIBUSB_DT_STRING:
-		usbi_dbg("LIBUSB_DT_STRING");
-		return _hid_get_string_descriptor(dev, _index, data, size);
-	case LIBUSB_DT_HID:
-		usbi_dbg("LIBUSB_DT_HID");
-		if (!_index)
-			return _hid_get_hid_descriptor(dev, data, size);
-		return LIBUSB_ERROR_INVALID_PARAM;
-	case LIBUSB_DT_REPORT:
-		usbi_dbg("LIBUSB_DT_REPORT");
-		if (!_index)
-			return _hid_get_report_descriptor(dev, data, size);
-		return LIBUSB_ERROR_INVALID_PARAM;
-	case LIBUSB_DT_PHYSICAL:
-		usbi_dbg("LIBUSB_DT_PHYSICAL");
-		if (HidD_GetPhysicalDescriptor(hid_handle, data, (ULONG)*size))
-			return LIBUSB_COMPLETED;
-		return LIBUSB_ERROR_OTHER;
-	}
-	usbi_dbg("unsupported");
-	return LIBUSB_ERROR_INVALID_PARAM;
-}
-
-static int _hid_get_report(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data,
-						   struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped,
-						   int report_type)
-{
-	uint8_t *buf;
-	DWORD ioctl_code, read_size, expected_size = (DWORD)*size;
-	int r = LIBUSB_SUCCESS;
-
-	if (tp->hid_buffer != NULL) {
-		usbi_dbg("program assertion failed: hid_buffer is not NULL");
-	}
-
-	if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) {
-		usbi_dbg("invalid size (%d)", *size);
-		return LIBUSB_ERROR_INVALID_PARAM;
-	}
-
-	switch (report_type) {
-		case HID_REPORT_TYPE_INPUT:
-			ioctl_code = IOCTL_HID_GET_INPUT_REPORT;
-			break;
-		case HID_REPORT_TYPE_FEATURE:
-			ioctl_code = IOCTL_HID_GET_FEATURE;
-			break;
-		default:
-			usbi_dbg("unknown HID report type %d", report_type);
-			return LIBUSB_ERROR_INVALID_PARAM;
-	}
-
-	// Add a trailing byte to detect overflows
-	buf = (uint8_t*)calloc(expected_size+1, 1);
-	if (buf == NULL) {
-		return LIBUSB_ERROR_NO_MEM;
-	}
-	buf[0] = (uint8_t)id;	// Must be set always
-	usbi_dbg("report ID: 0x%02X", buf[0]);
-
-	tp->hid_expected_size = expected_size;
-	read_size = expected_size;
-
-	// NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0)
-	if (!DeviceIoControl(hid_handle, ioctl_code, buf, expected_size+1,
-		buf, expected_size+1, &read_size, overlapped)) {
-		if (GetLastError() != ERROR_IO_PENDING) {
-			usbi_dbg("Failed to Read HID Report: %s", windows_error_str(0));
-			safe_free(buf);
-			return LIBUSB_ERROR_IO;
-		}
-		// Asynchronous wait
-		tp->hid_buffer = buf;
-		tp->hid_dest = (uint8_t*)data; // copy dest, as not necessarily the start of the transfer buffer
-		return LIBUSB_SUCCESS;
-	}
-
-	// Transfer completed synchronously => copy and discard extra buffer
-	if (read_size == 0) {
-		usbi_warn(NULL, "program assertion failed - read completed synchronously, but no data was read");
-		*size = 0;
-	} else {
-		if (buf[0] != id) {
-			usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id);
-		}
-		if ((size_t)read_size > expected_size) {
-			r = LIBUSB_ERROR_OVERFLOW;
-			usbi_dbg("OVERFLOW!");
-		} else {
-			r = LIBUSB_COMPLETED;
-		}
-
-		*size = MIN((size_t)read_size, *size);
-		if (id == 0) {
-			// Discard report ID
-			memcpy(data, buf+1, *size);
-		} else {
-			memcpy(data, buf, *size);
-		}
-	}
-	safe_free(buf);
-	return r;
-}
-
-static int _hid_set_report(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data,
-						   struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped,
-						   int report_type)
-{
-	uint8_t *buf = NULL;
-	DWORD ioctl_code, write_size= (DWORD)*size;
-
-	if (tp->hid_buffer != NULL) {
-		usbi_dbg("program assertion failed: hid_buffer is not NULL");
-	}
-
-	if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) {
-		usbi_dbg("invalid size (%d)", *size);
-		return LIBUSB_ERROR_INVALID_PARAM;
-	}
-
-	switch (report_type) {
-		case HID_REPORT_TYPE_OUTPUT:
-			ioctl_code = IOCTL_HID_SET_OUTPUT_REPORT;
-			break;
-		case HID_REPORT_TYPE_FEATURE:
-			ioctl_code = IOCTL_HID_SET_FEATURE;
-			break;
-		default:
-			usbi_dbg("unknown HID report type %d", report_type);
-			return LIBUSB_ERROR_INVALID_PARAM;
-	}
-
-	usbi_dbg("report ID: 0x%02X", id);
-	// When report IDs are not used (i.e. when id == 0), we must add
-	// a null report ID. Otherwise, we just use original data buffer
-	if (id == 0) {
-		write_size++;
-	}
-	buf = (uint8_t*) malloc(write_size);
-	if (buf == NULL) {
-		return LIBUSB_ERROR_NO_MEM;
-	}
-	if (id == 0) {
-		buf[0] = 0;
-		memcpy(buf + 1, data, *size);
-	} else {
-		// This seems like a waste, but if we don't duplicate the
-		// data, we'll get issues when freeing hid_buffer
-		memcpy(buf, data, *size);
-		if (buf[0] != id) {
-			usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id);
-		}
-	}
-
-	// NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0)
-	if (!DeviceIoControl(hid_handle, ioctl_code, buf, write_size,
-		buf, write_size, &write_size, overlapped)) {
-		if (GetLastError() != ERROR_IO_PENDING) {
-			usbi_dbg("Failed to Write HID Output Report: %s", windows_error_str(0));
-			safe_free(buf);
-			return LIBUSB_ERROR_IO;
-		}
-		tp->hid_buffer = buf;
-		tp->hid_dest = NULL;
-		return LIBUSB_SUCCESS;
-	}
-
-	// Transfer completed synchronously
-	*size = write_size;
-	if (write_size == 0) {
-		usbi_dbg("program assertion failed - write completed synchronously, but no data was written");
-	}
-	safe_free(buf);
-	return LIBUSB_COMPLETED;
-}
-
-static int _hid_class_request(struct hid_device_priv* dev, HANDLE hid_handle, int request_type,
-							  int request, int value, int _index, void *data, struct windows_transfer_priv *tp,
-							  size_t *size, OVERLAPPED* overlapped)
-{
-	int report_type = (value >> 8) & 0xFF;
-	int report_id = value & 0xFF;
-
-	if ( (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_INTERFACE)
-	  && (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_DEVICE) )
-		return LIBUSB_ERROR_INVALID_PARAM;
-
-	if (LIBUSB_REQ_OUT(request_type) && request == HID_REQ_SET_REPORT)
-		return _hid_set_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type);
-
-	if (LIBUSB_REQ_IN(request_type) && request == HID_REQ_GET_REPORT)
-		return _hid_get_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type);
-
-	return LIBUSB_ERROR_INVALID_PARAM;
-}
-
-
-/*
- * HID API functions
- */
-static int hid_init(int sub_api, struct libusb_context *ctx)
-{
-	DLL_LOAD(hid.dll, HidD_GetAttributes, TRUE);
-	DLL_LOAD(hid.dll, HidD_GetHidGuid, TRUE);
-	DLL_LOAD(hid.dll, HidD_GetPreparsedData, TRUE);
-	DLL_LOAD(hid.dll, HidD_FreePreparsedData, TRUE);
-	DLL_LOAD(hid.dll, HidD_GetManufacturerString, TRUE);
-	DLL_LOAD(hid.dll, HidD_GetProductString, TRUE);
-	DLL_LOAD(hid.dll, HidD_GetSerialNumberString, TRUE);
-	DLL_LOAD(hid.dll, HidP_GetCaps, TRUE);
-	DLL_LOAD(hid.dll, HidD_SetNumInputBuffers, TRUE);
-	DLL_LOAD(hid.dll, HidD_SetFeature, TRUE);
-	DLL_LOAD(hid.dll, HidD_GetFeature, TRUE);
-	DLL_LOAD(hid.dll, HidD_GetPhysicalDescriptor, TRUE);
-	DLL_LOAD(hid.dll, HidD_GetInputReport, FALSE);
-	DLL_LOAD(hid.dll, HidD_SetOutputReport, FALSE);
-	DLL_LOAD(hid.dll, HidD_FlushQueue, TRUE);
-	DLL_LOAD(hid.dll, HidP_GetValueCaps, TRUE);
-
-	api_hid_available = true;
-	return LIBUSB_SUCCESS;
-}
-
-static int hid_exit(int sub_api)
-{
-	return LIBUSB_SUCCESS;
-}
-
-// NB: open and close must ensure that they only handle interface of
-// the right API type, as these functions can be called wholesale from
-// composite_open(), with interfaces belonging to different APIs
-static int hid_open(int sub_api, struct libusb_device_handle *dev_handle)
-{
-	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
-	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
-	struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
-
-	HIDD_ATTRIBUTES hid_attributes;
-	PHIDP_PREPARSED_DATA preparsed_data = NULL;
-	HIDP_CAPS capabilities;
-	HIDP_VALUE_CAPS *value_caps;
-
-	HANDLE hid_handle = INVALID_HANDLE_VALUE;
-	int i, j;
-	// report IDs handling
-	ULONG size[3];
-	const char* type[3] = {"input", "output", "feature"};
-	int nb_ids[2];	// zero and nonzero report IDs
-
-	CHECK_HID_AVAILABLE;
-	if (priv->hid == NULL) {
-		usbi_err(ctx, "program assertion failed - private HID structure is unitialized");
-		return LIBUSB_ERROR_NOT_FOUND;
-	}
-
-	for (i = 0; i < USB_MAXINTERFACES; i++) {
-		if ( (priv->usb_interface[i].path != NULL)
-		  && (priv->usb_interface[i].apib->id == USB_API_HID) ) {
-			hid_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ,
-				NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
-			/*
-			 * http://www.lvr.com/hidfaq.htm: Why do I receive "Access denied" when attempting to access my HID?
-			 * "Windows 2000 and later have exclusive read/write access to HIDs that are configured as a system
-			 * keyboards or mice. An application can obtain a handle to a system keyboard or mouse by not
-			 * requesting READ or WRITE access with CreateFile. Applications can then use HidD_SetFeature and
-			 * HidD_GetFeature (if the device supports Feature reports)."
-			 */
-			if (hid_handle == INVALID_HANDLE_VALUE) {
-				usbi_warn(ctx, "could not open HID device in R/W mode (keyboard or mouse?) - trying without");
-				hid_handle = CreateFileA(priv->usb_interface[i].path, 0, FILE_SHARE_WRITE | FILE_SHARE_READ,
-					NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
-				if (hid_handle == INVALID_HANDLE_VALUE) {
-					usbi_err(ctx, "could not open device %s (interface %d): %s", priv->path, i, windows_error_str(0));
-					switch(GetLastError()) {
-					case ERROR_FILE_NOT_FOUND:	// The device was disconnected
-						return LIBUSB_ERROR_NO_DEVICE;
-					case ERROR_ACCESS_DENIED:
-						return LIBUSB_ERROR_ACCESS;
-					default:
-						return LIBUSB_ERROR_IO;
-					}
-				}
-				priv->usb_interface[i].restricted_functionality = true;
-			}
-			handle_priv->interface_handle[i].api_handle = hid_handle;
-		}
-	}
-
-	hid_attributes.Size = sizeof(hid_attributes);
-	do {
-		if (!HidD_GetAttributes(hid_handle, &hid_attributes)) {
-			usbi_err(ctx, "could not gain access to HID top collection (HidD_GetAttributes)");
-			break;
-		}
-
-		priv->hid->vid = hid_attributes.VendorID;
-		priv->hid->pid = hid_attributes.ProductID;
-
-		// Set the maximum available input buffer size
-		for (i=32; HidD_SetNumInputBuffers(hid_handle, i); i*=2);
-		usbi_dbg("set maximum input buffer size to %d", i/2);
-
-		// Get the maximum input and output report size
-		if (!HidD_GetPreparsedData(hid_handle, &preparsed_data) || !preparsed_data) {
-			usbi_err(ctx, "could not read HID preparsed data (HidD_GetPreparsedData)");
-			break;
-		}
-		if (HidP_GetCaps(preparsed_data, &capabilities) != HIDP_STATUS_SUCCESS) {
-			usbi_err(ctx, "could not parse HID capabilities (HidP_GetCaps)");
-			break;
-		}
-
-		// Find out if interrupt will need report IDs
-		size[0] = capabilities.NumberInputValueCaps;
-		size[1] = capabilities.NumberOutputValueCaps;
-		size[2] = capabilities.NumberFeatureValueCaps;
-		for (j=HidP_Input; j<=HidP_Feature; j++) {
-			usbi_dbg("%d HID %s report value(s) found", size[j], type[j]);
-			priv->hid->uses_report_ids[j] = false;
-			if (size[j] > 0) {
-				value_caps = (HIDP_VALUE_CAPS*) calloc(size[j], sizeof(HIDP_VALUE_CAPS));
-				if ( (value_caps != NULL)
-				  && (HidP_GetValueCaps((HIDP_REPORT_TYPE)j, value_caps, &size[j], preparsed_data) == HIDP_STATUS_SUCCESS)
-				  && (size[j] >= 1) ) {
-					nb_ids[0] = 0;
-					nb_ids[1] = 0;
-					for (i=0; i<(int)size[j]; i++) {
-						usbi_dbg("  Report ID: 0x%02X", value_caps[i].ReportID);
-						if (value_caps[i].ReportID != 0) {
-							nb_ids[1]++;
-						} else {
-							nb_ids[0]++;
-						}
-					}
-					if (nb_ids[1] != 0) {
-						if (nb_ids[0] != 0) {
-							usbi_warn(ctx, "program assertion failed: zero and nonzero report IDs used for %s",
-								type[j]);
-						}
-						priv->hid->uses_report_ids[j] = true;
-					}
-				} else {
-					usbi_warn(ctx, "  could not process %s report IDs", type[j]);
-				}
-				safe_free(value_caps);
-			}
-		}
-
-		// Set the report sizes
-		priv->hid->input_report_size = capabilities.InputReportByteLength;
-		priv->hid->output_report_size = capabilities.OutputReportByteLength;
-		priv->hid->feature_report_size = capabilities.FeatureReportByteLength;
-
-		// Fetch string descriptors
-		priv->hid->string_index[0] = priv->dev_descriptor.iManufacturer;
-		if (priv->hid->string_index[0] != 0) {
-			HidD_GetManufacturerString(hid_handle, priv->hid->string[0],
-				sizeof(priv->hid->string[0]));
-		} else {
-			priv->hid->string[0][0] = 0;
-		}
-		priv->hid->string_index[1] = priv->dev_descriptor.iProduct;
-		if (priv->hid->string_index[1] != 0) {
-			HidD_GetProductString(hid_handle, priv->hid->string[1],
-				sizeof(priv->hid->string[1]));
-		} else {
-			priv->hid->string[1][0] = 0;
-		}
-		priv->hid->string_index[2] = priv->dev_descriptor.iSerialNumber;
-		if (priv->hid->string_index[2] != 0) {
-			HidD_GetSerialNumberString(hid_handle, priv->hid->string[2],
-				sizeof(priv->hid->string[2]));
-		} else {
-			priv->hid->string[2][0] = 0;
-		}
-	} while(0);
-
-	if (preparsed_data) {
-		HidD_FreePreparsedData(preparsed_data);
-	}
-
-	return LIBUSB_SUCCESS;
-}
-
-static void hid_close(int sub_api, struct libusb_device_handle *dev_handle)
-{
-	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
-	struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
-	HANDLE file_handle;
-	int i;
-
-	if (!api_hid_available)
-		return;
-
-	for (i = 0; i < USB_MAXINTERFACES; i++) {
-		if (priv->usb_interface[i].apib->id == USB_API_HID) {
-			file_handle = handle_priv->interface_handle[i].api_handle;
-			if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) {
-				CloseHandle(file_handle);
-			}
-		}
-	}
-}
-
-static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
-{
-	struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
-	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
-
-	CHECK_HID_AVAILABLE;
-
-	// NB: Disconnection detection is not possible in this function
-	if (priv->usb_interface[iface].path == NULL) {
-		return LIBUSB_ERROR_NOT_FOUND;	// invalid iface
-	}
-
-	// We use dev_handle as a flag for interface claimed
-	if (handle_priv->interface_handle[iface].dev_handle == INTERFACE_CLAIMED) {
-		return LIBUSB_ERROR_BUSY;	// already claimed
-	}
-
-	handle_priv->interface_handle[iface].dev_handle = INTERFACE_CLAIMED;
-
-	usbi_dbg("claimed interface %d", iface);
-	handle_priv->active_interface = iface;
-
-	return LIBUSB_SUCCESS;
-}
-
-static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
-{
-	struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
-	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
-
-	CHECK_HID_AVAILABLE;
-
-	if (priv->usb_interface[iface].path == NULL) {
-		return LIBUSB_ERROR_NOT_FOUND;	// invalid iface
-	}
-
-	if (handle_priv->interface_handle[iface].dev_handle != INTERFACE_CLAIMED) {
-		return LIBUSB_ERROR_NOT_FOUND;	// invalid iface
-	}
-
-	handle_priv->interface_handle[iface].dev_handle = INVALID_HANDLE_VALUE;
-
-	return LIBUSB_SUCCESS;
-}
-
-static int hid_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting)
-{
-	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
-
-	CHECK_HID_AVAILABLE;
-
-	if (altsetting > 255) {
-		return LIBUSB_ERROR_INVALID_PARAM;
-	}
-
-	if (altsetting != 0) {
-		usbi_err(ctx, "set interface altsetting not supported for altsetting >0");
-		return LIBUSB_ERROR_NOT_SUPPORTED;
-	}
-
-	return LIBUSB_SUCCESS;
-}
-
-static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer)
-{
-	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-	struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
-	struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
-	struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
-	struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
-	WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *) transfer->buffer;
-	HANDLE hid_handle;
-	struct winfd wfd;
-	int current_interface, config;
-	size_t size;
-	int r = LIBUSB_ERROR_INVALID_PARAM;
-
-	CHECK_HID_AVAILABLE;
-
-	transfer_priv->pollable_fd = INVALID_WINFD;
-	safe_free(transfer_priv->hid_buffer);
-	transfer_priv->hid_dest = NULL;
-	size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE;
-
-	if (size > MAX_CTRL_BUFFER_LENGTH) {
-		return LIBUSB_ERROR_INVALID_PARAM;
-	}
-
-	current_interface = get_valid_interface(transfer->dev_handle, USB_API_HID);
-	if (current_interface < 0) {
-		if (auto_claim(transfer, &current_interface, USB_API_HID) != LIBUSB_SUCCESS) {
-			return LIBUSB_ERROR_NOT_FOUND;
-		}
-	}
-
-	usbi_dbg("will use interface %d", current_interface);
-	hid_handle = handle_priv->interface_handle[current_interface].api_handle;
-	// Always use the handle returned from usbi_create_fd (wfd.handle)
-	wfd = usbi_create_fd(hid_handle, RW_READ, NULL, NULL);
-	if (wfd.fd < 0) {
-		return LIBUSB_ERROR_NOT_FOUND;
-	}
-
-	switch(LIBUSB_REQ_TYPE(setup->request_type)) {
-	case LIBUSB_REQUEST_TYPE_STANDARD:
-		switch(setup->request) {
-		case LIBUSB_REQUEST_GET_DESCRIPTOR:
-			r = _hid_get_descriptor(priv->hid, wfd.handle, LIBUSB_REQ_RECIPIENT(setup->request_type),
-				(setup->value >> 8) & 0xFF, setup->value & 0xFF, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, &size);
-			break;
-		case LIBUSB_REQUEST_GET_CONFIGURATION:
-			r = windows_get_configuration(transfer->dev_handle, &config);
-			if (r == LIBUSB_SUCCESS) {
-				size = 1;
-				((uint8_t*)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = (uint8_t)config;
-				r = LIBUSB_COMPLETED;
-			}
-			break;
-		case LIBUSB_REQUEST_SET_CONFIGURATION:
-			if (setup->value == priv->active_config) {
-				r = LIBUSB_COMPLETED;
-			} else {
-				usbi_warn(ctx, "cannot set configuration other than the default one");
-				r = LIBUSB_ERROR_INVALID_PARAM;
-			}
-			break;
-		case LIBUSB_REQUEST_GET_INTERFACE:
-			size = 1;
-			((uint8_t*)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = 0;
-			r = LIBUSB_COMPLETED;
-			break;
-		case LIBUSB_REQUEST_SET_INTERFACE:
-			r = hid_set_interface_altsetting(0, transfer->dev_handle, setup->index, setup->value);
-			if (r == LIBUSB_SUCCESS) {
-				r = LIBUSB_COMPLETED;
-			}
-			break;
-		default:
-			usbi_warn(ctx, "unsupported HID control request");
-			r = LIBUSB_ERROR_INVALID_PARAM;
-			break;
-		}
-		break;
-	case LIBUSB_REQUEST_TYPE_CLASS:
-		r =_hid_class_request(priv->hid, wfd.handle, setup->request_type, setup->request, setup->value,
-			setup->index, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, transfer_priv,
-			&size, wfd.overlapped);
-		break;
-	default:
-		usbi_warn(ctx, "unsupported HID control request");
-		r = LIBUSB_ERROR_INVALID_PARAM;
-		break;
-	}
-
-	if (r == LIBUSB_COMPLETED) {
-		// Force request to be completed synchronously. Transferred size has been set by previous call
-		wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
-		// http://msdn.microsoft.com/en-us/library/ms684342%28VS.85%29.aspx
-		// set InternalHigh to the number of bytes transferred
-		wfd.overlapped->InternalHigh = (DWORD)size;
-		r = LIBUSB_SUCCESS;
-	}
-
-	if (r == LIBUSB_SUCCESS) {
-		// Use priv_transfer to store data needed for async polling
-		transfer_priv->pollable_fd = wfd;
-		transfer_priv->interface_number = (uint8_t)current_interface;
-	} else {
-		usbi_free_fd(&wfd);
-	}
-
-	return r;
-}
-
-static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer) {
-	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-	struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
-	struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
-	struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
-	struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
-	struct winfd wfd;
-	HANDLE hid_handle;
-	bool direction_in, ret;
-	int current_interface, length;
-	DWORD size;
-	int r = LIBUSB_SUCCESS;
-
-	CHECK_HID_AVAILABLE;
-
-	transfer_priv->pollable_fd = INVALID_WINFD;
-	transfer_priv->hid_dest = NULL;
-	safe_free(transfer_priv->hid_buffer);
-
-	current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint);
-	if (current_interface < 0) {
-		usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer");
-		return LIBUSB_ERROR_NOT_FOUND;
-	}
-
-	usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface);
-
-	hid_handle = handle_priv->interface_handle[current_interface].api_handle;
-	direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN;
-
-	wfd = usbi_create_fd(hid_handle, direction_in?RW_READ:RW_WRITE, NULL, NULL);
-	// Always use the handle returned from usbi_create_fd (wfd.handle)
-	if (wfd.fd < 0) {
-		return LIBUSB_ERROR_NO_MEM;
-	}
-
-	// If report IDs are not in use, an extra prefix byte must be added
-	if ( ((direction_in) && (!priv->hid->uses_report_ids[0]))
-	  || ((!direction_in) && (!priv->hid->uses_report_ids[1])) ) {
-		length = transfer->length+1;
-	} else {
-		length = transfer->length;
-	}
-	// Add a trailing byte to detect overflows on input
-	transfer_priv->hid_buffer = (uint8_t*)calloc(length+1, 1);
-	if (transfer_priv->hid_buffer == NULL) {
-		return LIBUSB_ERROR_NO_MEM;
-	}
-	transfer_priv->hid_expected_size = length;
-
-	if (direction_in) {
-		transfer_priv->hid_dest = transfer->buffer;
-		usbi_dbg("reading %d bytes (report ID: 0x00)", length);
-		ret = ReadFile(wfd.handle, transfer_priv->hid_buffer, length+1, &size, wfd.overlapped);
-	} else {
-		if (!priv->hid->uses_report_ids[1]) {
-			memcpy(transfer_priv->hid_buffer+1, transfer->buffer, transfer->length);
-		} else {
-			// We could actually do without the calloc and memcpy in this case
-			memcpy(transfer_priv->hid_buffer, transfer->buffer, transfer->length);
-		}
-		usbi_dbg("writing %d bytes (report ID: 0x%02X)", length, transfer_priv->hid_buffer[0]);
-		ret = WriteFile(wfd.handle, transfer_priv->hid_buffer, length, &size, wfd.overlapped);
-	}
-	if (!ret) {
-		if (GetLastError() != ERROR_IO_PENDING) {
-			usbi_err(ctx, "HID transfer failed: %s", windows_error_str(0));
-			usbi_free_fd(&wfd);
-			safe_free(transfer_priv->hid_buffer);
-			return LIBUSB_ERROR_IO;
-		}
-	} else {
-		// Only write operations that completed synchronously need to free up
-		// hid_buffer. For reads, copy_transfer_data() handles that process.
-		if (!direction_in) {
-			safe_free(transfer_priv->hid_buffer);
-		}
-		if (size == 0) {
-			usbi_err(ctx, "program assertion failed - no data was transferred");
-			size = 1;
-		}
-		if (size > (size_t)length) {
-			usbi_err(ctx, "OVERFLOW!");
-			r = LIBUSB_ERROR_OVERFLOW;
-		}
-		wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
-		wfd.overlapped->InternalHigh = size;
-	}
-
-	transfer_priv->pollable_fd = wfd;
-	transfer_priv->interface_number = (uint8_t)current_interface;
-
-	return r;
-}
-
-static int hid_abort_transfers(int sub_api, struct usbi_transfer *itransfer)
-{
-	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-	struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
-	struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
-	HANDLE hid_handle;
-	int current_interface;
-
-	CHECK_HID_AVAILABLE;
-
-	current_interface = transfer_priv->interface_number;
-	hid_handle = handle_priv->interface_handle[current_interface].api_handle;
-	CancelIo(hid_handle);
-
-	return LIBUSB_SUCCESS;
-}
-
-static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle)
-{
-	struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
-	HANDLE hid_handle;
-	int current_interface;
-
-	CHECK_HID_AVAILABLE;
-
-	// Flushing the queues on all interfaces is the best we can achieve
-	for (current_interface = 0; current_interface < USB_MAXINTERFACES; current_interface++) {
-		hid_handle = handle_priv->interface_handle[current_interface].api_handle;
-		if ((hid_handle != 0) && (hid_handle != INVALID_HANDLE_VALUE)) {
-			HidD_FlushQueue(hid_handle);
-		}
-	}
-	return LIBUSB_SUCCESS;
-}
-
-static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint)
-{
-	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
-	struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
-	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
-	HANDLE hid_handle;
-	int current_interface;
-
-	CHECK_HID_AVAILABLE;
-
-	current_interface = interface_by_endpoint(priv, handle_priv, endpoint);
-	if (current_interface < 0) {
-		usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear");
-		return LIBUSB_ERROR_NOT_FOUND;
-	}
-
-	usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface);
-	hid_handle = handle_priv->interface_handle[current_interface].api_handle;
-
-	// No endpoint selection with Microsoft's implementation, so we try to flush the
-	// whole interface. Should be OK for most case scenarios
-	if (!HidD_FlushQueue(hid_handle)) {
-		usbi_err(ctx, "Flushing of HID queue failed: %s", windows_error_str(0));
-		// Device was probably disconnected
-		return LIBUSB_ERROR_NO_DEVICE;
-	}
-
-	return LIBUSB_SUCCESS;
-}
-
-// This extra function is only needed for HID
-static int hid_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size) {
-	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-	struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
-	struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
-	int r = LIBUSB_TRANSFER_COMPLETED;
-	uint32_t corrected_size = io_size;
-
-	if (transfer_priv->hid_buffer != NULL) {
-		// If we have a valid hid_buffer, it means the transfer was async
-		if (transfer_priv->hid_dest != NULL) {	// Data readout
-			// First, check for overflow
-			if (corrected_size > transfer_priv->hid_expected_size) {
-				usbi_err(ctx, "OVERFLOW!");
-				corrected_size = (uint32_t)transfer_priv->hid_expected_size;
-				r = LIBUSB_TRANSFER_OVERFLOW;
-			}
-
-			if (transfer_priv->hid_buffer[0] == 0) {
-				// Discard the 1 byte report ID prefix
-				corrected_size--;
-				memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer+1, corrected_size);
-			} else {
-				memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer, corrected_size);
-			}
-			transfer_priv->hid_dest = NULL;
-		}
-		// For write, we just need to free the hid buffer
-		safe_free(transfer_priv->hid_buffer);
-	}
-	itransfer->transferred += corrected_size;
-	return r;
-}
-
-
-/*
  * Composite API functions
  */
 static int composite_init(int sub_api, struct libusb_context *ctx)
@@ -4207,8 +3070,7 @@
 	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
 	int r = LIBUSB_ERROR_NOT_FOUND;
 	uint8_t i;
-	// SUB_API_MAX+1 as the SUB_API_MAX pos is used to indicate availability of HID
-	bool available[SUB_API_MAX+1] = {0};
+	bool available[SUB_API_MAX] = {0};
 
 	for (i=0; i<USB_MAXINTERFACES; i++) {
 		switch (priv->usb_interface[i].apib->id) {
@@ -4216,9 +3078,6 @@
 			if (priv->usb_interface[i].sub_api != SUB_API_NOTSET)
 				available[priv->usb_interface[i].sub_api] = true;
 			break;
-		case USB_API_HID:
-			available[SUB_API_MAX] = true;
-			break;
 		default:
 			break;
 		}
@@ -4232,9 +3091,6 @@
 			}
 		}
 	}
-	if (available[SUB_API_MAX]) {	// HID driver
-		r = hid_open(SUB_API_NOTSET, dev_handle);
-	}
 	return r;
 }
 
@@ -4243,7 +3099,6 @@
 	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
 	uint8_t i;
 	bool available[SUB_API_MAX];
-	bool has_hid = false;
 
 	for (i = 0; i<SUB_API_MAX; i++) {
 		available[i] = false;
@@ -4253,8 +3108,6 @@
 		if ( (priv->usb_interface[i].apib->id == USB_API_WINUSBX)
 		  && (priv->usb_interface[i].sub_api != SUB_API_NOTSET) ) {
 			available[priv->usb_interface[i].sub_api] = true;
-		} else if (priv->usb_interface[i].apib->id == USB_API_HID) {
-			has_hid = true;
 		}
 	}
 
@@ -4263,10 +3116,6 @@
 			usb_api_backend[USB_API_WINUSBX].close(i, dev_handle);
 		}
 	}
-
-	if (has_hid) {
-		usb_api_backend[USB_API_HID].close(sub_api, dev_handle);
-	}
 }
 
 static int composite_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
diff --git a/third_party/libusb/src/libusb/os/windows_usb.h b/third_party/libusb/src/libusb/os/windows_usb.h
index 5d67a56..97c8047c 100644
--- a/third_party/libusb/src/libusb/os/windows_usb.h
+++ b/third_party/libusb/src/libusb/os/windows_usb.h
@@ -52,19 +52,12 @@
 #define MAX_CTRL_BUFFER_LENGTH      4096
 #define MAX_USB_DEVICES             256
 #define MAX_USB_STRING_LENGTH       128
-#define MAX_HID_REPORT_SIZE         1024
-#define MAX_HID_DESCRIPTOR_SIZE     256
 #define MAX_GUID_STRING_LENGTH      40
 #define MAX_PATH_LENGTH             128
 #define MAX_KEY_LENGTH              256
 #define LIST_SEPARATOR              ';'
 #define HTAB_SIZE                   1021
 
-// Handle code for HID interface that have been claimed ("dibs")
-#define INTERFACE_CLAIMED           ((HANDLE)(intptr_t)0xD1B5)
-// Additional return code for HID operations that completed synchronously
-#define LIBUSB_COMPLETED            (LIBUSB_SUCCESS + 1)
-
 // http://msdn.microsoft.com/en-us/library/ff545978.aspx
 // http://msdn.microsoft.com/en-us/library/ff545972.aspx
 // http://msdn.microsoft.com/en-us/library/ff545982.aspx
@@ -89,9 +82,8 @@
 #define USB_API_HUB         1
 #define USB_API_COMPOSITE   2
 #define USB_API_WINUSBX     3
-#define USB_API_HID         4
-#define USB_API_MAX         5
-// The following is used to indicate if the HID or composite extra props have already been set.
+#define USB_API_MAX         4
+// The following is used to indicate if the composite extra props have already been set.
 #define USB_API_SET         (1<<USB_API_MAX) 
 
 // Sub-APIs for WinUSB-like driver APIs (WinUSB, libusbK, libusb-win32 through the libusbK DLL)
@@ -139,70 +131,11 @@
  * with inline pseudo constructors/destructors
  */
 
-// TODO (v2+): move hid desc to libusb.h?
-struct libusb_hid_descriptor {
-	uint8_t  bLength;
-	uint8_t  bDescriptorType;
-	uint16_t bcdHID;
-	uint8_t  bCountryCode;
-	uint8_t  bNumDescriptors;
-	uint8_t  bClassDescriptorType;
-	uint16_t wClassDescriptorLength;
-};
-#define LIBUSB_DT_HID_SIZE              9
-#define HID_MAX_CONFIG_DESC_SIZE (LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE \
-	+ LIBUSB_DT_HID_SIZE + 2 * LIBUSB_DT_ENDPOINT_SIZE)
-#define HID_MAX_REPORT_SIZE             1024
-#define HID_IN_EP                       0x81
-#define HID_OUT_EP                      0x02
 #define LIBUSB_REQ_RECIPIENT(request_type) ((request_type) & 0x1F)
 #define LIBUSB_REQ_TYPE(request_type) ((request_type) & (0x03 << 5))
 #define LIBUSB_REQ_IN(request_type) ((request_type) & LIBUSB_ENDPOINT_IN)
 #define LIBUSB_REQ_OUT(request_type) (!LIBUSB_REQ_IN(request_type))
 
-// The following are used for HID reports IOCTLs
-#define HID_CTL_CODE(id) \
-  CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_NEITHER, FILE_ANY_ACCESS)
-#define HID_BUFFER_CTL_CODE(id) \
-  CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_BUFFERED, FILE_ANY_ACCESS)
-#define HID_IN_CTL_CODE(id) \
-  CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_IN_DIRECT, FILE_ANY_ACCESS)
-#define HID_OUT_CTL_CODE(id) \
-  CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
-
-#define IOCTL_HID_GET_FEATURE                 HID_OUT_CTL_CODE(100)
-#define IOCTL_HID_GET_INPUT_REPORT            HID_OUT_CTL_CODE(104)
-#define IOCTL_HID_SET_FEATURE                 HID_IN_CTL_CODE(100)
-#define IOCTL_HID_SET_OUTPUT_REPORT           HID_IN_CTL_CODE(101)
-
-enum libusb_hid_request_type {
-	HID_REQ_GET_REPORT = 0x01,
-	HID_REQ_GET_IDLE = 0x02,
-	HID_REQ_GET_PROTOCOL = 0x03,
-	HID_REQ_SET_REPORT = 0x09,
-	HID_REQ_SET_IDLE = 0x0A,
-	HID_REQ_SET_PROTOCOL = 0x0B
-};
-
-enum libusb_hid_report_type {
-	HID_REPORT_TYPE_INPUT = 0x01,
-	HID_REPORT_TYPE_OUTPUT = 0x02,
-	HID_REPORT_TYPE_FEATURE = 0x03
-};
-
-struct hid_device_priv {
-	uint16_t vid;
-	uint16_t pid;
-	uint8_t config;
-	uint8_t nb_interfaces;
-	bool uses_report_ids[3];	// input, ouptput, feature
-	uint16_t input_report_size;
-	uint16_t output_report_size;
-	uint16_t feature_report_size;
-	WCHAR string[3][MAX_USB_STRING_LENGTH];
-	uint8_t string_index[3];	// man, prod, ser
-};
-
 typedef struct libusb_device_descriptor USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;
 struct windows_device_priv {
 	uint8_t depth;						// distance to HCD
@@ -221,7 +154,6 @@
 		bool restricted_functionality;	// indicates if the interface functionality is restricted
 										// by Windows (eg. HID keyboards or mice cannot do R/W)
 	} usb_interface[USB_MAXINTERFACES];
-	struct hid_device_priv *hid;
 	USB_DEVICE_DESCRIPTOR dev_descriptor;
 	unsigned char **config_descriptor;	// list of pointers to the cached config descriptors
 };
@@ -239,7 +171,6 @@
 	p->path = NULL;
 	p->apib = &usb_api_backend[USB_API_UNSUPPORTED];
 	p->sub_api = SUB_API_NOTSET;
-	p->hid = NULL;
 	p->active_config = 0;
 	p->config_descriptor = NULL;
 	memset(&(p->dev_descriptor), 0, sizeof(USB_DEVICE_DESCRIPTOR));
@@ -262,7 +193,6 @@
 			safe_free(p->config_descriptor[i]);
 	}
 	safe_free(p->config_descriptor);
-	safe_free(p->hid);
 	for (i=0; i<USB_MAXINTERFACES; i++) {
 		safe_free(p->usb_interface[i].path);
 		safe_free(p->usb_interface[i].endpoint);
@@ -290,9 +220,6 @@
 struct windows_transfer_priv {
 	struct winfd pollable_fd;
 	uint8_t interface_number;
-	uint8_t *hid_buffer; // 1 byte extended data buffer, required for HID
-	uint8_t *hid_dest;   // transfer buffer destination, required for HID
-	size_t hid_expected_size;
 };
 
 // used to match a device driver (including filter drivers) against a supported API
@@ -821,98 +748,4 @@
 	WinUsb_SetPowerPolicy_t SetPowerPolicy;
 	WinUsb_WritePipe_t WritePipe;
 	WinUsb_ResetDevice_t ResetDevice;
-};
-
-/* hid.dll interface */
-
-#define HIDP_STATUS_SUCCESS  0x110000
-typedef void* PHIDP_PREPARSED_DATA;
-
-#pragma pack(1)
-typedef struct {
-	ULONG Size;
-	USHORT VendorID;
-	USHORT ProductID;
-	USHORT VersionNumber;
-} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
-#pragma pack()
-
-typedef USHORT USAGE;
-typedef struct {
-  USAGE Usage;
-  USAGE UsagePage;
-  USHORT InputReportByteLength;
-  USHORT OutputReportByteLength;
-  USHORT FeatureReportByteLength;
-  USHORT Reserved[17];
-  USHORT NumberLinkCollectionNodes;
-  USHORT NumberInputButtonCaps;
-  USHORT NumberInputValueCaps;
-  USHORT NumberInputDataIndices;
-  USHORT NumberOutputButtonCaps;
-  USHORT NumberOutputValueCaps;
-  USHORT NumberOutputDataIndices;
-  USHORT NumberFeatureButtonCaps;
-  USHORT NumberFeatureValueCaps;
-  USHORT NumberFeatureDataIndices;
-} HIDP_CAPS, *PHIDP_CAPS;
-
-typedef enum _HIDP_REPORT_TYPE {
-  HidP_Input,
-  HidP_Output,
-  HidP_Feature
-} HIDP_REPORT_TYPE;
-
-typedef struct _HIDP_VALUE_CAPS {
-  USAGE  UsagePage;
-  UCHAR  ReportID;
-  BOOLEAN  IsAlias;
-  USHORT  BitField;
-  USHORT  LinkCollection;
-  USAGE  LinkUsage;
-  USAGE  LinkUsagePage;
-  BOOLEAN  IsRange;
-  BOOLEAN  IsStringRange;
-  BOOLEAN  IsDesignatorRange;
-  BOOLEAN  IsAbsolute;
-  BOOLEAN  HasNull;
-  UCHAR  Reserved;
-  USHORT  BitSize;
-  USHORT  ReportCount;
-  USHORT  Reserved2[5];
-  ULONG  UnitsExp;
-  ULONG  Units;
-  LONG  LogicalMin, LogicalMax;
-  LONG  PhysicalMin, PhysicalMax;
-	union {
-	  struct {
-		USAGE  UsageMin, UsageMax;
-		USHORT  StringMin, StringMax;
-		USHORT  DesignatorMin, DesignatorMax;
-		USHORT  DataIndexMin, DataIndexMax;
-	  } Range;
-	  struct {
-		USAGE  Usage, Reserved1;
-		USHORT  StringIndex, Reserved2;
-		USHORT  DesignatorIndex, Reserved3;
-		USHORT  DataIndex, Reserved4;
-	  } NotRange;
-	} u;
-} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
-
-DLL_DECLARE(WINAPI, BOOL, HidD_GetAttributes, (HANDLE, PHIDD_ATTRIBUTES));
-DLL_DECLARE(WINAPI, VOID, HidD_GetHidGuid, (LPGUID));
-DLL_DECLARE(WINAPI, BOOL, HidD_GetPreparsedData, (HANDLE, PHIDP_PREPARSED_DATA *));
-DLL_DECLARE(WINAPI, BOOL, HidD_FreePreparsedData, (PHIDP_PREPARSED_DATA));
-DLL_DECLARE(WINAPI, BOOL, HidD_GetManufacturerString, (HANDLE, PVOID, ULONG));
-DLL_DECLARE(WINAPI, BOOL, HidD_GetProductString, (HANDLE, PVOID, ULONG));
-DLL_DECLARE(WINAPI, BOOL, HidD_GetSerialNumberString, (HANDLE, PVOID, ULONG));
-DLL_DECLARE(WINAPI, LONG, HidP_GetCaps, (PHIDP_PREPARSED_DATA, PHIDP_CAPS));
-DLL_DECLARE(WINAPI, BOOL, HidD_SetNumInputBuffers, (HANDLE, ULONG));
-DLL_DECLARE(WINAPI, BOOL, HidD_SetFeature, (HANDLE, PVOID, ULONG));
-DLL_DECLARE(WINAPI, BOOL, HidD_GetFeature, (HANDLE, PVOID, ULONG));
-DLL_DECLARE(WINAPI, BOOL, HidD_GetPhysicalDescriptor, (HANDLE, PVOID, ULONG));
-DLL_DECLARE(WINAPI, BOOL, HidD_GetInputReport, (HANDLE, PVOID, ULONG));
-DLL_DECLARE(WINAPI, BOOL, HidD_SetOutputReport, (HANDLE, PVOID, ULONG));
-DLL_DECLARE(WINAPI, BOOL, HidD_FlushQueue, (HANDLE));
-DLL_DECLARE(WINAPI, BOOL, HidP_GetValueCaps, (HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PULONG, PHIDP_PREPARSED_DATA));
+};
\ No newline at end of file
diff --git a/third_party/libvpx/README.chromium b/third_party/libvpx/README.chromium
index 44fd8d66..324dd01 100644
--- a/third_party/libvpx/README.chromium
+++ b/third_party/libvpx/README.chromium
@@ -5,9 +5,9 @@
 License File: source/libvpx/LICENSE
 Security Critical: yes
 
-Date: Saturday February 10 2018
+Date: Saturday March 03 2018
 Branch: master
-Commit: edc9a4687699b372a0c27856020b42434ddc3014
+Commit: c6fcb9bb94ea02324d9d842b71ac3d0ab0329c10
 
 Description:
 Contains the sources used to compile libvpx binaries used by Google Chrome and
diff --git a/third_party/libvpx/libvpx_srcs.gni b/third_party/libvpx/libvpx_srcs.gni
index 6ff45bcb..dbba94e 100644
--- a/third_party/libvpx/libvpx_srcs.gni
+++ b/third_party/libvpx/libvpx_srcs.gni
@@ -1435,6 +1435,7 @@
   "//third_party/libvpx/source/libvpx/vp8/encoder/vp8_quantize.c",
   "//third_party/libvpx/source/libvpx/vp8/vp8_cx_iface.c",
   "//third_party/libvpx/source/libvpx/vp8/vp8_dx_iface.c",
+  "//third_party/libvpx/source/libvpx/vp9/common/arm/neon/vp9_iht16x16_add_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/common/arm/neon/vp9_iht4x4_add_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/common/arm/neon/vp9_iht8x8_add_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/common/arm/neon/vp9_iht_neon.h",
@@ -2084,6 +2085,7 @@
   "//third_party/libvpx/source/libvpx/vp8/encoder/arm/neon/fastquantizeb_neon.c",
   "//third_party/libvpx/source/libvpx/vp8/encoder/arm/neon/shortfdct_neon.c",
   "//third_party/libvpx/source/libvpx/vp8/encoder/arm/neon/vp8_shortwalsh4x4_neon.c",
+  "//third_party/libvpx/source/libvpx/vp9/common/arm/neon/vp9_iht16x16_add_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/common/arm/neon/vp9_iht4x4_add_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/common/arm/neon/vp9_iht8x8_add_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_dct_neon.c",
@@ -2261,6 +2263,7 @@
   "//third_party/libvpx/source/libvpx/vp8/encoder/vp8_quantize.c",
   "//third_party/libvpx/source/libvpx/vp8/vp8_cx_iface.c",
   "//third_party/libvpx/source/libvpx/vp8/vp8_dx_iface.c",
+  "//third_party/libvpx/source/libvpx/vp9/common/arm/neon/vp9_iht16x16_add_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/common/arm/neon/vp9_iht4x4_add_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/common/arm/neon/vp9_iht8x8_add_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/common/arm/neon/vp9_iht_neon.h",
diff --git a/third_party/libvpx/source/config/ios/arm-neon/vp9_rtcd.h b/third_party/libvpx/source/config/ios/arm-neon/vp9_rtcd.h
index bd397cc..4df63fce 100644
--- a/third_party/libvpx/source/config/ios/arm-neon/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/ios/arm-neon/vp9_rtcd.h
@@ -159,11 +159,7 @@
                          uint8_t* dest,
                          int stride,
                          int tx_type);
-void vp9_iht8x8_64_add_neon(const tran_low_t* input,
-                            uint8_t* dest,
-                            int stride,
-                            int tx_type);
-#define vp9_iht8x8_64_add vp9_iht8x8_64_add_neon
+#define vp9_iht8x8_64_add vp9_iht8x8_64_add_c
 
 void vp9_quantize_fp_c(const tran_low_t* coeff_ptr,
                        intptr_t n_coeffs,
diff --git a/third_party/libvpx/source/config/ios/arm64/vp9_rtcd.h b/third_party/libvpx/source/config/ios/arm64/vp9_rtcd.h
index bd397cc..4df63fce 100644
--- a/third_party/libvpx/source/config/ios/arm64/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/ios/arm64/vp9_rtcd.h
@@ -159,11 +159,7 @@
                          uint8_t* dest,
                          int stride,
                          int tx_type);
-void vp9_iht8x8_64_add_neon(const tran_low_t* input,
-                            uint8_t* dest,
-                            int stride,
-                            int tx_type);
-#define vp9_iht8x8_64_add vp9_iht8x8_64_add_neon
+#define vp9_iht8x8_64_add vp9_iht8x8_64_add_c
 
 void vp9_quantize_fp_c(const tran_low_t* coeff_ptr,
                        intptr_t n_coeffs,
diff --git a/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vp9_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vp9_rtcd.h
index dc5b2d1..25b8930c 100644
--- a/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vp9_rtcd.h
@@ -184,14 +184,7 @@
                          uint8_t* dest,
                          int stride,
                          int tx_type);
-void vp9_iht8x8_64_add_neon(const tran_low_t* input,
-                            uint8_t* dest,
-                            int stride,
-                            int tx_type);
-RTCD_EXTERN void (*vp9_iht8x8_64_add)(const tran_low_t* input,
-                                      uint8_t* dest,
-                                      int stride,
-                                      int tx_type);
+#define vp9_iht8x8_64_add vp9_iht8x8_64_add_c
 
 void vp9_quantize_fp_c(const tran_low_t* coeff_ptr,
                        intptr_t n_coeffs,
@@ -298,9 +291,6 @@
   vp9_iht4x4_16_add = vp9_iht4x4_16_add_c;
   if (flags & HAS_NEON)
     vp9_iht4x4_16_add = vp9_iht4x4_16_add_neon;
-  vp9_iht8x8_64_add = vp9_iht8x8_64_add_c;
-  if (flags & HAS_NEON)
-    vp9_iht8x8_64_add = vp9_iht8x8_64_add_neon;
   vp9_quantize_fp = vp9_quantize_fp_c;
   if (flags & HAS_NEON)
     vp9_quantize_fp = vp9_quantize_fp_neon;
diff --git a/third_party/libvpx/source/config/linux/arm-neon/vp9_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon/vp9_rtcd.h
index bd397cc..4df63fce 100644
--- a/third_party/libvpx/source/config/linux/arm-neon/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon/vp9_rtcd.h
@@ -159,11 +159,7 @@
                          uint8_t* dest,
                          int stride,
                          int tx_type);
-void vp9_iht8x8_64_add_neon(const tran_low_t* input,
-                            uint8_t* dest,
-                            int stride,
-                            int tx_type);
-#define vp9_iht8x8_64_add vp9_iht8x8_64_add_neon
+#define vp9_iht8x8_64_add vp9_iht8x8_64_add_c
 
 void vp9_quantize_fp_c(const tran_low_t* coeff_ptr,
                        intptr_t n_coeffs,
diff --git a/third_party/libvpx/source/config/linux/arm64/vp9_rtcd.h b/third_party/libvpx/source/config/linux/arm64/vp9_rtcd.h
index bd397cc..4df63fce 100644
--- a/third_party/libvpx/source/config/linux/arm64/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm64/vp9_rtcd.h
@@ -159,11 +159,7 @@
                          uint8_t* dest,
                          int stride,
                          int tx_type);
-void vp9_iht8x8_64_add_neon(const tran_low_t* input,
-                            uint8_t* dest,
-                            int stride,
-                            int tx_type);
-#define vp9_iht8x8_64_add vp9_iht8x8_64_add_neon
+#define vp9_iht8x8_64_add vp9_iht8x8_64_add_c
 
 void vp9_quantize_fp_c(const tran_low_t* coeff_ptr,
                        intptr_t n_coeffs,
diff --git a/third_party/libvpx/source/config/vpx_version.h b/third_party/libvpx/source/config/vpx_version.h
index 38cedd0..c49d1dd 100644
--- a/third_party/libvpx/source/config/vpx_version.h
+++ b/third_party/libvpx/source/config/vpx_version.h
@@ -2,7 +2,7 @@
 #define VERSION_MAJOR  1
 #define VERSION_MINOR  7
 #define VERSION_PATCH  0
-#define VERSION_EXTRA  "110-gedc9a4687"
+#define VERSION_EXTRA  "133-gc6fcb9bb9"
 #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH))
-#define VERSION_STRING_NOSP "v1.7.0-110-gedc9a4687"
-#define VERSION_STRING      " v1.7.0-110-gedc9a4687"
+#define VERSION_STRING_NOSP "v1.7.0-133-gc6fcb9bb9"
+#define VERSION_STRING      " v1.7.0-133-gc6fcb9bb9"
diff --git a/tools/battor_agent/battor_agent.cc b/tools/battor_agent/battor_agent.cc
index cce7dae..abc54c2 100644
--- a/tools/battor_agent/battor_agent.cc
+++ b/tools/battor_agent/battor_agent.cc
@@ -119,7 +119,12 @@
   last_clock_sync_time_ = base::TimeTicks();
 
   command_ = Command::START_TRACING;
-  PerformAction(Action::REQUEST_CONNECTION);
+
+  if (connection_->IsOpen()) {
+    PerformAction(GetFirstAction(Command::START_TRACING));
+  } else {
+    PerformAction(Action::REQUEST_CONNECTION);
+  }
 }
 
 void BattOrAgent::StopTracing() {
@@ -128,7 +133,12 @@
   connection_->LogSerial("Starting command StopTracing.");
 
   command_ = Command::STOP_TRACING;
-  PerformAction(Action::REQUEST_CONNECTION);
+
+  if (connection_->IsOpen()) {
+    PerformAction(GetFirstAction(Command::STOP_TRACING));
+  } else {
+    PerformAction(Action::REQUEST_CONNECTION);
+  }
 }
 
 void BattOrAgent::RecordClockSyncMarker(const std::string& marker) {
@@ -138,7 +148,12 @@
 
   command_ = Command::RECORD_CLOCK_SYNC_MARKER;
   pending_clock_sync_marker_ = marker;
-  PerformAction(Action::REQUEST_CONNECTION);
+
+  if (connection_->IsOpen()) {
+    PerformAction(GetFirstAction(Command::RECORD_CLOCK_SYNC_MARKER));
+  } else {
+    PerformAction(Action::REQUEST_CONNECTION);
+  }
 }
 
 void BattOrAgent::GetFirmwareGitHash() {
@@ -147,7 +162,12 @@
   connection_->LogSerial("Starting command GetFirmwareGitHash.");
 
   command_ = Command::GET_FIRMWARE_GIT_HASH;
-  PerformAction(Action::REQUEST_CONNECTION);
+
+  if (connection_->IsOpen()) {
+    PerformAction(GetFirstAction(Command::GET_FIRMWARE_GIT_HASH));
+  } else {
+    PerformAction(Action::REQUEST_CONNECTION);
+  }
 }
 
 void BattOrAgent::BeginConnect() {
@@ -172,23 +192,7 @@
   }
 
   if (last_action_ == Action::POST_CONNECT_FLUSH) {
-    switch (command_) {
-      case Command::START_TRACING:
-        PerformAction(Action::SEND_INIT);
-        return;
-      case Command::STOP_TRACING:
-        PerformAction(Action::SEND_EEPROM_REQUEST);
-        return;
-      case Command::RECORD_CLOCK_SYNC_MARKER:
-        PerformAction(Action::SEND_CURRENT_SAMPLE_REQUEST);
-        return;
-      case Command::GET_FIRMWARE_GIT_HASH:
-        PerformAction(Action::SEND_GIT_HASH_REQUEST);
-        return;
-      case Command::INVALID:
-        NOTREACHED();
-        return;
-    }
+    PerformAction(GetFirstAction(command_));
   } else if (last_action_ == Action::POST_READ_ERROR_FLUSH) {
     base::TimeDelta request_samples_delay =
         base::TimeDelta::FromMilliseconds(kFrameRetryDelayMilliseconds);
@@ -744,4 +748,21 @@
       base::TimeDelta::FromSeconds(timeout_seconds));
 }
 
+BattOrAgent::Action BattOrAgent::GetFirstAction(BattOrAgent::Command command) {
+  switch (command_) {
+    case Command::START_TRACING:
+      return Action::SEND_INIT;
+    case Command::STOP_TRACING:
+      return Action::SEND_EEPROM_REQUEST;
+    case Command::RECORD_CLOCK_SYNC_MARKER:
+      return Action::SEND_CURRENT_SAMPLE_REQUEST;
+    case Command::GET_FIRMWARE_GIT_HASH:
+      return Action::SEND_GIT_HASH_REQUEST;
+    case Command::INVALID:
+      NOTREACHED();
+  }
+
+  return Action::INVALID;
+}
+
 }  // namespace battor
diff --git a/tools/battor_agent/battor_agent.h b/tools/battor_agent/battor_agent.h
index eb3e2ca..0c95664 100644
--- a/tools/battor_agent/battor_agent.h
+++ b/tools/battor_agent/battor_agent.h
@@ -186,6 +186,10 @@
   // Sets and restarts the action timeout timer.
   void SetActionTimeout(uint16_t timeout_seconds);
 
+  // Returns the first action for the specified command (excluding connection
+  // actions).
+  Action GetFirstAction(Command command);
+
   // The listener that handles the commands' results. It must outlive the agent.
   Listener* listener_;
 
diff --git a/tools/battor_agent/battor_agent_unittest.cc b/tools/battor_agent/battor_agent_unittest.cc
index a1f48cb..cb37e2f 100644
--- a/tools/battor_agent/battor_agent_unittest.cc
+++ b/tools/battor_agent/battor_agent_unittest.cc
@@ -191,14 +191,16 @@
 
   // Runs BattOrAgent::StartTracing until it reaches the specified state by
   // feeding it the callbacks it needs to progress.
-  void RunStartTracingTo(BattOrAgentState end_state) {
+  void RunStartTracingTo(BattOrAgentState end_state, bool connect) {
     GetTaskRunner()->RunUntilIdle();
 
-    GetAgent()->OnConnectionOpened(true);
-    GetTaskRunner()->RunUntilIdle();
+    if (connect) {
+      GetAgent()->OnConnectionOpened(true);
+      GetTaskRunner()->RunUntilIdle();
 
-    GetAgent()->OnConnectionFlushed(true);
-    GetTaskRunner()->RunUntilIdle();
+      GetAgent()->OnConnectionFlushed(true);
+      GetTaskRunner()->RunUntilIdle();
+    }
 
     if (end_state == BattOrAgentState::CONNECTED)
       return;
@@ -235,13 +237,15 @@
 
   // Runs BattOrAgent::StopTracing until it reaches the specified state by
   // feeding it the callbacks it needs to progress.
-  void RunStopTracingTo(BattOrAgentState end_state) {
+  void RunStopTracingTo(BattOrAgentState end_state, bool connect) {
     GetTaskRunner()->RunUntilIdle();
 
-    GetAgent()->OnConnectionOpened(true);
-    GetTaskRunner()->RunUntilIdle();
+    if (connect) {
+      GetAgent()->OnConnectionOpened(true);
+      GetTaskRunner()->RunUntilIdle();
 
-    OnConnectionFlushed(true);
+      OnConnectionFlushed(true);
+    }
 
     if (end_state == BattOrAgentState::CONNECTED)
       return;
@@ -286,13 +290,15 @@
 
   // Runs BattOrAgent::RecordClockSyncMarker until it reaches the specified
   // state by feeding it the callbacks it needs to progress.
-  void RunRecordClockSyncMarkerTo(BattOrAgentState end_state) {
+  void RunRecordClockSyncMarkerTo(BattOrAgentState end_state, bool connect) {
     GetTaskRunner()->RunUntilIdle();
 
-    GetAgent()->OnConnectionOpened(true);
-    GetTaskRunner()->RunUntilIdle();
+    if (connect) {
+      GetAgent()->OnConnectionOpened(true);
+      GetTaskRunner()->RunUntilIdle();
 
-    OnConnectionFlushed(true);
+      OnConnectionFlushed(true);
+    }
 
     if (end_state == BattOrAgentState::CONNECTED)
       return;
@@ -310,13 +316,15 @@
 
   // Runs BattOrAgent::GetFirmwareGitHash until it reaches the specified
   // state by feeding it the callbacks it needs to progress.
-  void RunGetFirmwareGitHashTo(BattOrAgentState end_state) {
+  void RunGetFirmwareGitHashTo(BattOrAgentState end_state, bool connect) {
     GetTaskRunner()->RunUntilIdle();
 
-    GetAgent()->OnConnectionOpened(true);
-    GetTaskRunner()->RunUntilIdle();
+    if (connect) {
+      GetAgent()->OnConnectionOpened(true);
+      GetTaskRunner()->RunUntilIdle();
 
-    OnConnectionFlushed(true);
+      OnConnectionFlushed(true);
+    }
 
     if (end_state == BattOrAgentState::CONNECTED)
       return;
@@ -397,7 +405,7 @@
               ReadMessage(BATTOR_MESSAGE_TYPE_CONTROL_ACK));
 
   GetAgent()->StartTracing();
-  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE);
+  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE, true);
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
 }
@@ -415,7 +423,7 @@
 
 TEST_F(BattOrAgentTest, StartTracingFailsIfInitSendFails) {
   GetAgent()->StartTracing();
-  RunStartTracingTo(BattOrAgentState::CONNECTED);
+  RunStartTracingTo(BattOrAgentState::CONNECTED, true);
   OnBytesSent(false);
 
   EXPECT_TRUE(IsCommandComplete());
@@ -425,12 +433,12 @@
 TEST_F(BattOrAgentTest, StartTracingSucceedsAfterInitAckReadFails) {
   GetAgent()->StartTracing();
 
-  RunStartTracingTo(BattOrAgentState::INIT_SENT);
+  RunStartTracingTo(BattOrAgentState::INIT_SENT, true);
   OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
 
   EXPECT_FALSE(IsCommandComplete());
 
-  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE);
+  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE, true);
 
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
@@ -439,13 +447,13 @@
 TEST_F(BattOrAgentTest, StartTracingSucceedsAfterInitWrongAckRead) {
   GetAgent()->StartTracing();
 
-  RunStartTracingTo(BattOrAgentState::INIT_SENT);
+  RunStartTracingTo(BattOrAgentState::INIT_SENT, true);
   OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
                 ToCharVector(kStartTracingAck));
 
   EXPECT_FALSE(IsCommandComplete());
 
-  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE);
+  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE, true);
 
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
@@ -454,7 +462,7 @@
 TEST_F(BattOrAgentTest, StartTracingFailsAfterSetGainSendFails) {
   GetAgent()->StartTracing();
 
-  RunStartTracingTo(BattOrAgentState::INIT_SENT);
+  RunStartTracingTo(BattOrAgentState::INIT_SENT, true);
   OnBytesSent(false);
 
   EXPECT_TRUE(IsCommandComplete());
@@ -464,12 +472,12 @@
 TEST_F(BattOrAgentTest, StartTracingSucceedsAfterSetGainAckReadFails) {
   GetAgent()->StartTracing();
 
-  RunStartTracingTo(BattOrAgentState::SET_GAIN_SENT);
+  RunStartTracingTo(BattOrAgentState::SET_GAIN_SENT, true);
   OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
 
   EXPECT_FALSE(IsCommandComplete());
 
-  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE);
+  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE, true);
 
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
@@ -478,13 +486,13 @@
 TEST_F(BattOrAgentTest, StartTracingSucceedsAfterSetGainWrongAckRead) {
   GetAgent()->StartTracing();
 
-  RunStartTracingTo(BattOrAgentState::SET_GAIN_SENT);
+  RunStartTracingTo(BattOrAgentState::SET_GAIN_SENT, true);
   OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
                 ToCharVector(kStartTracingAck));
 
   EXPECT_FALSE(IsCommandComplete());
 
-  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE);
+  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE, true);
 
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
@@ -492,7 +500,7 @@
 
 TEST_F(BattOrAgentTest, StartTracingFailsIfStartTracingSendFails) {
   GetAgent()->StartTracing();
-  RunStartTracingTo(BattOrAgentState::INIT_SENT);
+  RunStartTracingTo(BattOrAgentState::INIT_SENT, true);
   OnBytesSent(false);
 
   EXPECT_TRUE(IsCommandComplete());
@@ -504,12 +512,12 @@
 
   // Go through the correct init sequence, but give the wrong ack to
   // START_TRACING.
-  RunStartTracingTo(BattOrAgentState::START_TRACING_SENT);
+  RunStartTracingTo(BattOrAgentState::START_TRACING_SENT, true);
   OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK, ToCharVector(kInitAck));
 
   EXPECT_FALSE(IsCommandComplete());
 
-  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE);
+  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE, true);
 
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
@@ -520,13 +528,13 @@
 
   // Go through the correct init sequence, but indicate that we failed to read
   // the START_TRACING ack.
-  RunStartTracingTo(BattOrAgentState::START_TRACING_SENT);
+  RunStartTracingTo(BattOrAgentState::START_TRACING_SENT, true);
   OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
 
   EXPECT_FALSE(IsCommandComplete());
 
   // On the last attempt, give the correct ack to START_TRACING.
-  RunStartTracingTo(BattOrAgentState::START_TRACING_SENT);
+  RunStartTracingTo(BattOrAgentState::START_TRACING_SENT, true);
   OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
                 ToCharVector(kStartTracingAck));
 
@@ -537,7 +545,7 @@
 TEST_F(BattOrAgentTest, StartTracingSucceedsAfterSamplesReadDuringInit) {
   GetAgent()->StartTracing();
 
-  RunStartTracingTo(BattOrAgentState::INIT_SENT);
+  RunStartTracingTo(BattOrAgentState::INIT_SENT, true);
 
   // Send some samples instead of an INIT ACK. This will force a command retry.
   BattOrFrameHeader frame_header{1, 3 * sizeof(RawBattOrSample)};
@@ -549,7 +557,7 @@
 
   EXPECT_FALSE(IsCommandComplete());
 
-  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE);
+  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE, true);
 
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
@@ -557,17 +565,17 @@
 
 TEST_F(BattOrAgentTest, StartTracingFailsAfterTooManyCumulativeFailures) {
   GetAgent()->StartTracing();
-  RunStartTracingTo(BattOrAgentState::SET_GAIN_SENT);
+  RunStartTracingTo(BattOrAgentState::SET_GAIN_SENT, true);
 
   for (int i = 0; i < 9; i++) {
     OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
     AdvanceTickClock(base::TimeDelta::FromSeconds(2));
-    RunStartTracingTo(BattOrAgentState::SET_GAIN_SENT);
+    RunStartTracingTo(BattOrAgentState::SET_GAIN_SENT, true);
 
     EXPECT_FALSE(IsCommandComplete());
   }
 
-  RunStartTracingTo(BattOrAgentState::SET_GAIN_SENT);
+  RunStartTracingTo(BattOrAgentState::SET_GAIN_SENT, true);
   OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
 
   EXPECT_TRUE(IsCommandComplete());
@@ -576,13 +584,23 @@
 
 TEST_F(BattOrAgentTest, StartTracingRestartsConnectionUponRetry) {
   GetAgent()->StartTracing();
-  RunStartTracingTo(BattOrAgentState::INIT_SENT);
+  RunStartTracingTo(BattOrAgentState::INIT_SENT, true);
 
   EXPECT_CALL(*GetAgent()->GetConnection(), Close());
 
   OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
 
-  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE);
+  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE, true);
+
+  EXPECT_TRUE(IsCommandComplete());
+  EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
+}
+
+TEST_F(BattOrAgentTest, StartTracingCanReuseExistingConnection) {
+  ON_CALL(*GetAgent()->GetConnection(), IsOpen()).WillByDefault(Return(true));
+
+  GetAgent()->StartTracing();
+  RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE, false);
 
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
@@ -649,7 +667,7 @@
               ReadMessage(BATTOR_MESSAGE_TYPE_SAMPLES));
 
   GetAgent()->StopTracing();
-  RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
+  RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT, true);
 
   // Send the calibration frame.
   BattOrFrameHeader cal_frame_header{0, 2 * sizeof(RawBattOrSample)};
@@ -703,7 +721,7 @@
 
 TEST_F(BattOrAgentTest, StopTracingFailsIfEEPROMRequestSendFails) {
   GetAgent()->StopTracing();
-  RunStopTracingTo(BattOrAgentState::CONNECTED);
+  RunStopTracingTo(BattOrAgentState::CONNECTED, true);
   OnBytesSent(false);
 
   EXPECT_TRUE(IsCommandComplete());
@@ -713,12 +731,12 @@
 TEST_F(BattOrAgentTest, StopTracingSucceedsAfterEEPROMReadFails) {
   GetAgent()->StopTracing();
 
-  RunStopTracingTo(BattOrAgentState::EEPROM_REQUEST_SENT);
+  RunStopTracingTo(BattOrAgentState::EEPROM_REQUEST_SENT, true);
   OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
 
   EXPECT_FALSE(IsCommandComplete());
 
-  RunStopTracingTo(BattOrAgentState::SAMPLES_END_FRAME_RECEIVED);
+  RunStopTracingTo(BattOrAgentState::SAMPLES_END_FRAME_RECEIVED, true);
 
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
@@ -727,12 +745,12 @@
 TEST_F(BattOrAgentTest, StopTracingSucceedsAfterEEPROMWrongAckRead) {
   GetAgent()->StopTracing();
 
-  RunStopTracingTo(BattOrAgentState::EEPROM_REQUEST_SENT);
+  RunStopTracingTo(BattOrAgentState::EEPROM_REQUEST_SENT, true);
   OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK, ToCharVector(kInitAck));
 
   EXPECT_FALSE(IsCommandComplete());
 
-  RunStopTracingTo(BattOrAgentState::SAMPLES_END_FRAME_RECEIVED);
+  RunStopTracingTo(BattOrAgentState::SAMPLES_END_FRAME_RECEIVED, true);
 
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
@@ -741,7 +759,7 @@
 TEST_F(BattOrAgentTest, StopTracingFailsIfSendamplesRequestFails) {
   GetAgent()->StopTracing();
 
-  RunStopTracingTo(BattOrAgentState::EEPROM_RECEIVED);
+  RunStopTracingTo(BattOrAgentState::EEPROM_RECEIVED, true);
   OnBytesSent(false);
 
   EXPECT_TRUE(IsCommandComplete());
@@ -751,7 +769,7 @@
 TEST_F(BattOrAgentTest, StopTracingSucceedsAfterCalibrationFrameReadFailure) {
   GetAgent()->StopTracing();
 
-  RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
+  RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT, true);
 
   // Make a read fail in order to make sure that the agent will retry the frame.
   OnMessageRead(false, BATTOR_MESSAGE_TYPE_SAMPLES, nullptr);
@@ -780,7 +798,7 @@
 TEST_F(BattOrAgentTest, StopTracingSucceedsAfterDataFrameReadFailure) {
   GetAgent()->StopTracing();
 
-  RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED);
+  RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED, true);
 
   // Make a read fail in order to make sure that the agent will retry.
   OnMessageRead(false, BATTOR_MESSAGE_TYPE_SAMPLES, nullptr);
@@ -803,7 +821,7 @@
 TEST_F(BattOrAgentTest, StopTracingFailsWithManyCalibrationFrameReadFailures) {
   GetAgent()->StopTracing();
 
-  RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
+  RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT, true);
 
   for (int i = 0; i < 9; i++) {
     OnMessageRead(false, BATTOR_MESSAGE_TYPE_SAMPLES, nullptr);
@@ -825,7 +843,7 @@
 TEST_F(BattOrAgentTest, StopTracingFailsWithManyDataFrameReadFailures) {
   GetAgent()->StopTracing();
 
-  RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED);
+  RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED, true);
 
   for (int i = 0; i < 9; i++) {
     OnMessageRead(false, BATTOR_MESSAGE_TYPE_SAMPLES, nullptr);
@@ -847,7 +865,7 @@
 TEST_F(BattOrAgentTest, StopTracingSucceedsWithFewDataFrameReadFailures) {
   GetAgent()->StopTracing();
 
-  RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED);
+  RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED, true);
 
   // Fail to receive first data frame.
   OnMessageRead(false, BATTOR_MESSAGE_TYPE_SAMPLES, nullptr);
@@ -890,7 +908,7 @@
 TEST_F(BattOrAgentTest, StopTracingSucceedsAfterSamplesReadHasWrongType) {
   GetAgent()->StopTracing();
 
-  RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED);
+  RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED, true);
 
   // Send the incorrect type of frame.
   OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK, ToCharVector(kInitAck));
@@ -914,7 +932,7 @@
 TEST_F(BattOrAgentTest, StopTracingSucceedsAfterCalibrationFrameWrongLength) {
   GetAgent()->StopTracing();
 
-  RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
+  RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT, true);
 
   // Send a calibration frame with a mismatch between the frame length in the
   // header and the actual frame length.
@@ -952,7 +970,7 @@
 TEST_F(BattOrAgentTest, StopTracingSucceedsAfterDataFrameHasWrongLength) {
   GetAgent()->StopTracing();
 
-  RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED);
+  RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED, true);
 
   // Send a data frame with a mismatch between the frame length in the
   // header and the actual frame length.
@@ -980,7 +998,7 @@
 TEST_F(BattOrAgentTest, StopTracingSucceedsAfterCalibrationFrameMissingByte) {
   GetAgent()->StopTracing();
 
-  RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
+  RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT, true);
 
   BattOrFrameHeader cal_frame_header_bad{0, 2 * sizeof(RawBattOrSample)};
   RawBattOrSample cal_frame_bad[] = {
@@ -1023,7 +1041,7 @@
 TEST_F(BattOrAgentTest, StopTracingSucceedsAfterDataFrameMissingByte) {
   GetAgent()->StopTracing();
 
-  RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED);
+  RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED, true);
 
   BattOrFrameHeader frame_header_bad{1, 1 * sizeof(RawBattOrSample)};
   RawBattOrSample frame_bad[] = {RawBattOrSample{1, 1}};
@@ -1054,7 +1072,7 @@
 TEST_F(BattOrAgentTest, StopTracingSucceedsAfterDataFrameArrivesOutOfOrder) {
   GetAgent()->StopTracing();
 
-  RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED);
+  RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED, true);
 
   // Frame with sequence number 1.
   BattOrFrameHeader frame_header1{1, 1 * sizeof(RawBattOrSample)};
@@ -1087,6 +1105,17 @@
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
 }
 
+TEST_F(BattOrAgentTest, StopTracingCanReuseExistingConnection) {
+  ON_CALL(*GetAgent()->GetConnection(), IsOpen()).WillByDefault(Return(true));
+
+  GetAgent()->StopTracing();
+
+  RunStopTracingTo(BattOrAgentState::SAMPLES_END_FRAME_RECEIVED, false);
+
+  EXPECT_TRUE(IsCommandComplete());
+  EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
+}
+
 TEST_F(BattOrAgentTest, RecordClockSyncMarker) {
   testing::InSequence s;
   EXPECT_CALL(*GetAgent()->GetConnection(), Open());
@@ -1104,7 +1133,7 @@
 
   GetAgent()->RecordClockSyncMarker(kClockSyncId);
   RunRecordClockSyncMarkerTo(
-      BattOrAgentState::RECORD_CLOCK_SYNC_MARKER_COMPLETE);
+      BattOrAgentState::RECORD_CLOCK_SYNC_MARKER_COMPLETE, true);
 
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
@@ -1114,7 +1143,8 @@
   // Record a clock sync marker that says CLOCK_SYNC_ID happened at sample #2.
   GetAgent()->RecordClockSyncMarker(kClockSyncId);
 
-  RunRecordClockSyncMarkerTo(BattOrAgentState::CURRENT_SAMPLE_REQUEST_SENT);
+  RunRecordClockSyncMarkerTo(BattOrAgentState::CURRENT_SAMPLE_REQUEST_SENT,
+                             true);
 
   uint32_t current_sample = 1;
   OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
@@ -1125,7 +1155,7 @@
 
   GetTaskRunner()->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
   GetAgent()->StopTracing();
-  RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
+  RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT, true);
 
   // Now run StopTracing, and make sure that CLOCK_SYNC_ID gets printed out with
   // sample #2 (including calibration frame samples).
@@ -1172,7 +1202,7 @@
 TEST_F(BattOrAgentTest, RecordClockSyncMarkerFailsIfSampleRequestSendFails) {
   GetAgent()->RecordClockSyncMarker(kClockSyncId);
 
-  RunRecordClockSyncMarkerTo(BattOrAgentState::CONNECTED);
+  RunRecordClockSyncMarkerTo(BattOrAgentState::CONNECTED, true);
   OnBytesSent(false);
 
   EXPECT_TRUE(IsCommandComplete());
@@ -1182,7 +1212,8 @@
 TEST_F(BattOrAgentTest, RecordClockSyncMarkerFailsIfCurrentSampleReadFails) {
   GetAgent()->RecordClockSyncMarker(kClockSyncId);
 
-  RunRecordClockSyncMarkerTo(BattOrAgentState::CURRENT_SAMPLE_REQUEST_SENT);
+  RunRecordClockSyncMarkerTo(BattOrAgentState::CURRENT_SAMPLE_REQUEST_SENT,
+                             true);
   OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
 
   EXPECT_TRUE(IsCommandComplete());
@@ -1193,7 +1224,8 @@
        RecordClockSyncMarkerFailsIfCurrentSampleReadHasWrongType) {
   GetAgent()->RecordClockSyncMarker(kClockSyncId);
 
-  RunRecordClockSyncMarkerTo(BattOrAgentState::CURRENT_SAMPLE_REQUEST_SENT);
+  RunRecordClockSyncMarkerTo(BattOrAgentState::CURRENT_SAMPLE_REQUEST_SENT,
+                             true);
 
   uint32_t current_sample = 1;
   OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL,
@@ -1203,10 +1235,22 @@
   EXPECT_EQ(BATTOR_ERROR_UNEXPECTED_MESSAGE, GetCommandError());
 }
 
+TEST_F(BattOrAgentTest, RecordClockSyncMarkerCanReuseExistingConnection) {
+  ON_CALL(*GetAgent()->GetConnection(), IsOpen()).WillByDefault(Return(true));
+
+  GetAgent()->RecordClockSyncMarker(kClockSyncId);
+
+  RunRecordClockSyncMarkerTo(
+      BattOrAgentState::RECORD_CLOCK_SYNC_MARKER_COMPLETE, false);
+
+  EXPECT_TRUE(IsCommandComplete());
+  EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
+}
+
 TEST_F(BattOrAgentTest, GetFirmwareGitHash) {
   GetAgent()->GetFirmwareGitHash();
 
-  RunGetFirmwareGitHashTo(BattOrAgentState::READ_GIT_HASH_RECEIVED);
+  RunGetFirmwareGitHashTo(BattOrAgentState::READ_GIT_HASH_RECEIVED, true);
 
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
@@ -1228,7 +1272,8 @@
 TEST_F(BattOrAgentTest, GetFirmwareGitHashSucceedsReadHasWrongType) {
   GetAgent()->GetFirmwareGitHash();
 
-  RunGetFirmwareGitHashTo(BattOrAgentState::GIT_FIRMWARE_HASH_REQUEST_SENT);
+  RunGetFirmwareGitHashTo(BattOrAgentState::GIT_FIRMWARE_HASH_REQUEST_SENT,
+                          true);
 
   uint32_t current_sample = 1;
   OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL,
@@ -1236,7 +1281,7 @@
 
   EXPECT_FALSE(IsCommandComplete());
 
-  RunGetFirmwareGitHashTo(BattOrAgentState::READ_GIT_HASH_RECEIVED);
+  RunGetFirmwareGitHashTo(BattOrAgentState::READ_GIT_HASH_RECEIVED, true);
 
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
@@ -1244,13 +1289,25 @@
 
 TEST_F(BattOrAgentTest, GetFirmwareRestartsConnectionUponRetry) {
   GetAgent()->GetFirmwareGitHash();
-  RunGetFirmwareGitHashTo(BattOrAgentState::GIT_FIRMWARE_HASH_REQUEST_SENT);
+  RunGetFirmwareGitHashTo(BattOrAgentState::GIT_FIRMWARE_HASH_REQUEST_SENT,
+                          true);
 
   EXPECT_CALL(*GetAgent()->GetConnection(), Close());
 
   OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
 
-  RunGetFirmwareGitHashTo(BattOrAgentState::READ_GIT_HASH_RECEIVED);
+  RunGetFirmwareGitHashTo(BattOrAgentState::READ_GIT_HASH_RECEIVED, true);
+
+  EXPECT_TRUE(IsCommandComplete());
+  EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
+}
+
+TEST_F(BattOrAgentTest, GetFirmwareGitHashCanReuseExistingConnection) {
+  ON_CALL(*GetAgent()->GetConnection(), IsOpen()).WillByDefault(Return(true));
+
+  GetAgent()->GetFirmwareGitHash();
+
+  RunGetFirmwareGitHashTo(BattOrAgentState::READ_GIT_HASH_RECEIVED, false);
 
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
diff --git a/tools/code_coverage/coverage.py b/tools/code_coverage/coverage.py
index 1e60818..1a2c86e 100755
--- a/tools/code_coverage/coverage.py
+++ b/tools/code_coverage/coverage.py
@@ -123,6 +123,10 @@
 # Used to extract a mapping between directories and components.
 COMPONENT_MAPPING_URL = 'https://storage.googleapis.com/chromium-owners/component_map.json'
 
+# Caches the results returned by _GetBuildArgs, don't use this variable
+# directly, call _GetBuildArgs instead.
+_BUILD_ARGS = None
+
 
 class _CoverageSummary(object):
   """Encapsulates coverage summary representation."""
@@ -326,7 +330,7 @@
 
   Returns an empty string is target_os is not specified.
   """
-  build_args = _ParseArgsGnFile()
+  build_args = _GetBuildArgs()
   return build_args['target_os'] if 'target_os' in build_args else ''
 
 
@@ -734,7 +738,7 @@
     Returns:
       A boolean indicates whether goma is configured for building or not.
     """
-    build_args = _ParseArgsGnFile()
+    build_args = _GetBuildArgs()
     return 'use_goma' in build_args and build_args['use_goma'] == 'true'
 
   logging.info('Building %s', str(targets))
@@ -981,9 +985,12 @@
   2. Use xvfb.
     2.1. "python testing/xvfb.py out/coverage/url_unittests <arguments>"
     2.2. "testing/xvfb.py out/coverage/url_unittests <arguments>"
-  3. Use iossim to run tests on iOS platform.
+  3. Use iossim to run tests on iOS platform, please refer to testing/iossim.mm
+    for its usage.
     3.1. "out/Coverage-iphonesimulator/iossim
-          out/Coverage-iphonesimulator/url_unittests.app <arguments>"
+          <iossim_arguments> -c <app_arguments>
+          out/Coverage-iphonesimulator/url_unittests.app"
+
 
   Args:
     command: A command used to run a target.
@@ -1005,7 +1012,7 @@
   if _IsIOSCommand(command):
     # For a given application bundle, the binary resides in the bundle and has
     # the same name with the application without the .app extension.
-    app_path = command_parts[1]
+    app_path = command_parts[-1].rstrip(os.path.sep)
     app_name = os.path.splitext(os.path.basename(app_path))[0]
     return os.path.join(app_path, app_name)
 
@@ -1030,7 +1037,7 @@
 
 def _ValidateBuildingWithClangCoverage():
   """Asserts that targets are built with Clang coverage enabled."""
-  build_args = _ParseArgsGnFile()
+  build_args = _GetBuildArgs()
 
   if (CLANG_COVERAGE_BUILD_ARG not in build_args or
       build_args[CLANG_COVERAGE_BUILD_ARG] != 'true'):
@@ -1051,19 +1058,23 @@
   ], ('Coverage is only supported on linux, mac, chromeos and ios.')
 
 
-def _ParseArgsGnFile():
+def _GetBuildArgs():
   """Parses args.gn file and returns results as a dictionary.
 
   Returns:
     A dictionary representing the build args.
   """
+  global _BUILD_ARGS
+  if _BUILD_ARGS is not None:
+    return _BUILD_ARGS
+
+  _BUILD_ARGS = {}
   build_args_path = os.path.join(BUILD_DIR, 'args.gn')
   assert os.path.exists(build_args_path), ('"%s" is not a build directory, '
                                            'missing args.gn file.' % BUILD_DIR)
   with open(build_args_path) as build_args_file:
     build_args_lines = build_args_file.readlines()
 
-  build_args = {}
   for build_arg_line in build_args_lines:
     build_arg_without_comments = build_arg_line.split('#')[0]
     key_value_pair = build_arg_without_comments.split('=')
@@ -1075,9 +1086,9 @@
     # Values are wrapped within a pair of double-quotes, so remove the leading
     # and trailing double-quotes.
     value = key_value_pair[1].strip().strip('"')
-    build_args[key] = value
+    _BUILD_ARGS[key] = value
 
-  return build_args
+  return _BUILD_ARGS
 
 
 def _VerifyPathsAndReturnAbsolutes(paths):
diff --git a/tools/fuchsia/local-sdk.py b/tools/fuchsia/local-sdk.py
index 7667caa..d9c842f4 100755
--- a/tools/fuchsia/local-sdk.py
+++ b/tools/fuchsia/local-sdk.py
@@ -31,8 +31,8 @@
     os.makedirs(path)
 
 
-def BuildForArch(project, arch):
-  Run('scripts/build-zircon.sh', '-p', project)
+def BuildForArch(arch):
+  Run('scripts/build-zircon.sh', '-t', arch)
   Run('build/gn/gen.py', '--target_cpu=' + arch,
       '--packages=garnet/packages/sdk', '--release')
   Run('buildtools/ninja', '-C', 'out/release-' + arch)
@@ -58,8 +58,8 @@
   # Switch to the Fuchsia tree and build an SDK.
   os.chdir(fuchsia_root)
 
-  BuildForArch('x86', 'x86-64')
-  BuildForArch('arm64', 'aarch64')
+  BuildForArch('x64')
+  BuildForArch('arm64')
 
   tempdir = tempfile.mkdtemp()
   sdk_tar = os.path.join(tempdir, 'fuchsia-sdk.tgz')
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index 499d041b..50d230a 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -103,41 +103,41 @@
     "structures": [12010],
   },
   "chrome/browser/resources/quota_internals_resources.grd": {
-    "includes": [12110],
+    "includes": [12210],
   },
   "chrome/browser/resources/settings/settings_resources_vulcanized.grd": {
-    "includes": [12130],
+    "includes": [12230],
   },
   "chrome/browser/resources/settings/settings_resources.grd": {
-    "structures": [12140],
+    "structures": [12240],
   },
   "chrome/browser/resources/sync_file_system_internals_resources.grd": {
-    "includes": [12640],
+    "includes": [12740],
   },
   "chrome/browser/resources/task_scheduler_internals/resources.grd": {
-    "includes": [12670],
+    "includes": [12770],
   },
   "chrome/browser/resources/translate_internals_resources.grd": {
-    "includes": [12680],
+    "includes": [12780],
   },
   "chrome/browser/resources/webapks_ui_resources.grd": {
-    "includes": [12700],
+    "includes": [12800],
   },
   "chrome/browser/vr/testapp/vr_testapp_resources.grd": {
-    "includes": [12710],
+    "includes": [12810],
   },
   # END chrome/browser section.
 
   # START chrome/ miscellaneous section.
   "chrome/common/common_resources.grd": {
-    "includes": [12750],
+    "includes": [12850],
   },
   "chrome/renderer/resources/renderer_resources.grd": {
-    "includes": [12760],
-    "structures": [12840],
+    "includes": [12860],
+    "structures": [12940],
   },
   "chrome/test/data/webui_test_resources.grd": {
-    "includes": [12850],
+    "includes": [12950],
   },
   # END chrome/ miscellaneous section.
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index e5105caef..2aed3cb8 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -32126,6 +32126,9 @@
   <int value="4" label="CCT"/>
   <int value="5" label="Clicking link"/>
   <int value="6" label="Reloading page"/>
+  <int value="7" label="Clicking notification"/>
+  <int value="8" label="File URL intent"/>
+  <int value="9" label="Content URL intent"/>
 </enum>
 
 <enum name="OfflinePagesAggregatedRequestResult">
@@ -37268,6 +37271,7 @@
   <int value="18" label="QUIC_STREAM_ACKED_UNSENT_FIN"/>
   <int value="19" label="QUIC_STREAM_ACKED_UNSENT_FIN"/>
   <int value="20" label="QUIC_STREAM_SEQUENCER_BUFFER"/>
+  <int value="21" label="QUIC_CHROMIUM_CLIENT_SESSION_CLOSE_SESSION_ON_ERROR"/>
 </enum>
 
 <enum name="QuickofficeErrorTypes">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index b7db1f6..9642738b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -57045,6 +57045,18 @@
   </summary>
 </histogram>
 
+<histogram name="OfflinePages.RequestJob.IntentDataChangedAfterValidation"
+    enum="BooleanChanged">
+  <owner>jianli@chromium.org</owner>
+  <summary>
+    Records whether the content read from intent URI (file:// or content://)
+    changed after the initial validation that was done to route the request when
+    the intent was received. This is recorded at the time that Offline Page
+    Request Handler tried to read the data, in preparation to show an offline
+    page to a user.
+  </summary>
+</histogram>
+
 <histogram name="OfflinePages.RequestJob.OpenFileErrorCode"
     enum="NetErrorCodes">
   <owner>jianli@chromium.org</owner>
@@ -69750,6 +69762,15 @@
   </summary>
 </histogram>
 
+<histogram name="Privacy.ConsentAuditor.UserEventServiceReady" enum="Boolean">
+  <owner>dullweber@google.com</owner>
+  <owner>msramek@google.com</owner>
+  <summary>
+    Logs whether the UserEventServiceReady is ready when recording a consent.
+    This should always be true as the service is created at startup.
+  </summary>
+</histogram>
+
 <histogram name="Process.Sandbox.FlagOverrodeRemoteSessionCheck" enum="Boolean">
   <owner>pastarmovj@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index a211404..2d7fdb7 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -633,6 +633,12 @@
     Metrics taken when a download completes. Its parent event is
     Download.Started.
   </summary>
+  <metric name="BytesWasted">
+    <summary>
+      The number of bytes that have been used in an excess (ie. download
+      restarting the middle of a download).
+    </summary>
+  </metric>
   <metric name="DownloadId">
     <summary>
       The id of the download that is used to associate separate events.
@@ -658,6 +664,12 @@
     Metrics taken when a download is interrupted. Its parent event is
     Download.Started.
   </summary>
+  <metric name="BytesWasted">
+    <summary>
+      The number of bytes that have been used in an excess (ie. download
+      restarting the middle of a download).
+    </summary>
+  </metric>
   <metric name="ChangeInFileSize">
     <summary>
       The difference in the size of the file that is downloaded compared to the
diff --git a/tools/traffic_annotation/auditor/BUILD.gn b/tools/traffic_annotation/auditor/BUILD.gn
index 7704bf9df..df52f36 100644
--- a/tools/traffic_annotation/auditor/BUILD.gn
+++ b/tools/traffic_annotation/auditor/BUILD.gn
@@ -86,7 +86,6 @@
     "traffic_annotation_auditor_unittest.cc",
   ]
   data = [
-    "../scripts/annotations_xml_downstream_caller.py",
     "../summary/annotations.xml",
     "tests/annotations_diff12.txt",
     "tests/annotations_diff13.txt",
diff --git a/tools/traffic_annotation/scripts/README.md b/tools/traffic_annotation/scripts/README.md
index 8124ddf..245670a 100644
--- a/tools/traffic_annotation/scripts/README.md
+++ b/tools/traffic_annotation/scripts/README.md
@@ -1,7 +1,2 @@
 # Traffic Annotation Scripts
 This file describes the scripts in `tools/traffic_annotation/scripts`
-
-# annotations_xml_downstream_caller.py
-This script calls all scripts that either update a file based on
-`annotations.xml`, or test if a file is in sync with it. Call with `test` switch
-for test mode.
diff --git a/ui/accessibility/ax_event_generator.cc b/ui/accessibility/ax_event_generator.cc
index 52747b1..9d0b644 100644
--- a/ui/accessibility/ax_event_generator.cc
+++ b/ui/accessibility/ax_event_generator.cc
@@ -411,7 +411,10 @@
         return;
 
       source_nodes.insert(source_node);
-      AddEvent(source_node, Event::RELATED_NODE_CHANGED);
+
+      // GCC < 6.4 requires this pointer when calling a member
+      // function in anonymous function
+      this->AddEvent(source_node, Event::RELATED_NODE_CHANGED);
     });
   };
 
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index 212265e..71ea29c 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -147,7 +147,7 @@
         gfx::Vector2d(output_size.width(), output_size.height()));
   }
 
-  support_->RequestCopyOfSurface(std::move(request));
+  support_->RequestCopyOfOutput(std::move(request));
 }
 
 bool DelegatedFrameHostAndroid::CanCopyFromCompositingSurface() const {
diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc
index 0d96c59fb..9b76e9f 100644
--- a/ui/app_list/views/app_list_view.cc
+++ b/ui/app_list/views/app_list_view.cc
@@ -524,6 +524,9 @@
 }
 
 void AppListView::HandleClickOrTap(ui::LocatedEvent* event) {
+  // Clear focus if the located event is not handled by any child view.
+  GetFocusManager()->ClearFocus();
+
   // No-op if app list is on fullscreen all apps state and the event location is
   // within apps grid view's bounds.
   if (app_list_state_ == AppListViewState::FULLSCREEN_ALL_APPS &&
diff --git a/ui/app_list/views/folder_header_view.cc b/ui/app_list/views/folder_header_view.cc
index 41860ae..9e36647 100644
--- a/ui/app_list/views/folder_header_view.cc
+++ b/ui/app_list/views/folder_header_view.cc
@@ -193,13 +193,6 @@
   folder_name_view_->SetBoundsRect(text_bounds);
 }
 
-bool FolderHeaderView::OnKeyPressed(const ui::KeyEvent& event) {
-  if (event.key_code() == ui::VKEY_RETURN)
-    delegate_->GiveBackFocusToSearchBox();
-
-  return false;
-}
-
 void FolderHeaderView::ContentsChanged(views::Textfield* sender,
                                        const base::string16& new_contents) {
   // Temporarily remove from observer to ignore data change caused by us.
@@ -221,6 +214,11 @@
 
 bool FolderHeaderView::HandleKeyEvent(views::Textfield* sender,
                                       const ui::KeyEvent& key_event) {
+  if (key_event.key_code() == ui::VKEY_RETURN &&
+      key_event.type() == ui::ET_KEY_PRESSED) {
+    delegate_->GiveBackFocusToSearchBox();
+    return true;
+  }
   if (!CanProcessLeftRightKeyTraversal(key_event))
     return false;
   return ProcessLeftRightKeyTraversalForTextfield(folder_name_view_, key_event);
diff --git a/ui/app_list/views/folder_header_view.h b/ui/app_list/views/folder_header_view.h
index 5d5cb2f..1073986 100644
--- a/ui/app_list/views/folder_header_view.h
+++ b/ui/app_list/views/folder_header_view.h
@@ -67,7 +67,6 @@
 
   // views::View overrides:
   void Layout() override;
-  bool OnKeyPressed(const ui::KeyEvent& event) override;
 
   // views::TextfieldController overrides:
   void ContentsChanged(views::Textfield* sender,
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn
index 56f8428..c72fd583 100644
--- a/ui/aura/BUILD.gn
+++ b/ui/aura/BUILD.gn
@@ -277,6 +277,7 @@
     "//base/test:test_support",
     "//cc:test_support",
     "//components/viz/test:test_support",
+    "//services/service_manager/public/cpp",
     "//services/ui/public/cpp/input_devices",
     "//services/ui/public/interfaces",
     "//skia",
diff --git a/ui/aura/event_injector.cc b/ui/aura/event_injector.cc
index f13e0674..d756e12 100644
--- a/ui/aura/event_injector.cc
+++ b/ui/aura/event_injector.cc
@@ -61,7 +61,7 @@
   }
   remote_event_dispatcher_->DispatchEvent(
       host->GetDisplayId(), MapEvent(*event),
-      base::Bind([](bool result) { DCHECK(result); }));
+      base::BindOnce([](bool result) { DCHECK(result); }));
   return ui::EventDispatchDetails();
 }
 
diff --git a/ui/aura/mus/window_port_mus.cc b/ui/aura/mus/window_port_mus.cc
index a6f7008..3f992885 100644
--- a/ui/aura/mus/window_port_mus.cc
+++ b/ui/aura/mus/window_port_mus.cc
@@ -96,11 +96,11 @@
   window_tree_client_->SetHitTestMask(this, rect);
 }
 
-void WindowPortMus::Embed(
-    ui::mojom::WindowTreeClientPtr client,
-    uint32_t flags,
-    const ui::mojom::WindowTree::EmbedCallback& callback) {
-  window_tree_client_->Embed(window_, std::move(client), flags, callback);
+void WindowPortMus::Embed(ui::mojom::WindowTreeClientPtr client,
+                          uint32_t flags,
+                          ui::mojom::WindowTree::EmbedCallback callback) {
+  window_tree_client_->Embed(window_, std::move(client), flags,
+                             std::move(callback));
 }
 
 std::unique_ptr<viz::ClientLayerTreeFrameSink>
diff --git a/ui/aura/mus/window_port_mus.h b/ui/aura/mus/window_port_mus.h
index 6689943..1f0e3dd 100644
--- a/ui/aura/mus/window_port_mus.h
+++ b/ui/aura/mus/window_port_mus.h
@@ -93,7 +93,7 @@
   // details on arguments.
   void Embed(ui::mojom::WindowTreeClientPtr client,
              uint32_t flags,
-             const ui::mojom::WindowTree::EmbedCallback& callback);
+             ui::mojom::WindowTree::EmbedCallback callback);
 
   std::unique_ptr<viz::ClientLayerTreeFrameSink> RequestLayerTreeFrameSink(
       scoped_refptr<viz::ContextProvider> context_provider,
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index d737bb0..1209491 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -337,11 +337,10 @@
   tree_->SetHitTestMask(window->server_id(), out_rect);
 }
 
-void WindowTreeClient::Embed(
-    Window* window,
-    ui::mojom::WindowTreeClientPtr client,
-    uint32_t flags,
-    const ui::mojom::WindowTree::EmbedCallback& callback) {
+void WindowTreeClient::Embed(Window* window,
+                             ui::mojom::WindowTreeClientPtr client,
+                             uint32_t flags,
+                             ui::mojom::WindowTree::EmbedCallback callback) {
   DCHECK(tree_);
   // Window::Init() must be called before Embed() (otherwise the server hasn't
   // been told about the window).
@@ -350,12 +349,12 @@
     // The window server removes all children before embedding. In other words,
     // it's generally an error to Embed() with existing children. So, fail
     // early.
-    callback.Run(false);
+    std::move(callback).Run(false);
     return;
   }
 
   tree_->Embed(WindowMus::Get(window)->server_id(), std::move(client), flags,
-               callback);
+               std::move(callback));
 }
 
 void WindowTreeClient::ScheduleEmbed(
@@ -1667,8 +1666,8 @@
                                    uint32_t key_state,
                                    const gfx::Point& position,
                                    uint32_t effect_bitmask,
-                                   const OnDragEnterCallback& callback) {
-  callback.Run(drag_drop_controller_->OnDragEnter(
+                                   OnDragEnterCallback callback) {
+  std::move(callback).Run(drag_drop_controller_->OnDragEnter(
       GetWindowByServerId(window_id), key_state, position, effect_bitmask));
 }
 
@@ -1676,8 +1675,8 @@
                                   uint32_t key_state,
                                   const gfx::Point& position,
                                   uint32_t effect_bitmask,
-                                  const OnDragOverCallback& callback) {
-  callback.Run(drag_drop_controller_->OnDragOver(
+                                  OnDragOverCallback callback) {
+  std::move(callback).Run(drag_drop_controller_->OnDragOver(
       GetWindowByServerId(window_id), key_state, position, effect_bitmask));
 }
 
@@ -1693,8 +1692,8 @@
                                       uint32_t key_state,
                                       const gfx::Point& position,
                                       uint32_t effect_bitmask,
-                                      const OnCompleteDropCallback& callback) {
-  callback.Run(drag_drop_controller_->OnCompleteDrop(
+                                      OnCompleteDropCallback callback) {
+  std::move(callback).Run(drag_drop_controller_->OnCompleteDrop(
       GetWindowByServerId(window_id), key_state, position, effect_bitmask));
 }
 
@@ -1946,16 +1945,11 @@
                                                drag_image_offset, source);
 }
 
-void WindowTreeClient::WmMoveDragImage(
-    const gfx::Point& screen_location,
-    const WmMoveDragImageCallback& callback) {
-  if (!window_manager_delegate_) {
-    callback.Run();
-    return;
-  }
-
-  window_manager_delegate_->OnWmMoveDragImage(screen_location);
-  callback.Run();
+void WindowTreeClient::WmMoveDragImage(const gfx::Point& screen_location,
+                                       WmMoveDragImageCallback callback) {
+  if (window_manager_delegate_)
+    window_manager_delegate_->OnWmMoveDragImage(screen_location);
+  std::move(callback).Run();
 }
 
 void WindowTreeClient::WmDestroyDragImage() {
diff --git a/ui/aura/mus/window_tree_client.h b/ui/aura/mus/window_tree_client.h
index decd26be..38492d13 100644
--- a/ui/aura/mus/window_tree_client.h
+++ b/ui/aura/mus/window_tree_client.h
@@ -165,7 +165,7 @@
   void Embed(Window* window,
              ui::mojom::WindowTreeClientPtr client,
              uint32_t flags,
-             const ui::mojom::WindowTree::EmbedCallback& callback);
+             ui::mojom::WindowTree::EmbedCallback callback);
 
   // Schedules an embed of a client. See
   // mojom::WindowTreeClient::ScheduleEmbed() for details.
@@ -453,18 +453,18 @@
                    uint32_t event_flags,
                    const gfx::Point& position,
                    uint32_t effect_bitmask,
-                   const OnDragEnterCallback& callback) override;
+                   OnDragEnterCallback callback) override;
   void OnDragOver(ui::Id window_id,
                   uint32_t event_flags,
                   const gfx::Point& position,
                   uint32_t effect_bitmask,
-                  const OnDragOverCallback& callback) override;
+                  OnDragOverCallback callback) override;
   void OnDragLeave(ui::Id window_id) override;
   void OnCompleteDrop(ui::Id window_id,
                       uint32_t event_flags,
                       const gfx::Point& position,
                       uint32_t effect_bitmask,
-                      const OnCompleteDropCallback& callback) override;
+                      OnCompleteDropCallback callback) override;
   void OnPerformDragDropCompleted(uint32_t change_id,
                                   bool success,
                                   uint32_t action_taken) override;
@@ -510,7 +510,7 @@
                         const gfx::Vector2d& drag_image_offset,
                         ui::mojom::PointerKind source) override;
   void WmMoveDragImage(const gfx::Point& screen_location,
-                       const WmMoveDragImageCallback& callback) override;
+                       WmMoveDragImageCallback callback) override;
   void WmDestroyDragImage() override;
   void WmPerformMoveLoop(uint32_t change_id,
                          ui::Id window_id,
diff --git a/ui/aura/test/mus/test_window_manager_client.cc b/ui/aura/test/mus/test_window_manager_client.cc
index 2cf51ab1..28f6675ff 100644
--- a/ui/aura/test/mus/test_window_manager_client.cc
+++ b/ui/aura/test/mus/test_window_manager_client.cc
@@ -43,7 +43,7 @@
 
 void TestWindowManagerClient::AddAccelerators(
     std::vector<ui::mojom::WmAcceleratorPtr> accelerators,
-    const AddAcceleratorsCallback& callback) {}
+    AddAcceleratorsCallback callback) {}
 
 void TestWindowManagerClient::RemoveAccelerator(uint32_t id) {}
 
@@ -56,7 +56,7 @@
     bool is_primary_display,
     ui::Id window_id,
     const std::vector<display::Display>& mirrors,
-    const SetDisplayRootCallback& callback) {}
+    SetDisplayRootCallback callback) {}
 
 void TestWindowManagerClient::SetDisplayConfiguration(
     const std::vector<display::Display>& displays,
@@ -64,7 +64,7 @@
     int64_t primary_display_id,
     int64_t internal_display_id,
     const std::vector<display::Display>& mirrors,
-    const SetDisplayConfigurationCallback& callback) {
+    SetDisplayConfigurationCallback callback) {
   last_internal_display_id_ = internal_display_id;
   changes_.push_back(WindowManagerClientChangeType::SET_DISPLAY_CONFIGURATION);
 }
@@ -72,11 +72,11 @@
 void TestWindowManagerClient::SwapDisplayRoots(
     int64_t display_id1,
     int64_t display_id2,
-    const SwapDisplayRootsCallback& callback) {}
+    SwapDisplayRootsCallback callback) {}
 
 void TestWindowManagerClient::SetBlockingContainers(
     std::vector<ui::mojom::BlockingContainersPtr> blocking_containers,
-    const SetBlockingContainersCallback& callback) {}
+    SetBlockingContainersCallback callback) {}
 
 void TestWindowManagerClient::WmResponse(uint32_t change_id, bool response) {}
 
diff --git a/ui/aura/test/mus/test_window_manager_client.h b/ui/aura/test/mus/test_window_manager_client.h
index 48f995c..66d4a85a 100644
--- a/ui/aura/test/mus/test_window_manager_client.h
+++ b/ui/aura/test/mus/test_window_manager_client.h
@@ -40,7 +40,7 @@
       const gfx::Insets& mouse_insets,
       const gfx::Insets& touch_insets) override;
   void AddAccelerators(std::vector<ui::mojom::WmAcceleratorPtr> accelerators,
-                       const AddAcceleratorsCallback& callback) override;
+                       AddAcceleratorsCallback callback) override;
   void RemoveAccelerator(uint32_t id) override;
   void SetKeyEventsThatDontHideCursor(
       std::vector<::ui::mojom::EventMatcherPtr> dont_hide_cursor_list) override;
@@ -49,20 +49,20 @@
                       bool is_primary_display,
                       ui::Id window_id,
                       const std::vector<display::Display>& mirrors,
-                      const SetDisplayRootCallback& callback) override;
+                      SetDisplayRootCallback callback) override;
   void SetDisplayConfiguration(
       const std::vector<display::Display>& displays,
       std::vector<::ui::mojom::WmViewportMetricsPtr> viewport_metrics,
       int64_t primary_display_id,
       int64_t internal_display_id,
       const std::vector<display::Display>& mirrors,
-      const SetDisplayConfigurationCallback& callback) override;
+      SetDisplayConfigurationCallback callback) override;
   void SwapDisplayRoots(int64_t display_id1,
                         int64_t display_id2,
-                        const SwapDisplayRootsCallback& callback) override;
+                        SwapDisplayRootsCallback callback) override;
   void SetBlockingContainers(
       std::vector<ui::mojom::BlockingContainersPtr> blocking_containers,
-      const SetBlockingContainersCallback& callback) override;
+      SetBlockingContainersCallback callback) override;
   void WmResponse(uint32_t change_id, bool response) override;
   void WmSetBoundsResponse(uint32_t change_id) override;
   void WmRequestClose(ui::Id transport_window_id) override;
diff --git a/ui/aura/test/mus/test_window_tree.cc b/ui/aura/test/mus/test_window_tree.cc
index 375460e9..e0cddb4d 100644
--- a/ui/aura/test/mus/test_window_tree.cc
+++ b/ui/aura/test/mus/test_window_tree.cc
@@ -253,7 +253,7 @@
 }
 
 void TestWindowTree::GetWindowTree(ui::Id window_id,
-                                   const GetWindowTreeCallback& callback) {}
+                                   GetWindowTreeCallback callback) {}
 
 void TestWindowTree::SetCapture(uint32_t change_id, ui::Id window_id) {
   OnChangeReceived(change_id, WindowTreeChangeType::CAPTURE);
@@ -270,15 +270,15 @@
 void TestWindowTree::Embed(ui::Id window_id,
                            ui::mojom::WindowTreeClientPtr client,
                            uint32_t flags,
-                           const EmbedCallback& callback) {}
+                           EmbedCallback callback) {}
 
 void TestWindowTree::ScheduleEmbed(ui::mojom::WindowTreeClientPtr client,
-                                   const ScheduleEmbedCallback& callback) {}
+                                   ScheduleEmbedCallback callback) {}
 
 void TestWindowTree::EmbedUsingToken(ui::Id window_id,
                                      const base::UnguessableToken& token,
                                      uint32_t embed_flags,
-                                     const EmbedUsingTokenCallback& callback) {}
+                                     EmbedUsingTokenCallback callback) {}
 
 void TestWindowTree::SetFocus(uint32_t change_id, ui::Id window_id) {
   OnChangeReceived(change_id, WindowTreeChangeType::FOCUS);
@@ -328,8 +328,8 @@
 }
 
 void TestWindowTree::GetCursorLocationMemory(
-    const GetCursorLocationMemoryCallback& callback) {
-  callback.Run(mojo::ScopedSharedBufferHandle());
+    GetCursorLocationMemoryCallback callback) {
+  std::move(callback).Run(mojo::ScopedSharedBufferHandle());
 }
 
 void TestWindowTree::PerformDragDrop(
diff --git a/ui/aura/test/mus/test_window_tree.h b/ui/aura/test/mus/test_window_tree.h
index f8257983..4140b95 100644
--- a/ui/aura/test/mus/test_window_tree.h
+++ b/ui/aura/test/mus/test_window_tree.h
@@ -187,8 +187,7 @@
                      ui::Id window_id,
                      ui::Id relative_window_id,
                      ui::mojom::OrderDirection direction) override;
-  void GetWindowTree(ui::Id window_id,
-                     const GetWindowTreeCallback& callback) override;
+  void GetWindowTree(ui::Id window_id, GetWindowTreeCallback callback) override;
   void SetCapture(uint32_t change_id, ui::Id window_id) override;
   void ReleaseCapture(uint32_t change_id, ui::Id window_id) override;
   void StartPointerWatcher(bool want_moves) override;
@@ -196,13 +195,13 @@
   void Embed(ui::Id window_id,
              ui::mojom::WindowTreeClientPtr client,
              uint32_t flags,
-             const EmbedCallback& callback) override;
+             EmbedCallback callback) override;
   void ScheduleEmbed(ui::mojom::WindowTreeClientPtr client,
-                     const ScheduleEmbedCallback& callback) override;
+                     ScheduleEmbedCallback callback) override;
   void EmbedUsingToken(ui::Id window_id,
                        const base::UnguessableToken& token,
                        uint32_t embed_flags,
-                       const EmbedUsingTokenCallback& callback) override;
+                       EmbedUsingTokenCallback callback) override;
   void SetFocus(uint32_t change_id, ui::Id window_id) override;
   void SetCanFocus(ui::Id window_id, bool can_focus) override;
   void SetEventTargetingPolicy(ui::Id window_id,
@@ -227,7 +226,7 @@
       mojo::AssociatedInterfaceRequest<ui::mojom::WindowManagerClient> internal)
       override;
   void GetCursorLocationMemory(
-      const GetCursorLocationMemoryCallback& callback) override;
+      GetCursorLocationMemoryCallback callback) override;
   void PerformDragDrop(
       uint32_t change_id,
       ui::Id source_window_id,
diff --git a/ui/aura/test/ui_controls_factory_ozone.cc b/ui/aura/test/ui_controls_factory_ozone.cc
index 416bbf6..cdd53a5 100644
--- a/ui/aura/test/ui_controls_factory_ozone.cc
+++ b/ui/aura/test/ui_controls_factory_ozone.cc
@@ -8,9 +8,14 @@
 #include "base/macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ui/public/interfaces/remote_event_dispatcher.mojom.h"
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/env.h"
+#include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/test/aura_test_utils.h"
+#include "ui/aura/test/env_test_helper.h"
 #include "ui/aura/test/ui_controls_factory_aura.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/test/ui_controls_aura.h"
@@ -21,6 +26,15 @@
 namespace test {
 namespace {
 
+// Callback from Window Service with the result of posting an event. |result|
+// is true if event successfully processed and |closure| is an optional closure
+// to run when done (used in client code to wait for ack).
+void OnWindowServiceProcessedEvent(base::OnceClosure closure, bool result) {
+  DCHECK(result);
+  if (closure)
+    std::move(closure).Run();
+}
+
 class UIControlsOzone : public ui_controls::UIControlsAura {
  public:
   UIControlsOzone(WindowTreeHost* host) : host_(host) {}
@@ -45,48 +59,60 @@
 
     if (control) {
       flags |= ui::EF_CONTROL_DOWN;
-      PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL, flags);
+      PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL, flags,
+                   base::OnceClosure());
     }
 
     if (shift) {
       flags |= ui::EF_SHIFT_DOWN;
-      PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, flags);
+      PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, flags,
+                   base::OnceClosure());
     }
 
     if (alt) {
       flags |= ui::EF_ALT_DOWN;
-      PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_MENU, flags);
+      PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_MENU, flags,
+                   base::OnceClosure());
     }
 
     if (command) {
       flags |= ui::EF_COMMAND_DOWN;
-      PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_LWIN, flags);
+      PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_LWIN, flags,
+                   base::OnceClosure());
     }
 
-    PostKeyEvent(ui::ET_KEY_PRESSED, key, flags);
-    PostKeyEvent(ui::ET_KEY_RELEASED, key, flags);
+    PostKeyEvent(ui::ET_KEY_PRESSED, key, flags, base::OnceClosure());
+    const bool has_modifier = control || shift || alt || command;
+    // Pass the real closure to the last generated KeyEvent.
+    PostKeyEvent(ui::ET_KEY_RELEASED, key, flags,
+                 has_modifier ? base::OnceClosure() : std::move(closure));
 
     if (alt) {
       flags &= ~ui::EF_ALT_DOWN;
-      PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_MENU, flags);
+      PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_MENU, flags,
+                   (shift || control || command) ? base::OnceClosure()
+                                                 : std::move(closure));
     }
 
     if (shift) {
       flags &= ~ui::EF_SHIFT_DOWN;
-      PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_SHIFT, flags);
+      PostKeyEvent(
+          ui::ET_KEY_RELEASED, ui::VKEY_SHIFT, flags,
+          (control || command) ? base::OnceClosure() : std::move(closure));
     }
 
     if (control) {
       flags &= ~ui::EF_CONTROL_DOWN;
-      PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL, flags);
+      PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL, flags,
+                   command ? base::OnceClosure() : std::move(closure));
     }
 
     if (command) {
       flags &= ~ui::EF_COMMAND_DOWN;
-      PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_LWIN, flags);
+      PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_LWIN, flags,
+                   std::move(closure));
     }
 
-    RunClosureAfterAllPendingUIEvents(std::move(closure));
     return true;
   }
 
@@ -114,9 +140,9 @@
     else
       event_type = ui::ET_MOUSE_MOVED;
 
-    PostMouseEvent(event_type, host_location, button_down_mask_, 0);
+    PostMouseEvent(event_type, host_location, button_down_mask_, 0,
+                   std::move(closure));
 
-    RunClosureAfterAllPendingUIEvents(std::move(closure));
     return true;
   }
   bool SendMouseEvents(ui_controls::MouseButton type, int state) override {
@@ -155,65 +181,91 @@
 
     if (state & ui_controls::DOWN) {
       button_down_mask_ |= flag;
-      PostMouseEvent(ui::ET_MOUSE_PRESSED, host_location,
-                     button_down_mask_ | flag, flag);
+      // Pass the real closure to the last generated MouseEvent.
+      PostMouseEvent(
+          ui::ET_MOUSE_PRESSED, host_location, button_down_mask_ | flag, flag,
+          (state & ui_controls::UP) ? base::OnceClosure() : std::move(closure));
     }
     if (state & ui_controls::UP) {
       button_down_mask_ &= ~flag;
       PostMouseEvent(ui::ET_MOUSE_RELEASED, host_location,
-                     button_down_mask_ | flag, flag);
+                     button_down_mask_ | flag, flag, std::move(closure));
     }
 
-    RunClosureAfterAllPendingUIEvents(std::move(closure));
     return true;
   }
   bool SendMouseClick(ui_controls::MouseButton type) override {
     return SendMouseEvents(type, ui_controls::UP | ui_controls::DOWN);
   }
-  void RunClosureAfterAllPendingUIEvents(base::OnceClosure closure) {
-    if (!closure.is_null())
-      base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                    std::move(closure));
-  }
 
  private:
-  void SendEventToSink(ui::Event* event) {
-    ui::EventSourceTestApi event_source_test(host_->GetEventSource());
-    ui::EventDispatchDetails details = event_source_test.SendEventToSink(event);
-    if (details.dispatcher_destroyed)
+  void SendEventToSink(ui::Event* event, base::OnceClosure closure) {
+    if (aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS) {
+      std::unique_ptr<ui::Event> event_to_send;
+      if (event->IsMouseEvent()) {
+        // WindowService expects MouseEvents as PointerEvents.
+        // See http://crbug.com/617222.
+        event_to_send =
+            std::make_unique<ui::PointerEvent>(*event->AsMouseEvent());
+      } else {
+        event_to_send = ui::Event::Clone(*event);
+      }
+
+      GetRemoteEventDispatcher()->DispatchEvent(
+          host_->GetDisplayId(), std::move(event_to_send),
+          base::BindOnce(&OnWindowServiceProcessedEvent, std::move(closure)));
       return;
+    }
+
+    // Post the task before processing the event. This is necessary in case
+    // processing the event results in a nested message loop.
+    if (closure) {
+      base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                    std::move(closure));
+    }
+
+    ui::EventSourceTestApi event_source_test(host_->GetEventSource());
+    ignore_result(event_source_test.SendEventToSink(event));
   }
 
-  void PostKeyEvent(ui::EventType type, ui::KeyboardCode key_code, int flags) {
+  void PostKeyEvent(ui::EventType type,
+                    ui::KeyboardCode key_code,
+                    int flags,
+                    base::OnceClosure closure) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(&UIControlsOzone::PostKeyEventTask,
-                              base::Unretained(this), type, key_code, flags));
+        FROM_HERE, base::BindOnce(&UIControlsOzone::PostKeyEventTask,
+                                  base::Unretained(this), type, key_code, flags,
+                                  std::move(closure)));
   }
 
   void PostKeyEventTask(ui::EventType type,
                         ui::KeyboardCode key_code,
-                        int flags) {
+                        int flags,
+                        base::OnceClosure closure) {
     // Do not rewrite injected events. See crbug.com/136465.
     flags |= ui::EF_FINAL;
 
     ui::KeyEvent key_event(type, key_code, flags);
-    SendEventToSink(&key_event);
+    SendEventToSink(&key_event, std::move(closure));
   }
 
   void PostMouseEvent(ui::EventType type,
                       const gfx::Point& host_location,
                       int flags,
-                      int changed_button_flags) {
+                      int changed_button_flags,
+                      base::OnceClosure closure) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::Bind(&UIControlsOzone::PostMouseEventTask, base::Unretained(this),
-                   type, host_location, flags, changed_button_flags));
+        base::BindOnce(&UIControlsOzone::PostMouseEventTask,
+                       base::Unretained(this), type, host_location, flags,
+                       changed_button_flags, std::move(closure)));
   }
 
   void PostMouseEventTask(ui::EventType type,
                           const gfx::Point& host_location,
                           int flags,
-                          int changed_button_flags) {
+                          int changed_button_flags,
+                          base::OnceClosure closure) {
     ui::MouseEvent mouse_event(type, host_location, host_location,
                                ui::EventTimeForNow(), flags,
                                changed_button_flags);
@@ -221,10 +273,25 @@
     // This hack is necessary to set the repeat count for clicks.
     ui::MouseEvent mouse_event2(&mouse_event);
 
-    SendEventToSink(&mouse_event2);
+    SendEventToSink(&mouse_event2, std::move(closure));
+  }
+
+  // Returns the ui::mojom::RemoteEventDispatcher, which is used to send events
+  // to the Window Service for dispatch.
+  ui::mojom::RemoteEventDispatcher* GetRemoteEventDispatcher() {
+    DCHECK_EQ(aura::Env::Mode::MUS, aura::Env::GetInstance()->mode());
+    if (!remote_event_dispatcher_) {
+      DCHECK(aura::test::EnvTestHelper().GetWindowTreeClient());
+      aura::test::EnvTestHelper()
+          .GetWindowTreeClient()
+          ->connector()
+          ->BindInterface(ui::mojom::kServiceName, &remote_event_dispatcher_);
+    }
+    return remote_event_dispatcher_.get();
   }
 
   WindowTreeHost* host_;
+  ui::mojom::RemoteEventDispatcherPtr remote_event_dispatcher_;
 
   // Mask of the mouse buttons currently down. This is static as it needs to
   // track the state globally for all displays. A UIControlsOzone instance is
diff --git a/ui/file_manager/file_manager/foreground/css/drive_welcome.css b/ui/file_manager/file_manager/foreground/css/drive_welcome.css
index f9715e1..f65f42b7 100644
--- a/ui/file_manager/file_manager/foreground/css/drive_welcome.css
+++ b/ui/file_manager/file_manager/foreground/css/drive_welcome.css
@@ -13,7 +13,7 @@
 
 .drive-welcome-links {
   flex: none;
-  margin-top: 6px;
+  margin: 6px 0;
 }
 
 .drive-welcome .imitate-paper-button {
@@ -28,7 +28,8 @@
 .drive-welcome.header {
   flex: none;
   height: 128px;
-  overflow: hidden;
+  overflow-x: hidden;
+  overflow-y: auto;
   position: relative;
   transition: height 180ms ease, visibility 0ms linear 180ms;
 }
@@ -39,13 +40,12 @@
 }
 
 .drive-welcome.header .drive-welcome-wrapper {
-  align-items: center;
   background-color: rgb(79, 129, 232);
-  bottom: 0;
   color: white;
   display: flex;
   flex-direction: row;
   left: 0;
+  padding-top: 21px;
   position: absolute;
   right: 0;
   top: 0;
@@ -58,15 +58,15 @@
   border-radius: 4px;
   flex: none;
   height: 78px;
-  margin: 25px;
+  /* Icon should be centered within 128px banner with 25px margin. */
+  /* 4px at top adds to 21px padding-top in drive-welcom-header to make 25px. */
+  margin: 4px 25px 0 25px;
   width: 78px;
 }
 
 .drive-welcome.header .drive-welcome-message {
   -webkit-margin-end: 50px;
   -webkit-margin-start: 20px;
-  display: flex;
-  flex-direction: column;
   z-index: 1;
 }
 
@@ -74,16 +74,10 @@
   font-size: 21px;
   font-weight: 500;
   margin-bottom: 4px;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
 }
 
 .drive-welcome.header .drive-welcome-text {
   font-size: 16px;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
 }
 
 .drive-welcome.header .drive-welcome-dismiss {
diff --git a/ui/shell_dialogs/base_shell_dialog_win.cc b/ui/shell_dialogs/base_shell_dialog_win.cc
index 9ccd1bb..ab670621 100644
--- a/ui/shell_dialogs/base_shell_dialog_win.cc
+++ b/ui/shell_dialogs/base_shell_dialog_win.cc
@@ -64,6 +64,7 @@
 // static
 base::Thread* BaseShellDialogImpl::CreateDialogThread() {
   base::Thread* thread = new base::Thread("Chrome_ShellDialogThread");
+  // Many shell dialogs require a COM Single-Threaded Apartment (STA) to run.
   thread->init_com_with_mta(false);
   bool started = thread->Start();
   DCHECK(started);
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 8d72c2a2..c16fa85 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -915,6 +915,7 @@
     "cocoa/drag_drop_client_mac_unittest.mm",
     "controls/button/blue_button_unittest.cc",
     "controls/button/button_unittest.cc",
+    "controls/button/checkbox_unittest.cc",
     "controls/button/image_button_factory_unittest.cc",
     "controls/button/image_button_unittest.cc",
     "controls/button/label_button_label_unittest.cc",
diff --git a/ui/views/controls/button/checkbox.h b/ui/views/controls/button/checkbox.h
index cec15a8..ca8da3cb 100644
--- a/ui/views/controls/button/checkbox.h
+++ b/ui/views/controls/button/checkbox.h
@@ -45,6 +45,9 @@
   // an accessible name can be used, e.g. a Label, StyledLabel or Link.
   void SetAssociatedLabel(View* labelling_view);
 
+  // LabelButton:
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+
  protected:
   // Returns whether MD is enabled. Returns true if |force_md| in the
   // constructor or --secondary-ui-md flag is set.
@@ -52,7 +55,6 @@
 
   // LabelButton:
   const char* GetClassName() const override;
-  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   void OnFocus() override;
   void OnBlur() override;
   void OnNativeThemeChanged(const ui::NativeTheme* theme) override;
diff --git a/ui/views/controls/button/checkbox_unittest.cc b/ui/views/controls/button/checkbox_unittest.cc
new file mode 100644
index 0000000..2984921c
--- /dev/null
+++ b/ui/views/controls/button/checkbox_unittest.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 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 "ui/views/controls/button/checkbox.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_node_data.h"
+#include "ui/views/controls/button/checkbox.h"
+#include "ui/views/controls/styled_label.h"
+#include "ui/views/test/views_test_base.h"
+
+namespace views {
+
+class CheckboxTest : public ViewsTestBase {
+ public:
+  CheckboxTest() {}
+  ~CheckboxTest() override {}
+
+  void SetUp() override {
+    ViewsTestBase::SetUp();
+
+    // Create a widget so that the Checkbox can query the hover state
+    // correctly.
+    widget_ = std::make_unique<Widget>();
+    Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
+    params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+    params.bounds = gfx::Rect(0, 0, 650, 650);
+    widget_->Init(params);
+    widget_->Show();
+
+    checkbox_ = new Checkbox(base::string16());
+    widget_->SetContentsView(checkbox_);
+  }
+
+  void TearDown() override {
+    widget_.reset();
+    ViewsTestBase::TearDown();
+  }
+
+ protected:
+  Checkbox* checkbox() { return checkbox_; }
+
+ private:
+  std::unique_ptr<Widget> widget_;
+  Checkbox* checkbox_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(CheckboxTest);
+};
+
+TEST_F(CheckboxTest, AccessibilityTest) {
+  const base::string16 label_text = base::ASCIIToUTF16("Some label");
+  StyledLabel label(label_text, nullptr);
+  checkbox()->SetAssociatedLabel(&label);
+
+  ui::AXNodeData ax_data;
+  checkbox()->GetAccessibleNodeData(&ax_data);
+
+  EXPECT_EQ(ax_data.GetString16Attribute(ax::mojom::StringAttribute::kName),
+            label_text);
+  EXPECT_EQ(ax_data.role, ax::mojom::Role::kCheckBox);
+}
+
+}  // namespace views
diff --git a/ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.js b/ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.js
index e8b2bba..5c3ad39d 100644
--- a/ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.js
+++ b/ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.js
@@ -13,6 +13,7 @@
  * Chrome OS only strings may be undefined.
  * @type {{
  *   controlledSettingExtension: string,
+ *   controlledSettingExtensionWithoutName: string,
  *   controlledSettingPolicy: string,
  *   controlledSettingRecommendedMatches: string,
  *   controlledSettingRecommendedDiffers: string,
@@ -120,7 +121,9 @@
       return '';  // Tooltips may not be defined, e.g. in OOBE.
     switch (type) {
       case CrPolicyIndicatorType.EXTENSION:
-        return CrPolicyStrings.controlledSettingExtension.replace('$1', name);
+        return name.length > 0 ?
+            CrPolicyStrings.controlledSettingExtension.replace('$1', name) :
+            CrPolicyStrings.controlledSettingExtensionWithoutName;
       case CrPolicyIndicatorType.PRIMARY_USER:
         return CrPolicyStrings.controlledSettingShared.replace('$1', name);
       case CrPolicyIndicatorType.OWNER: