diff --git a/DEPS b/DEPS
index 7730d80..816f86b 100644
--- a/DEPS
+++ b/DEPS
@@ -109,7 +109,7 @@
   # 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': '6d5c73cfe1b8791ea33a832a661142ddd5403ac5',
+  'v8_revision': 'b44922e76ed7f82598052e777de0da40bfe302b2',
   # 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.
@@ -129,7 +129,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': 'ce6a1cc587a7d5db7655a915dd18b1c1f0ef2de2',
+  'pdfium_revision': '367ed462b51799c008795b19e886ccbed221b9be',
   # 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.
@@ -579,7 +579,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '790b2751b5158c1b52500e04b93e2e525056566d',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '195b9510dbe50604b276b4170e30b9e7a299ba09',
       'condition': 'checkout_linux',
   },
 
@@ -604,7 +604,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'd06cc78ec8cc4d9b55fe3232b92d9066f5d776d7',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '7b7eb8800be040d4405fe1d04f002ad1f3a5a38f',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -813,7 +813,7 @@
     Var('chromium_git') + '/external/libaddressinput.git' + '@' + 'd7ed8e2f3f35ce9a3aafdfdc48745ceab66e7229',
 
   'src/third_party/libaom/source/libaom': {
-    'url': Var('aomedia_git') + '/aom.git' + '@' +  '7a76b645a08ce45ef52dfb7fd719a26c1af1da85',
+    'url': Var('aomedia_git') + '/aom.git' + '@' +  '10df8d1b586133d5c04ce8907cf2d23d43765521',
     'condition': 'checkout_libaom',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index d05280a..971e4ce 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -787,6 +787,7 @@
     "//ui/gl",
     "//ui/gl/init",
     "//ui/shell_dialogs",
+    "//url",
     "//v8",
   ]
 
diff --git a/android_webview/browser/aw_variations_service_client.cc b/android_webview/browser/aw_variations_service_client.cc
index fe14b44..7781ad84 100644
--- a/android_webview/browser/aw_variations_service_client.cc
+++ b/android_webview/browser/aw_variations_service_client.cc
@@ -48,7 +48,10 @@
 }
 
 Channel AwVariationsServiceClient::GetChannel() {
-  return android_webview::GetChannel();
+  // Pretend stand-alone WebView is always "stable" for the purpose of
+  // variations. This simplifies experiment design, since stand-alone WebView
+  // need not be considered separately when choosing channels.
+  return android_webview::GetChannelOrStable();
 }
 
 bool AwVariationsServiceClient::GetSupportsPermanentConsistency() {
diff --git a/android_webview/browser/aw_web_ui_controller_factory.cc b/android_webview/browser/aw_web_ui_controller_factory.cc
index 29db65e..876da06 100644
--- a/android_webview/browser/aw_web_ui_controller_factory.cc
+++ b/android_webview/browser/aw_web_ui_controller_factory.cc
@@ -8,6 +8,7 @@
 #include "components/safe_browsing/web_ui/constants.h"
 #include "components/safe_browsing/web_ui/safe_browsing_ui.h"
 #include "content/public/browser/web_ui.h"
+#include "url/gurl.h"
 
 using content::WebUI;
 using content::WebUIController;
diff --git a/android_webview/common/aw_channel.cc b/android_webview/common/aw_channel.cc
index 165f5e8..ece22c8 100644
--- a/android_webview/common/aw_channel.cc
+++ b/android_webview/common/aw_channel.cc
@@ -10,13 +10,8 @@
 
 using version_info::Channel;
 
-Channel GetChannel() {
+Channel GetChannelOrStable() {
   Channel channel = version_info::GetChannel();
-  // There are separate Monochrome APKs built for each channel, but only one
-  // stand-alone WebView APK for all channels, so stand-alone WebView has
-  // channel "unknown". Pretend stand-alone WebView is always "stable" for the
-  // purpose of variations. This simplifies experiment design, since stand-alone
-  // WebView need not be considered separately when choosing channels.
   return channel == Channel::UNKNOWN ? Channel::STABLE : channel;
 }
 
diff --git a/android_webview/common/aw_channel.h b/android_webview/common/aw_channel.h
index 318d6cb..c4b9278 100644
--- a/android_webview/common/aw_channel.h
+++ b/android_webview/common/aw_channel.h
@@ -9,7 +9,10 @@
 
 namespace android_webview {
 
-version_info::Channel GetChannel();
+// There are separate Monochrome APKs built for each channel, but only one
+// stand-alone WebView APK for all channels, so stand-alone WebView has channel
+// "unknown". Return the channel if it's known, or "stable" if it's "unknown".
+version_info::Channel GetChannelOrStable();
 
 }  // namespace android_webview
 
diff --git a/android_webview/common/crash_reporter/aw_crash_reporter_client.cc b/android_webview/common/crash_reporter/aw_crash_reporter_client.cc
index e77c26a5..f961f7d5 100644
--- a/android_webview/common/crash_reporter/aw_crash_reporter_client.cc
+++ b/android_webview/common/crash_reporter/aw_crash_reporter_client.cc
@@ -73,7 +73,8 @@
                                 std::string* channel) override {
     *product_name = "AndroidWebView";
     *version = PRODUCT_VERSION;
-    *channel = version_info::GetChannelString(android_webview::GetChannel());
+    *channel =
+        version_info::GetChannelString(android_webview::GetChannelOrStable());
   }
   // Microdumps are always enabled in WebView builds, conversely to what happens
   // in the case of the other Chrome for Android builds (where they are enabled
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 57cb1c1..8aae70bf 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -63,7 +63,6 @@
     "shell_init_params.h",
     "shell_observer.h",
     "sticky_keys/sticky_keys_controller.h",
-    "system/accessibility/select_to_speak_tray_utils.h",
     "system/status_area_widget.h",
     "system/status_area_widget_delegate.h",
     "system/system_tray_focus_observer.h",
@@ -638,7 +637,6 @@
     "system/accessibility/dictation_button_tray.h",
     "system/accessibility/select_to_speak_tray.cc",
     "system/accessibility/select_to_speak_tray.h",
-    "system/accessibility/select_to_speak_tray_utils.cc",
     "system/audio/audio_detailed_view.cc",
     "system/audio/audio_detailed_view.h",
     "system/audio/display_speaker_controller.cc",
@@ -1324,9 +1322,9 @@
     "//services/ui/common:mus_common",
     "//services/ui/public/cpp",
     "//services/ui/public/cpp/input_devices",
-    "//services/ui/public/interfaces",
     "//services/ui/ws2:host",
     "//services/ui/ws2:lib",
+    "//services/ws/public/mojom",
     "//skia",
     "//ui/aura",
     "//ui/events",
@@ -1343,7 +1341,6 @@
     "//ash/app_menu",
     "//ash/assistant/ui:constants",
     "//ash/autoclick/common:autoclick",
-    "//ash/components/autoclick/public/mojom",
     "//ash/components/cursor",
     "//ash/components/fast_ink",
     "//ash/components/quick_launch/public/mojom",
@@ -1392,7 +1389,7 @@
     "//services/service_manager/public/cpp",
     "//services/ui/gpu_host",
     "//services/ui/public/cpp/input_devices:input_device_controller",
-    "//services/ui/public/interfaces/input_devices",
+    "//services/ws/public/mojom/input_devices",
 
     # TODO(msw): Remove this; only ash_with_content should depend on webkit.
     "//ash/app_list/presenter",
@@ -1434,7 +1431,6 @@
   ]
 
   data_deps = [
-    "//ash/components/autoclick:autoclick_app",
     ":dbus_service_files",
   ]
 
@@ -1490,7 +1486,7 @@
     "//gpu/config",
     "//ipc",
     "//mojo/public/cpp/bindings",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//ui/aura",
     "//ui/base",
@@ -2031,8 +2027,8 @@
     "//services/catalog:lib",
     "//services/service_manager/public/cpp:service_test_support",
     "//services/ui/public/cpp/input_devices:test_support",
-    "//services/ui/public/interfaces",
     "//services/ui/ws2:test_support",
+    "//services/ws/public/mojom",
     "//skia",
     "//testing/gmock",
     "//testing/gtest",
@@ -2325,8 +2321,8 @@
     "//device/bluetooth",
     "//services/ui/public/cpp",
     "//services/ui/public/cpp/input_devices",
-    "//services/ui/public/interfaces",
     "//services/ui/ws2:test_support",
+    "//services/ws/public/mojom",
     "//skia",
     "//testing/gtest",
     "//ui/accessibility",
diff --git a/ash/DEPS b/ash/DEPS
index 70cb01f..743c6cfb 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -36,6 +36,7 @@
   "+services/ui/public",
   "+services/ui/ws2",
   "+services/viz/public",
+  "+services/ws/public",
   "+skia/ext",
   "+third_party/cros_system_api",
   "+third_party/icu",
@@ -52,7 +53,6 @@
   "-ash/components",
 
   # Ash can talk to public interfaces for mini-apps.
-  "+ash/components/autoclick/public",
   "+ash/components/quick_launch/public",
   "+ash/components/tap_visualizer/public",
 
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index cb8eb15..e1b8822 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -41,7 +41,7 @@
 #include "base/run_loop.h"
 #include "base/test/metrics/user_action_tester.h"
 #include "base/test/scoped_feature_list.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/test/test_windows.h"
diff --git a/ash/accelerators/accelerator_unittest.cc b/ash/accelerators/accelerator_unittest.cc
index 9ea43213..2bfbaa4 100644
--- a/ash/accelerators/accelerator_unittest.cc
+++ b/ash/accelerators/accelerator_unittest.cc
@@ -6,8 +6,8 @@
 #include "ash/accelerators/accelerator_controller.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
 #include "services/ui/ws2/test_window_tree_client.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/window.h"
 #include "ui/base/accelerators/test_accelerator_target.h"
 #include "ui/events/event.h"
diff --git a/ash/accessibility/accessibility_controller.cc b/ash/accessibility/accessibility_controller.cc
index 1e9825b..92157009 100644
--- a/ash/accessibility/accessibility_controller.cc
+++ b/ash/accessibility/accessibility_controller.cc
@@ -12,7 +12,6 @@
 #include "ash/accessibility/accessibility_observer.h"
 #include "ash/accessibility/accessibility_panel_layout_manager.h"
 #include "ash/autoclick/autoclick_controller.h"
-#include "ash/components/autoclick/public/mojom/autoclick.mojom.h"
 #include "ash/high_contrast/high_contrast_controller.h"
 #include "ash/policy/policy_recommendation_restorer.h"
 #include "ash/public/cpp/ash_pref_names.h"
@@ -33,8 +32,6 @@
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
-#include "mash/public/mojom/launchable.mojom.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "ui/aura/window.h"
 #include "ui/base/cursor/cursor_type.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -236,10 +233,8 @@
 
 }  // namespace
 
-AccessibilityController::AccessibilityController(
-    service_manager::Connector* connector)
-    : connector_(connector),
-      autoclick_delay_(AutoclickController::GetDefaultAutoclickDelay()) {
+AccessibilityController::AccessibilityController()
+    : autoclick_delay_(AutoclickController::GetDefaultAutoclickDelay()) {
   Shell::Get()->session_controller()->AddObserver(this);
   Shell::Get()->tablet_mode_controller()->AddObserver(this);
 }
@@ -858,15 +853,6 @@
 
   NotifyAccessibilityStatusChanged();
 
-  if (!features::IsAshInBrowserProcess()) {
-    if (!connector_)  // Null in tests.
-      return;
-    mash::mojom::LaunchablePtr launchable;
-    connector_->BindInterface("autoclick_app", &launchable);
-    launchable->Launch(mash::mojom::kWindow, mash::mojom::LaunchMode::DEFAULT);
-    return;
-  }
-
   Shell::Get()->autoclick_controller()->SetEnabled(enabled);
 }
 
@@ -879,15 +865,6 @@
     return;
   autoclick_delay_ = autoclick_delay;
 
-  if (!features::IsAshInBrowserProcess()) {
-    if (!connector_)  // Null in tests.
-      return;
-    autoclick::mojom::AutoclickControllerPtr autoclick_controller;
-    connector_->BindInterface("autoclick_app", &autoclick_controller);
-    autoclick_controller->SetAutoclickDelay(autoclick_delay_.InMilliseconds());
-    return;
-  }
-
   Shell::Get()->autoclick_controller()->SetAutoclickDelay(autoclick_delay_);
 }
 
diff --git a/ash/accessibility/accessibility_controller.h b/ash/accessibility/accessibility_controller.h
index 40e32cf..3ad2815 100644
--- a/ash/accessibility/accessibility_controller.h
+++ b/ash/accessibility/accessibility_controller.h
@@ -22,10 +22,6 @@
 class PrefRegistrySimple;
 class PrefService;
 
-namespace service_manager {
-class Connector;
-}
-
 namespace ash {
 
 class AccessibilityHighlightController;
@@ -45,7 +41,7 @@
       public SessionObserver,
       public TabletModeObserver {
  public:
-  explicit AccessibilityController(service_manager::Connector* connector);
+  AccessibilityController();
   ~AccessibilityController() override;
 
   // See Shell::RegisterProfilePrefs().
@@ -200,8 +196,6 @@
   void UpdateVirtualKeyboardFromPref();
   void UpdateAccessibilityHighlightingFromPrefs();
 
-  service_manager::Connector* connector_ = nullptr;
-
   // The pref service of the currently active user or the signin profile before
   // user logs in. Can be null in ash_unittests.
   PrefService* active_user_prefs_ = nullptr;
diff --git a/ash/app_launch_unittest.cc b/ash/app_launch_unittest.cc
index 89ac6d9..d298eb4 100644
--- a/ash/app_launch_unittest.cc
+++ b/ash/app_launch_unittest.cc
@@ -8,8 +8,8 @@
 #include "base/command_line.h"
 #include "base/run_loop.h"
 #include "services/service_manager/public/cpp/service_test.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/window_server_test.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/window_server_test.mojom.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/views/layout/layout_provider.h"
 
diff --git a/ash/app_list/BUILD.gn b/ash/app_list/BUILD.gn
index 83308a26..79edabd 100644
--- a/ash/app_list/BUILD.gn
+++ b/ash/app_list/BUILD.gn
@@ -111,8 +111,8 @@
     "//components/sync",
     "//mojo/public/cpp/bindings",
     "//services/ui/public/cpp",
-    "//services/ui/public/interfaces",
     "//services/ui/ws2/remote_view_host",
+    "//services/ws/public/mojom",
     "//skia",
     "//third_party/icu",
     "//ui/accessibility",
diff --git a/ash/app_list/DEPS b/ash/app_list/DEPS
index 8ba0ff8..ab59494d 100644
--- a/ash/app_list/DEPS
+++ b/ash/app_list/DEPS
@@ -4,6 +4,7 @@
   "+components/sync",
   "+mojo/public/cpp",
   "+services/ui/public",
+  "+services/ws/public",
   "+skia",
   "+third_party/google_toolbox_for_mac/src",
   "+third_party/skia",
diff --git a/ash/app_list/views/search_result_answer_card_view.cc b/ash/app_list/views/search_result_answer_card_view.cc
index f9cd6142..73072ed 100644
--- a/ash/app_list/views/search_result_answer_card_view.cc
+++ b/ash/app_list/views/search_result_answer_card_view.cc
@@ -16,8 +16,8 @@
 #include "ash/public/cpp/app_list/app_list_constants.h"
 #include "base/bind.h"
 #include "base/feature_list.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
 #include "services/ui/ws2/remote_view_host/server_remote_view_host.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/accessibility/ax_node.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/aura/window.h"
diff --git a/ash/ash_service.cc b/ash/ash_service.cc
index b36c92b4..722dfaa3 100644
--- a/ash/ash_service.cc
+++ b/ash/ash_service.cc
@@ -34,9 +34,9 @@
 #include "services/ui/gpu_host/gpu_host.h"
 #include "services/ui/public/cpp/gpu/gpu.h"
 #include "services/ui/public/cpp/input_devices/input_device_controller.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
 #include "services/ui/ws2/host_context_factory.h"
 #include "services/ui/ws2/window_service.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/ui_base_features.h"
diff --git a/ash/ash_service.h b/ash/ash_service.h
index 3d0db80..1d1bbd7 100644
--- a/ash/ash_service.h
+++ b/ash/ash_service.h
@@ -12,7 +12,7 @@
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/mojom/service_factory.mojom.h"
 #include "services/ui/gpu_host/gpu_host_delegate.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 
 namespace aura {
 class Env;
diff --git a/ash/ash_service_unittest.cc b/ash/ash_service_unittest.cc
index 5962523..d5cec7b 100644
--- a/ash/ash_service_unittest.cc
+++ b/ash/ash_service_unittest.cc
@@ -14,9 +14,9 @@
 #include "base/run_loop.h"
 #include "services/service_manager/public/cpp/service_test.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/env.h"
 #include "ui/aura/mus/property_converter.h"
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 8784db0..b45f2e0 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1672,9 +1672,6 @@
       <message name="IDS_ASH_ASH_SERVICE_NAME" desc="Name of the internal software service that manages windows. Appears in the task manager.">
         Window manager
       </message>
-      <message name="IDS_ASH_AUTOCLICK_APP_NAME" desc="Name of the accessibility app that automatically clicks the mouse when it stops moving. Appears in the task manager.">
-        Autoclick
-      </message>
       <!-- TODO(jamescook): Move next to font service code. -->
       <message name="IDS_ASH_FONT_SERVICE_NAME" desc="Name of the internal software service that provides fonts. Appears in the task manager.">
         Font service
diff --git a/ash/assistant/assistant_interaction_controller.cc b/ash/assistant/assistant_interaction_controller.cc
index 6695b9c8..cd18102 100644
--- a/ash/assistant/assistant_interaction_controller.cc
+++ b/ash/assistant/assistant_interaction_controller.cc
@@ -196,13 +196,6 @@
 
     assistant_interaction_model_.CommitPendingQuery();
     assistant_interaction_model_.SetMicState(MicState::kClosed);
-
-    if (!assistant::ui::kIsMotionSpecEnabled) {
-      // Clear the interaction to wipe the stage.
-      assistant_interaction_model_.ClearInteraction(
-          /*retain_committed_query=*/true,
-          /*retain_pending_query=*/false);
-    }
   }
 
   // Start caching a new Assistant response for the interaction.
@@ -315,13 +308,6 @@
   assistant_interaction_model_.SetPendingQuery(
       std::make_unique<AssistantVoiceQuery>(final_result));
   assistant_interaction_model_.CommitPendingQuery();
-
-  if (!assistant::ui::kIsMotionSpecEnabled) {
-    // Clear the interaction to wipe the stage.
-    assistant_interaction_model_.ClearInteraction(
-        /*retain_committed_query=*/true,
-        /*retain_pending_response=*/true);
-  }
 }
 
 void AssistantInteractionController::OnSpeechLevelUpdated(float speech_level) {
diff --git a/ash/assistant/ui/assistant_container_view.cc b/ash/assistant/ui/assistant_container_view.cc
index df9435ab..90648395 100644
--- a/ash/assistant/ui/assistant_container_view.cc
+++ b/ash/assistant/ui/assistant_container_view.cc
@@ -188,8 +188,8 @@
   const bool visible =
       assistant_controller_->ui_controller()->model()->visible();
 
-  // When visible with the motion spec enabled, size changes are animated.
-  if (visible && assistant::ui::kIsMotionSpecEnabled) {
+  // When visible, size changes are animated.
+  if (visible) {
     resize_animation_ = std::make_unique<gfx::SlideAnimation>(this);
     resize_animation_->SetSlideDuration(kResizeAnimationDurationMs);
 
diff --git a/ash/assistant/ui/assistant_main_view.cc b/ash/assistant/ui/assistant_main_view.cc
index 20c6b58..163145c 100644
--- a/ash/assistant/ui/assistant_main_view.cc
+++ b/ash/assistant/ui/assistant_main_view.cc
@@ -132,29 +132,29 @@
 void AssistantMainView::OnUiVisibilityChanged(bool visible,
                                               AssistantSource source) {
   if (visible) {
-    // When Assistant UI is shown and the motion spec is enabled, we animate in
-    // the appearance of the caption bar and dialog plate.
-    if (assistant::ui::kIsMotionSpecEnabled) {
-      using namespace assistant::util;
+    // When Assistant UI is shown, we animate in the appearance of the caption
+    // bar and dialog plate.
+    using assistant::util::CreateLayerAnimationSequence;
+    using assistant::util::CreateOpacityElement;
 
-      // Animate the caption bar from 0% to 100% opacity with delay.
-      caption_bar_->layer()->SetOpacity(0.f);
-      caption_bar_->layer()->GetAnimator()->StartAnimation(
-          CreateLayerAnimationSequence(
-              ui::LayerAnimationElement::CreatePauseElement(
-                  ui::LayerAnimationElement::AnimatableProperty::OPACITY,
-                  kCaptionBarAnimationFadeInDelay),
-              CreateOpacityElement(1.f, kCaptionBarAnimationFadeInDuration)));
+    // Animate the caption bar from 0% to 100% opacity with delay.
+    caption_bar_->layer()->SetOpacity(0.f);
+    caption_bar_->layer()->GetAnimator()->StartAnimation(
+        CreateLayerAnimationSequence(
+            ui::LayerAnimationElement::CreatePauseElement(
+                ui::LayerAnimationElement::AnimatableProperty::OPACITY,
+                kCaptionBarAnimationFadeInDelay),
+            CreateOpacityElement(1.f, kCaptionBarAnimationFadeInDuration)));
 
-      // Animate the dialog plate from 0% to 100% opacity with delay.
-      dialog_plate_->layer()->SetOpacity(0.f);
-      dialog_plate_->layer()->GetAnimator()->StartAnimation(
-          CreateLayerAnimationSequence(
-              ui::LayerAnimationElement::CreatePauseElement(
-                  ui::LayerAnimationElement::AnimatableProperty::OPACITY,
-                  kDialogPlateAnimationFadeInDelay),
-              CreateOpacityElement(1.f, kDialogPlateAnimationFadeInDuration)));
-    }
+    // Animate the dialog plate from 0% to 100% opacity with delay.
+    dialog_plate_->layer()->SetOpacity(0.f);
+    dialog_plate_->layer()->GetAnimator()->StartAnimation(
+        CreateLayerAnimationSequence(
+            ui::LayerAnimationElement::CreatePauseElement(
+                ui::LayerAnimationElement::AnimatableProperty::OPACITY,
+                kDialogPlateAnimationFadeInDelay),
+            CreateOpacityElement(1.f, kDialogPlateAnimationFadeInDuration)));
+
     return;
   }
 
diff --git a/ash/assistant/ui/assistant_ui_constants.h b/ash/assistant/ui/assistant_ui_constants.h
index 1fa8764..8349e30 100644
--- a/ash/assistant/ui/assistant_ui_constants.h
+++ b/ash/assistant/ui/assistant_ui_constants.h
@@ -29,11 +29,6 @@
 namespace assistant {
 namespace ui {
 
-// TODO(dmblack): Remove when enabling. This is used to gate aspects of the
-// motion spec which are being developed in pieces which should be enabled
-// together.
-constexpr bool kIsMotionSpecEnabled = true;
-
 // Returns the default font list for Assistant UI.
 const gfx::FontList& GetDefaultFontList();
 
diff --git a/ash/assistant/ui/main_stage/assistant_footer_view.cc b/ash/assistant/ui/main_stage/assistant_footer_view.cc
index deaa419..15db85a 100644
--- a/ash/assistant/ui/main_stage/assistant_footer_view.cc
+++ b/ash/assistant/ui/main_stage/assistant_footer_view.cc
@@ -105,6 +105,10 @@
 }
 
 void AssistantFooterView::OnVoiceInteractionSetupCompleted(bool completed) {
+  using assistant::util::CreateLayerAnimationSequence;
+  using assistant::util::CreateOpacityElement;
+  using assistant::util::StartLayerAnimationSequence;
+
   // When the consent state changes, we need to hide/show the appropriate views.
   views::View* hide_view =
       completed ? static_cast<views::View*>(opt_in_view_)
@@ -114,19 +118,6 @@
       completed ? static_cast<views::View*>(suggestion_container_)
                 : static_cast<views::View*>(opt_in_view_);
 
-  // When the motion spec is disabled, we don't animate the transition.
-  if (!assistant::ui::kIsMotionSpecEnabled) {
-    OnAnimationStarted(*animation_observer_);
-
-    hide_view->layer()->SetOpacity(0.f);
-    show_view->layer()->SetOpacity(1.f);
-
-    OnAnimationEnded(*animation_observer_);
-    return;
-  }
-
-  using namespace assistant::util;
-
   // Hide the view for the previous consent state by fading to 0% opacity.
   hide_view->layer()->GetAnimator()->StartAnimation(
       CreateLayerAnimationSequence(
diff --git a/ash/assistant/ui/main_stage/assistant_header_view.cc b/ash/assistant/ui/main_stage/assistant_header_view.cc
index 02e6ebbe..d9074cb 100644
--- a/ash/assistant/ui/main_stage/assistant_header_view.cc
+++ b/ash/assistant/ui/main_stage/assistant_header_view.cc
@@ -95,18 +95,6 @@
   AddChildView(molecule_icon_);
 }
 
-void AssistantHeaderView::OnCommittedQueryChanged(
-    const AssistantQuery& committed_query) {
-  if (!assistant::ui::kIsMotionSpecEnabled) {
-    layout_manager_->set_cross_axis_alignment(
-        views::BoxLayout::CrossAxisAlignment::CROSS_AXIS_ALIGNMENT_START);
-
-    // Force a layout/paint pass.
-    Layout();
-    SchedulePaint();
-  }
-}
-
 void AssistantHeaderView::OnResponseChanged(const AssistantResponse& response) {
   // We only handle the first response when animating the molecule icon. For
   // all subsequent responses the molecule icon remains unchanged.
@@ -115,10 +103,9 @@
 
   is_first_response_ = false;
 
-  if (!assistant::ui::kIsMotionSpecEnabled)
-    return;
-
-  using namespace assistant::util;
+  using assistant::util::CreateLayerAnimationSequence;
+  using assistant::util::CreateOpacityElement;
+  using assistant::util::CreateTransformElement;
 
   // The molecule icon will be animated from the center of its parent, to the
   // left hand side.
@@ -150,48 +137,40 @@
 void AssistantHeaderView::OnUiVisibilityChanged(bool visible,
                                                 AssistantSource source) {
   if (visible) {
-    // When Assistant UI is shown and the motion spec is enabled, we animate in
-    // the appearance of the molecule icon.
-    if (assistant::ui::kIsMotionSpecEnabled) {
-      using namespace assistant::util;
+    // When Assistant UI is shown, we animate in the appearance of the molecule
+    // icon.
+    using assistant::util::CreateLayerAnimationSequence;
+    using assistant::util::CreateOpacityElement;
+    using assistant::util::CreateTransformElement;
 
-      // We're going to animate the molecule icon up into position so we'll need
-      // to apply an initial transformation.
-      gfx::Transform transform;
-      transform.Translate(0, kAppearAnimationTranslationUpDip);
+    // We're going to animate the molecule icon up into position so we'll need
+    // to apply an initial transformation.
+    gfx::Transform transform;
+    transform.Translate(0, kAppearAnimationTranslationUpDip);
 
-      // Set up our pre-animation values.
-      molecule_icon_->layer()->SetOpacity(0.f);
-      molecule_icon_->layer()->SetTransform(transform);
+    // Set up our pre-animation values.
+    molecule_icon_->layer()->SetOpacity(0.f);
+    molecule_icon_->layer()->SetTransform(transform);
 
-      // Start animating molecule icon.
-      molecule_icon_->layer()->GetAnimator()->StartTogether(
-          {// Animate the transformation.
-           CreateLayerAnimationSequence(CreateTransformElement(
-               gfx::Transform(), kAppearAnimationTranslateUpDuration,
-               gfx::Tween::Type::FAST_OUT_SLOW_IN_2)),
-           // Animate the opacity to 100% with delay.
-           CreateLayerAnimationSequence(
-               ui::LayerAnimationElement::CreatePauseElement(
-                   ui::LayerAnimationElement::AnimatableProperty::OPACITY,
-                   kAppearAnimationFadeInDelay),
-               CreateOpacityElement(1.f, kAppearAnimationFadeInDuration))});
-    }
+    // Start animating molecule icon.
+    molecule_icon_->layer()->GetAnimator()->StartTogether(
+        {// Animate the transformation.
+         CreateLayerAnimationSequence(CreateTransformElement(
+             gfx::Transform(), kAppearAnimationTranslateUpDuration,
+             gfx::Tween::Type::FAST_OUT_SLOW_IN_2)),
+         // Animate the opacity to 100% with delay.
+         CreateLayerAnimationSequence(
+             ui::LayerAnimationElement::CreatePauseElement(
+                 ui::LayerAnimationElement::AnimatableProperty::OPACITY,
+                 kAppearAnimationFadeInDelay),
+             CreateOpacityElement(1.f, kAppearAnimationFadeInDuration))});
+
     return;
   }
 
   // When Assistant UI is hidden, we restore initial state for the next session.
   is_first_response_ = true;
 
-  if (!assistant::ui::kIsMotionSpecEnabled) {
-    layout_manager_->set_cross_axis_alignment(
-        views::BoxLayout::CrossAxisAlignment::CROSS_AXIS_ALIGNMENT_CENTER);
-
-    // Force a layout/paint pass.
-    Layout();
-    SchedulePaint();
-  }
-
   molecule_icon_->layer()->SetTransform(gfx::Transform());
 }
 
diff --git a/ash/assistant/ui/main_stage/assistant_header_view.h b/ash/assistant/ui/main_stage/assistant_header_view.h
index 3a2f8f9..8a4c7b2 100644
--- a/ash/assistant/ui/main_stage/assistant_header_view.h
+++ b/ash/assistant/ui/main_stage/assistant_header_view.h
@@ -33,7 +33,6 @@
   void ChildVisibilityChanged(views::View* child) override;
 
   // AssistantInteractionModelObserver:
-  void OnCommittedQueryChanged(const AssistantQuery& committed_query) override;
   void OnResponseChanged(const AssistantResponse& response) override;
 
   // AssistantUiModelObserver:
diff --git a/ash/assistant/ui/main_stage/assistant_main_stage.cc b/ash/assistant/ui/main_stage/assistant_main_stage.cc
index b0f3d25..cb94972 100644
--- a/ash/assistant/ui/main_stage/assistant_main_stage.cc
+++ b/ash/assistant/ui/main_stage/assistant_main_stage.cc
@@ -321,11 +321,28 @@
 }
 
 void AssistantMainStage::OnCommittedQueryChanged(const AssistantQuery& query) {
-  // When the motion spec is disabled and a query is committed, we...
-  if (!assistant::ui::kIsMotionSpecEnabled) {
-    // ...hide the greeting label...
-    greeting_label_->layer()->SetOpacity(0.f);
+  using assistant::util::CreateLayerAnimationSequence;
+  using assistant::util::CreateOpacityElement;
 
+  if (is_first_query_) {
+    // Hide the greeting label (for the first query)...
+    greeting_label_->layer()->GetAnimator()->StartAnimation(
+        CreateLayerAnimationSequence(
+            CreateOpacityElement(0.f, kGreetingAnimationFadeOutDuration)));
+  }
+
+  // ...and always show the progress indicator.
+  progress_indicator_->layer()->GetAnimator()->StartAnimation(
+      CreateLayerAnimationSequence(
+          // Delay...
+          ui::LayerAnimationElement::CreatePauseElement(
+              ui::LayerAnimationElement::AnimatableProperty::OPACITY,
+              kProgressAnimationFadeInDelay),
+          // ...then fade in.
+          CreateOpacityElement(1.f, kProgressAnimationFadeInDuration)));
+
+  // After the first query, the progress indicator should be left aligned.
+  if (!is_first_query_) {
     const int overlay_width = overlay_layout_container_->width();
     const int indicator_width = progress_indicator_->width();
     const int translation = -(overlay_width - indicator_width) / 2 +
@@ -334,43 +351,7 @@
     gfx::Transform transform;
     transform.Translate(translation, 0);
 
-    // ...and show the progress indicator, having translated it from being
-    // center aligned, to left aligned in its parent.
     progress_indicator_->layer()->SetTransform(transform);
-    progress_indicator_->layer()->SetOpacity(1.f);
-  } else {
-    // When the motion spec is enabled we...
-    using namespace assistant::util;
-
-    if (is_first_query_) {
-      // ...hide the greeting label (for the first query)...
-      greeting_label_->layer()->GetAnimator()->StartAnimation(
-          CreateLayerAnimationSequence(
-              CreateOpacityElement(0.f, kGreetingAnimationFadeOutDuration)));
-    }
-
-    // ...and always show the progress indicator.
-    progress_indicator_->layer()->GetAnimator()->StartAnimation(
-        CreateLayerAnimationSequence(
-            // Delay...
-            ui::LayerAnimationElement::CreatePauseElement(
-                ui::LayerAnimationElement::AnimatableProperty::OPACITY,
-                kProgressAnimationFadeInDelay),
-            // ...then fade in.
-            CreateOpacityElement(1.f, kProgressAnimationFadeInDuration)));
-
-    // After the first query, the progress indicator should be left aligned.
-    if (!is_first_query_) {
-      const int overlay_width = overlay_layout_container_->width();
-      const int indicator_width = progress_indicator_->width();
-      const int translation = -(overlay_width - indicator_width) / 2 +
-                              kProgressIndicatorMarginLeftDip;
-
-      gfx::Transform transform;
-      transform.Translate(translation, 0);
-
-      progress_indicator_->layer()->SetTransform(transform);
-    }
   }
 
   is_first_query_ = false;
@@ -381,15 +362,13 @@
 
   // Update the view.
   committed_query_view_->SetQuery(query);
-
-  // When the motion spec is disabled, we will immediately activate the
-  // committed query to prevent deviation from current UI behavior. When the
-  // motion spec is enabled, we activate the query upon receipt of the response.
-  if (!assistant::ui::kIsMotionSpecEnabled)
-    OnActivateQuery();
 }
 
 void AssistantMainStage::OnActivateQuery() {
+  using assistant::util::CreateLayerAnimationSequence;
+  using assistant::util::CreateOpacityElement;
+  using assistant::util::CreateTransformElement;
+
   // Clear the previously active query.
   OnActiveQueryCleared();
 
@@ -397,34 +376,28 @@
   committed_query_view_ = nullptr;
 
   // Upon response delivery, we consider a query active. The view for the query
-  // should move from the bottom of its parent to the top. This transition is
-  // animated when the motion spec is enabled.
-  if (!assistant::ui::kIsMotionSpecEnabled) {
-    active_query_view_->layer()->SetTransform(gfx::Transform());
-  } else {
-    using namespace assistant::util;
-    active_query_view_->layer()->GetAnimator()->StartTogether(
-        {// Animate transformation.
-         CreateLayerAnimationSequence(
-             // Pause...
-             ui::LayerAnimationElement::CreatePauseElement(
-                 ui::LayerAnimationElement::AnimatableProperty::TRANSFORM,
-                 kAnimationTranslateUpDelay),
-             // ...then translate up to top of parent.
-             CreateTransformElement(gfx::Transform(),
-                                    kAnimationTranslateUpDuration,
-                                    gfx::Tween::Type::FAST_OUT_SLOW_IN_2)),
-         // Animate opacity.
-         CreateLayerAnimationSequence(
-             // Fade out to 0%...
-             CreateOpacityElement(0.f, kAnimationFadeOutDuration),
-             // ...then pause...
-             ui::LayerAnimationElement::CreatePauseElement(
-                 ui::LayerAnimationElement::AnimatableProperty::OPACITY,
-                 kAnimationFadeInDelay),
-             // ...then fade in to 100%.
-             CreateOpacityElement(1.f, kAnimationFadeInDuration))});
-  }
+  // should animate from the bottom of its parent to the top.
+  active_query_view_->layer()->GetAnimator()->StartTogether(
+      {// Animate transformation.
+       CreateLayerAnimationSequence(
+           // Pause...
+           ui::LayerAnimationElement::CreatePauseElement(
+               ui::LayerAnimationElement::AnimatableProperty::TRANSFORM,
+               kAnimationTranslateUpDelay),
+           // ...then translate up to top of parent.
+           CreateTransformElement(gfx::Transform(),
+                                  kAnimationTranslateUpDuration,
+                                  gfx::Tween::Type::FAST_OUT_SLOW_IN_2)),
+       // Animate opacity.
+       CreateLayerAnimationSequence(
+           // Fade out to 0%...
+           CreateOpacityElement(0.f, kAnimationFadeOutDuration),
+           // ...then pause...
+           ui::LayerAnimationElement::CreatePauseElement(
+               ui::LayerAnimationElement::AnimatableProperty::OPACITY,
+               kAnimationFadeInDelay),
+           // ...then fade in to 100%.
+           CreateOpacityElement(1.f, kAnimationFadeInDuration))});
 
   UpdateTopPadding();
   UpdateFooter();
@@ -434,13 +407,10 @@
   if (!active_query_view_)
     return;
 
-  if (!assistant::ui::kIsMotionSpecEnabled) {
-    delete active_query_view_;
-    active_query_view_ = nullptr;
-    return;
-  }
-
-  using namespace assistant::util;
+  using assistant::util::CreateLayerAnimationSequence;
+  using assistant::util::CreateOpacityElement;
+  using assistant::util::CreateTransformElement;
+  using assistant::util::StartLayerAnimationSequencesTogether;
 
   // The active query view will translate off stage.
   gfx::Transform transform;
@@ -488,6 +458,9 @@
   }
 
   if (!pending_query_view_) {
+    using assistant::util::CreateLayerAnimationSequence;
+    using assistant::util::CreateOpacityElement;
+
     pending_query_view_ = new AssistantQueryView();
     pending_query_view_->AddObserver(this);
 
@@ -497,17 +470,13 @@
 
     query_layout_container_->AddChildView(pending_query_view_);
 
-    if (assistant::ui::kIsMotionSpecEnabled) {
-      using namespace assistant::util;
+    // Starting from 0% opacity...
+    pending_query_view_->layer()->SetOpacity(0.f);
 
-      // Starting from 0% opacity...
-      pending_query_view_->layer()->SetOpacity(0.f);
-
-      // ...animate the pending query view to 100% opacity.
-      pending_query_view_->layer()->GetAnimator()->StartAnimation(
-          CreateLayerAnimationSequence(
-              CreateOpacityElement(1.f, kPendingQueryAnimationFadeInDuration)));
-    }
+    // ...animate the pending query view to 100% opacity.
+    pending_query_view_->layer()->GetAnimator()->StartAnimation(
+        CreateLayerAnimationSequence(
+            CreateOpacityElement(1.f, kPendingQueryAnimationFadeInDuration)));
 
     UpdateFooter();
   }
@@ -529,63 +498,58 @@
 }
 
 void AssistantMainStage::OnResponseChanged(const AssistantResponse& response) {
-  // When the response changes, we hide the progress indicator.
-  if (!assistant::ui::kIsMotionSpecEnabled) {
-    progress_indicator_->layer()->SetOpacity(0.f);
-    return;
-  }
+  using assistant::util::CreateLayerAnimationSequence;
+  using assistant::util::CreateOpacityElement;
 
   // Animate the progress indicator to 0% opacity.
-  using namespace assistant::util;
   progress_indicator_->layer()->GetAnimator()->StartAnimation(
       CreateLayerAnimationSequence(
           CreateOpacityElement(0.f, kProgressAnimationFadeOutDuration)));
 
-  // If the motion spec is enabled, we only consider the query active once
-  // the response has been received. When the motion spec is disabled, we
-  // immediately activate the query as it is committed.
+  // We only consider the query active once the response has been received.
   OnActivateQuery();
 }
 
 void AssistantMainStage::OnUiVisibilityChanged(bool visible,
                                                AssistantSource source) {
   if (visible) {
-    // When Assistant UI is shown and the motion spec is enabled, we animate in
-    // the appearance of the greeting label and footer.
-    if (assistant::ui::kIsMotionSpecEnabled) {
-      using namespace assistant::util;
+    // When Assistant UI is shown, we animate in the appearance of the greeting
+    // label and footer.
+    using assistant::util::CreateLayerAnimationSequence;
+    using assistant::util::CreateOpacityElement;
+    using assistant::util::CreateTransformElement;
 
-      // We're going to animate the greeting label up into position so we'll
-      // need to apply an initial transformation.
-      gfx::Transform transform;
-      transform.Translate(0, kGreetingAnimationTranslationDip);
+    // We're going to animate the greeting label up into position so we'll
+    // need to apply an initial transformation.
+    gfx::Transform transform;
+    transform.Translate(0, kGreetingAnimationTranslationDip);
 
-      // Set up our pre-animation values.
-      greeting_label_->layer()->SetOpacity(0.f);
-      greeting_label_->layer()->SetTransform(transform);
+    // Set up our pre-animation values.
+    greeting_label_->layer()->SetOpacity(0.f);
+    greeting_label_->layer()->SetTransform(transform);
 
-      // Start animating greeting label.
-      greeting_label_->layer()->GetAnimator()->StartTogether(
-          {// Animate the transformation.
-           CreateLayerAnimationSequence(CreateTransformElement(
-               gfx::Transform(), kGreetingAnimationTranslateUpDuration,
-               gfx::Tween::Type::FAST_OUT_SLOW_IN_2)),
-           // Animate the opacity to 100% with delay.
-           CreateLayerAnimationSequence(
-               ui::LayerAnimationElement::CreatePauseElement(
-                   ui::LayerAnimationElement::AnimatableProperty::OPACITY,
-                   kGreetingAnimationFadeInDelay),
-               CreateOpacityElement(1.f, kGreetingAnimationFadeInDuration))});
+    // Start animating greeting label.
+    greeting_label_->layer()->GetAnimator()->StartTogether(
+        {// Animate the transformation.
+         CreateLayerAnimationSequence(CreateTransformElement(
+             gfx::Transform(), kGreetingAnimationTranslateUpDuration,
+             gfx::Tween::Type::FAST_OUT_SLOW_IN_2)),
+         // Animate the opacity to 100% with delay.
+         CreateLayerAnimationSequence(
+             ui::LayerAnimationElement::CreatePauseElement(
+                 ui::LayerAnimationElement::AnimatableProperty::OPACITY,
+                 kGreetingAnimationFadeInDelay),
+             CreateOpacityElement(1.f, kGreetingAnimationFadeInDuration))});
 
-      // Animate the footer from 0% to 100% opacity with delay.
-      footer_->layer()->SetOpacity(0.f);
-      footer_->layer()->GetAnimator()->StartAnimation(
-          CreateLayerAnimationSequence(
-              ui::LayerAnimationElement::CreatePauseElement(
-                  ui::LayerAnimationElement::AnimatableProperty::OPACITY,
-                  kFooterEntryAnimationFadeInDelay),
-              CreateOpacityElement(1.f, kFooterEntryAnimationFadeInDuration)));
-    }
+    // Animate the footer from 0% to 100% opacity with delay.
+    footer_->layer()->SetOpacity(0.f);
+    footer_->layer()->GetAnimator()->StartAnimation(
+        CreateLayerAnimationSequence(
+            ui::LayerAnimationElement::CreatePauseElement(
+                ui::LayerAnimationElement::AnimatableProperty::OPACITY,
+                kFooterEntryAnimationFadeInDelay),
+            CreateOpacityElement(1.f, kFooterEntryAnimationFadeInDuration)));
+
     return;
   }
 
@@ -651,18 +615,16 @@
 }
 
 void AssistantMainStage::UpdateFooter() {
+  using assistant::util::CreateLayerAnimationSequence;
+  using assistant::util::CreateOpacityElement;
+  using assistant::util::CreateTransformElement;
+  using assistant::util::StartLayerAnimationSequence;
+  using assistant::util::StartLayerAnimationSequencesTogether;
+
   // The footer is only visible when the committed/pending query views are not.
   // When it is not visible, it should not process events.
   bool visible = !committed_query_view_ && !pending_query_view_;
 
-  if (!assistant::ui::kIsMotionSpecEnabled) {
-    footer_->layer()->SetOpacity(visible ? 1.f : 0.f);
-    footer_->set_can_process_events_within_subtree(visible ? true : false);
-    return;
-  }
-
-  using namespace assistant::util;
-
   if (visible) {
     // The footer will animate up into position so we need to set an initial
     // offset transformation from which to animate.
diff --git a/ash/assistant/ui/main_stage/ui_element_container_view.cc b/ash/assistant/ui/main_stage/ui_element_container_view.cc
index daa08f5..1c1f92d 100644
--- a/ash/assistant/ui/main_stage/ui_element_container_view.cc
+++ b/ash/assistant/ui/main_stage/ui_element_container_view.cc
@@ -243,18 +243,17 @@
 
 void UiElementContainerView::OnCommittedQueryChanged(
     const AssistantQuery& query) {
+  using assistant::util::CreateLayerAnimationSequence;
+  using assistant::util::CreateOpacityElement;
+
   // We don't allow processing of events while waiting for the next query
   // response. The contents will be faded out, so it should not be interactive.
   // We also scroll to the top to play nice with the transition animation.
   set_can_process_events_within_subtree(false);
   ScrollToPosition(vertical_scroll_bar(), 0);
 
-  if (!assistant::ui::kIsMotionSpecEnabled)
-    return;
-
   // When a query is committed, we fade out the views for the previous response
   // until the next Assistant response has been received.
-  using namespace assistant::util;
   for (const std::pair<ui::Layer*, float>& pair : ui_element_layers_) {
     pair.first->GetAnimator()->StartAnimation(
         CreateLayerAnimationSequence(CreateOpacityElement(
@@ -264,22 +263,16 @@
 
 void UiElementContainerView::OnResponseChanged(
     const AssistantResponse& response) {
-  // If the motion spec is disabled, we clear any previous response and then
-  // add the new response in a single step.
-  if (!assistant::ui::kIsMotionSpecEnabled) {
-    OnResponseCleared();
-    OnResponseAdded(response);
-    return;
-  }
-
-  // If the motion spec is enabled but we haven't cached any layers, there is
-  // nothing to animate off stage so we can proceed to add the new response.
+  // If we haven't cached any layers, there is nothing to animate off stage so
+  // we can proceed to add the new response.
   if (ui_element_layers_.empty()) {
     OnResponseAdded(response);
     return;
   }
 
-  using namespace assistant::util;
+  using assistant::util::CreateLayerAnimationSequence;
+  using assistant::util::CreateOpacityElement;
+  using assistant::util::StartLayerAnimationSequence;
 
   // There is a previous response on stage, so we'll animate it off before
   // adding the new response. The new response will be added upon invocation of
@@ -334,17 +327,13 @@
 void UiElementContainerView::OnAllUiElementsAdded() {
   DCHECK(!is_processing_ui_element_);
 
+  using assistant::util::CreateLayerAnimationSequence;
+  using assistant::util::CreateOpacityElement;
+
   // Now that the response for the current query has been added to the view
   // hierarchy, we can re-enable processing of events.
   set_can_process_events_within_subtree(true);
 
-  // If the motion spec is disabled, there's nothing to do because the views
-  // do not need to be animated in.
-  if (!assistant::ui::kIsMotionSpecEnabled)
-    return;
-
-  using namespace assistant::util;
-
   // Now that we've received and added all UI elements for the current query
   // response, we can animate them in.
   for (const std::pair<ui::Layer*, float>& pair : ui_element_layers_) {
@@ -446,20 +435,19 @@
     content_view()->AddChildView(view_holder);
     view_holder->Attach();
 
-    if (assistant::ui::kIsMotionSpecEnabled) {
-      // The view will be animated on its own layer, so we need to do some
-      // initial layer setup. We're going to fade the view in, so hide it.
-      view_holder->native_view()->layer()->SetFillsBoundsOpaquely(false);
-      view_holder->native_view()->layer()->SetOpacity(0.f);
+    // The view will be animated on its own layer, so we need to do some initial
+    // layer setup. We're going to fade the view in, so hide it.
+    view_holder->native_view()->layer()->SetFillsBoundsOpaquely(false);
+    view_holder->native_view()->layer()->SetOpacity(0.f);
 
-      // We cache the layer for the view for use during animations and cache
-      // its desired opacity that we'll animate to while processing the next
-      // query response.
-      ui_element_layers_.push_back(
-          std::pair<ui::Layer*, float>(view_holder->native_view()->layer(),
-                                       kCardElementAnimationFadeOutOpacity));
-    }
+    // We cache the layer for the view for use during animations and cache its
+    // desired opacity that we'll animate to while processing the next query
+    // response.
+    ui_element_layers_.push_back(
+        std::pair<ui::Layer*, float>(view_holder->native_view()->layer(),
+                                     kCardElementAnimationFadeOutOpacity));
   }
+
   // TODO(dmblack): Handle Mash case.
 
   // Once the card has been rendered and embedded, we can resume processing
@@ -473,19 +461,17 @@
 
   views::View* text_element_view = new AssistantTextElementView(text_element);
 
-  if (assistant::ui::kIsMotionSpecEnabled) {
-    // The view will be animated on its own layer, so we need to do some initial
-    // layer setup. We're going to fade the view in, so hide it.
-    text_element_view->SetPaintToLayer();
-    text_element_view->layer()->SetFillsBoundsOpaquely(false);
-    text_element_view->layer()->SetOpacity(0.f);
+  // The view will be animated on its own layer, so we need to do some initial
+  // layer setup. We're going to fade the view in, so hide it.
+  text_element_view->SetPaintToLayer();
+  text_element_view->layer()->SetFillsBoundsOpaquely(false);
+  text_element_view->layer()->SetOpacity(0.f);
 
-    // We cache the layer for the view for use during animations and cache its
-    // desired opacity that we'll animate to while processing the next query
-    // response.
-    ui_element_layers_.push_back(std::pair<ui::Layer*, float>(
-        text_element_view->layer(), kTextElementAnimationFadeOutOpacity));
-  }
+  // We cache the layer for the view for use during animations and cache its
+  // desired opacity that we'll animate to while processing the next query
+  // response.
+  ui_element_layers_.push_back(std::pair<ui::Layer*, float>(
+      text_element_view->layer(), kTextElementAnimationFadeOutOpacity));
 
   content_view()->AddChildView(text_element_view);
 }
diff --git a/ash/autoclick/common/autoclick_controller_common.h b/ash/autoclick/common/autoclick_controller_common.h
index 664331d..e8dfe42 100644
--- a/ash/autoclick/common/autoclick_controller_common.h
+++ b/ash/autoclick/common/autoclick_controller_common.h
@@ -27,6 +27,7 @@
 // after a certain amout of time at that location.
 // AutoclickControllerCommon is the common code for both ash and mus to handle
 // events and to manage autoclick time delay and timer.
+// TODO(jamescook): Collapse with AutoclickController. https://crbug.com/876115
 class AutoclickControllerCommon {
  public:
   AutoclickControllerCommon(base::TimeDelta delay,
diff --git a/ash/components/autoclick/BUILD.gn b/ash/components/autoclick/BUILD.gn
deleted file mode 100644
index ef73514..0000000
--- a/ash/components/autoclick/BUILD.gn
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/ui.gni")
-import("//services/service_manager/public/cpp/service.gni")
-import("//services/service_manager/public/service_manifest.gni")
-import("//mojo/public/tools/bindings/mojom.gni")
-
-source_set("lib") {
-  sources = [
-    "autoclick_application.cc",
-    "autoclick_application.h",
-  ]
-
-  deps = [
-    "//ash/autoclick/common:autoclick",
-    "//ash/components/autoclick/public/mojom",
-    "//ash/public/cpp",
-    "//base",
-    "//mash/public/mojom",
-    "//mojo/public/cpp/bindings",
-    "//services/service_manager/public/cpp",
-    "//services/ui/public/cpp",
-    "//services/ui/public/interfaces",
-    "//ui/aura",
-    "//ui/views",
-    "//ui/views/mus:for_mojo_application",
-  ]
-}
-
-service("autoclick_app") {
-  sources = [
-    "main.cc",
-  ]
-
-  deps = [
-    ":lib",
-    "//base",
-    "//mojo/public/cpp/bindings",
-    "//services/service_manager/public/cpp",
-    "//ui/views/mus:for_mojo_application",
-  ]
-
-  resources = [ "$root_out_dir/views_mus_resources.pak" ]
-}
-
-service_manifest("manifest") {
-  name = "autoclick_app"
-  source = "manifest.json"
-}
diff --git a/ash/components/autoclick/DEPS b/ash/components/autoclick/DEPS
deleted file mode 100644
index 71f675c..0000000
--- a/ash/components/autoclick/DEPS
+++ /dev/null
@@ -1,5 +0,0 @@
-include_rules = [
-  "+ash/autoclick/common",
-  "+mash/public/mojom",
-  "+services/ui/public",
-]
diff --git a/ash/components/autoclick/autoclick_application.cc b/ash/components/autoclick/autoclick_application.cc
deleted file mode 100644
index a593a201..0000000
--- a/ash/components/autoclick/autoclick_application.cc
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/components/autoclick/autoclick_application.h"
-
-#include <utility>
-
-#include "ash/public/cpp/ash_constants.h"
-#include "ash/public/cpp/shell_window_ids.h"
-#include "base/macros.h"
-#include "base/strings/utf_string_conversions.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "services/service_manager/public/cpp/service_context.h"
-#include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/event_injector.mojom.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
-#include "ui/aura/mus/property_converter.h"
-#include "ui/base/ui_base_types.h"
-#include "ui/display/display.h"
-#include "ui/display/screen.h"
-#include "ui/events/base_event_utils.h"
-#include "ui/views/mus/aura_init.h"
-#include "ui/views/mus/mus_client.h"
-#include "ui/views/mus/pointer_watcher_event_router.h"
-#include "ui/views/pointer_watcher.h"
-#include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_delegate.h"
-
-namespace autoclick {
-
-// AutoclickUI handles events to the autoclick app.
-class AutoclickUI : public views::WidgetDelegateView,
-                    public views::PointerWatcher {
- public:
-  explicit AutoclickUI(
-      ash::AutoclickControllerCommon* autoclick_controller_common)
-      : autoclick_controller_common_(autoclick_controller_common) {
-    views::MusClient::Get()->pointer_watcher_event_router()->AddPointerWatcher(
-        this, true /* want_moves */);
-  }
-  ~AutoclickUI() override {
-    views::MusClient::Get()
-        ->pointer_watcher_event_router()
-        ->RemovePointerWatcher(this);
-  }
-
- private:
-  // Overridden from views::WidgetDelegate:
-  base::string16 GetWindowTitle() const override {
-    // TODO(beng): use resources.
-    return base::ASCIIToUTF16("Autoclick");
-  }
-
-  // Overridden from views::PointerWatcher:
-  void OnPointerEventObserved(const ui::PointerEvent& event,
-                              const gfx::Point& location_in_screen,
-                              gfx::NativeView target) override {
-    // AutoclickControllerCommon won't work correctly with a target.
-    DCHECK(!event.target());
-    if (event.IsTouchPointerEvent()) {
-      autoclick_controller_common_->CancelAutoclick();
-    } else if (event.IsMousePointerEvent()) {
-      if (event.type() == ui::ET_POINTER_WHEEL_CHANGED) {
-        autoclick_controller_common_->HandleMouseEvent(
-            ui::MouseWheelEvent(event));
-      } else {
-        ui::MouseEvent mouse_event(event);
-        // AutoclickControllerCommon wants screen coordinates when there isn't a
-        // target.
-        mouse_event.set_location(location_in_screen);
-        autoclick_controller_common_->HandleMouseEvent(mouse_event);
-      }
-    }
-  }
-
-  ash::AutoclickControllerCommon* autoclick_controller_common_;
-
-  DISALLOW_COPY_AND_ASSIGN(AutoclickUI);
-};
-
-AutoclickApplication::AutoclickApplication()
-    : launchable_binding_(this), autoclick_binding_(this) {
-  registry_.AddInterface<mash::mojom::Launchable>(base::BindRepeating(
-      &AutoclickApplication::BindLaunchableRequest, base::Unretained(this)));
-  registry_.AddInterface<mojom::AutoclickController>(
-      base::BindRepeating(&AutoclickApplication::BindAutoclickControllerRequest,
-                          base::Unretained(this)));
-}
-
-AutoclickApplication::~AutoclickApplication() = default;
-
-void AutoclickApplication::OnStart() {
-  views::AuraInit::InitParams params;
-  params.connector = context()->connector();
-  params.identity = context()->identity();
-  params.register_path_provider = running_standalone_;
-  aura_init_ = views::AuraInit::Create(params);
-  if (!aura_init_) {
-    context()->QuitNow();
-    return;
-  }
-  autoclick_controller_common_ =
-      std::make_unique<ash::AutoclickControllerCommon>(
-          base::TimeDelta::FromMilliseconds(ash::kDefaultAutoclickDelayMs),
-          this);
-}
-
-void AutoclickApplication::OnBindInterface(
-    const service_manager::BindSourceInfo& remote_info,
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle interface_pipe) {
-  registry_.BindInterface(interface_name, std::move(interface_pipe));
-}
-
-void AutoclickApplication::Launch(uint32_t what, mash::mojom::LaunchMode how) {
-  if (!widget_) {
-    widget_.reset(new views::Widget);
-    views::Widget::InitParams params(
-        views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
-    params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
-    params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
-    params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
-    params.accept_events = false;
-    params.delegate = new AutoclickUI(autoclick_controller_common_.get());
-
-    params.mus_properties[ui::mojom::WindowManager::kContainerId_InitProperty] =
-        mojo::ConvertTo<std::vector<uint8_t>>(
-            static_cast<int32_t>(ash::kShellWindowId_OverlayContainer));
-    params.show_state = ui::SHOW_STATE_FULLSCREEN;
-    widget_->Init(params);
-  } else {
-    widget_->Close();
-    context()->QuitNow();
-  }
-}
-
-void AutoclickApplication::SetAutoclickDelay(uint32_t delay_in_milliseconds) {
-  autoclick_controller_common_->SetAutoclickDelay(
-      base::TimeDelta::FromMilliseconds(delay_in_milliseconds));
-}
-
-void AutoclickApplication::BindLaunchableRequest(
-    mash::mojom::LaunchableRequest request) {
-  launchable_binding_.Close();
-  launchable_binding_.Bind(std::move(request));
-}
-
-void AutoclickApplication::BindAutoclickControllerRequest(
-    mojom::AutoclickControllerRequest request) {
-  autoclick_binding_.Close();
-  autoclick_binding_.Bind(std::move(request));
-}
-
-views::Widget* AutoclickApplication::CreateAutoclickRingWidget(
-    const gfx::Point& point_in_screen) {
-  return widget_.get();
-}
-
-void AutoclickApplication::UpdateAutoclickRingWidget(
-    views::Widget* widget,
-    const gfx::Point& point_in_screen) {
-  // Not used in mus.
-}
-
-void AutoclickApplication::DoAutoclick(const gfx::Point& point_in_screen,
-                                       const int mouse_event_flags) {
-  // The window service expects display pixel coordinates for events.
-  display::Display display =
-      display::Screen::GetScreen()->GetDisplayNearestPoint(point_in_screen);
-  gfx::Point point_in_root(point_in_screen);
-  point_in_root -= display.bounds().OffsetFromOrigin();
-  gfx::Point point_in_pixels =
-      gfx::ScaleToFlooredPoint(point_in_root, display.device_scale_factor());
-
-  // Connect to the window service event generation interface.
-  ui::mojom::EventInjectorPtr event_injector;
-  context()->connector()->BindInterface(ui::mojom::kServiceName,
-                                        &event_injector);
-
-  // Inject a synthetic click.
-  ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, point_in_pixels,
-                             point_in_pixels, ui::EventTimeForNow(),
-                             mouse_event_flags | ui::EF_LEFT_MOUSE_BUTTON,
-                             ui::EF_LEFT_MOUSE_BUTTON);
-  ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, point_in_pixels,
-                               point_in_pixels, ui::EventTimeForNow(),
-                               mouse_event_flags | ui::EF_LEFT_MOUSE_BUTTON,
-                               ui::EF_LEFT_MOUSE_BUTTON);
-  event_injector->InjectEventNoAck(
-      display.id(), std::make_unique<ui::PointerEvent>(press_event));
-  event_injector->InjectEventNoAck(
-      display.id(), std::make_unique<ui::PointerEvent>(release_event));
-}
-
-void AutoclickApplication::OnAutoclickCanceled() {
-  // Not used in mus.
-}
-
-}  // namespace autoclick
diff --git a/ash/components/autoclick/autoclick_application.h b/ash/components/autoclick/autoclick_application.h
deleted file mode 100644
index f6b9024..0000000
--- a/ash/components/autoclick/autoclick_application.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_COMPONENTS_AUTOCLICK_AUTOCLICK_APPLICATION_H_
-#define ASH_COMPONENTS_AUTOCLICK_AUTOCLICK_APPLICATION_H_
-
-#include <map>
-
-#include "ash/autoclick/common/autoclick_controller_common.h"
-#include "ash/autoclick/common/autoclick_controller_common_delegate.h"
-#include "ash/components/autoclick/public/mojom/autoclick.mojom.h"
-#include "base/macros.h"
-#include "mash/public/mojom/launchable.mojom.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/service_manager/public/cpp/identity.h"
-#include "services/service_manager/public/cpp/service.h"
-
-namespace views {
-class AuraInit;
-class Widget;
-}  // namespace views
-
-namespace autoclick {
-
-// AutoclickApplication is a mojo mini-app that implements the accessibility
-// autoclick feature. The feature watches for the mouse to stop moving then
-// generates a click after a short delay.
-class AutoclickApplication : public service_manager::Service,
-                             public mash::mojom::Launchable,
-                             public mojom::AutoclickController,
-                             public ash::AutoclickControllerCommonDelegate {
- public:
-  AutoclickApplication();
-  ~AutoclickApplication() override;
-
-  void set_running_standalone(bool value) { running_standalone_ = value; }
-
- private:
-  // service_manager::Service:
-  void OnStart() override;
-  void OnBindInterface(const service_manager::BindSourceInfo& remote_info,
-                       const std::string& interface_name,
-                       mojo::ScopedMessagePipeHandle interface_pipe) override;
-
-  // mojom::Launchable:
-  void Launch(uint32_t what, mash::mojom::LaunchMode how) override;
-
-  // mojom::AutoclickController:
-  void SetAutoclickDelay(uint32_t delay_in_milliseconds) override;
-
-  void BindLaunchableRequest(mash::mojom::LaunchableRequest request);
-
-  void BindAutoclickControllerRequest(
-      mojom::AutoclickControllerRequest request);
-
-  // ash::AutoclickControllerCommonDelegate:
-  views::Widget* CreateAutoclickRingWidget(
-      const gfx::Point& point_in_screen) override;
-  void UpdateAutoclickRingWidget(views::Widget* widget,
-                                 const gfx::Point& point_in_screen) override;
-  void DoAutoclick(const gfx::Point& point_in_screen,
-                   const int mouse_event_flags) override;
-  void OnAutoclickCanceled() override;
-
-  service_manager::BinderRegistry registry_;
-  mojo::Binding<mash::mojom::Launchable> launchable_binding_;
-  mojo::Binding<mojom::AutoclickController> autoclick_binding_;
-
-  std::unique_ptr<views::AuraInit> aura_init_;
-  std::unique_ptr<ash::AutoclickControllerCommon> autoclick_controller_common_;
-  std::unique_ptr<views::Widget> widget_;
-
-  bool running_standalone_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(AutoclickApplication);
-};
-
-}  // namespace autoclick
-
-#endif  // ASH_COMPONENTS_AUTOCLICK_AUTOCLICK_APPLICATION_H_
diff --git a/ash/components/autoclick/main.cc b/ash/components/autoclick/main.cc
deleted file mode 100644
index 8a6cdf6..0000000
--- a/ash/components/autoclick/main.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/components/autoclick/autoclick_application.h"
-#include "services/service_manager/public/c/main.h"
-#include "services/service_manager/public/cpp/service_runner.h"
-
-MojoResult ServiceMain(MojoHandle service_request_handle) {
-  autoclick::AutoclickApplication* app = new autoclick::AutoclickApplication;
-  app->set_running_standalone(true);
-  service_manager::ServiceRunner runner(app);
-  return runner.Run(service_request_handle);
-}
diff --git a/ash/components/autoclick/manifest.json b/ash/components/autoclick/manifest.json
deleted file mode 100644
index f453f3d8..0000000
--- a/ash/components/autoclick/manifest.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "name": "autoclick_app",
-  "display_name": "Autoclick",
-  "sandbox_type": "none",
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "provides": {
-        "mash:launchable": [ "mash.mojom.Launchable" ],
-        "chromeos:autoclick": [ "autoclick.mojom.AutoclickController" ]
-      },
-      "requires": {
-        "*": [ "app" ],
-        "service_manager": [ "service_manager:service_manager" ],
-        "ui": [ "privileged" ]
-      }
-    }
-  }
-}
diff --git a/ash/components/autoclick/public/mojom/BUILD.gn b/ash/components/autoclick/public/mojom/BUILD.gn
deleted file mode 100644
index aa30329a..0000000
--- a/ash/components/autoclick/public/mojom/BUILD.gn
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-mojom("mojom") {
-  sources = [
-    "autoclick.mojom",
-  ]
-}
diff --git a/ash/components/autoclick/public/mojom/autoclick.mojom b/ash/components/autoclick/public/mojom/autoclick.mojom
deleted file mode 100644
index 51f4e825..0000000
--- a/ash/components/autoclick/public/mojom/autoclick.mojom
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module autoclick.mojom;
-
-// Used by Chrome to set the time to wait in milliseconds from when the mouse
-// stops moving to when the autoclick event is sent in autoclick_app.
-interface AutoclickController {
-  SetAutoclickDelay(uint32 delay_in_milliseconds);
-};
diff --git a/ash/components/fast_ink/BUILD.gn b/ash/components/fast_ink/BUILD.gn
index 9aee966..eeb3d8bc 100644
--- a/ash/components/fast_ink/BUILD.gn
+++ b/ash/components/fast_ink/BUILD.gn
@@ -18,7 +18,7 @@
     "//components/viz/service",
     "//gpu",
     "//gpu/command_buffer/client:gles2_interface",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//ui/aura",
     "//ui/gfx",
diff --git a/ash/components/tap_visualizer/BUILD.gn b/ash/components/tap_visualizer/BUILD.gn
index a0d61cc..6343c1b 100644
--- a/ash/components/tap_visualizer/BUILD.gn
+++ b/ash/components/tap_visualizer/BUILD.gn
@@ -18,7 +18,7 @@
     "//cc/paint",
     "//services/service_manager/public/cpp",
     "//services/ui/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//ui/compositor",
     "//ui/events",
diff --git a/ash/components/tap_visualizer/DEPS b/ash/components/tap_visualizer/DEPS
index 7f08c61..f8cf998 100644
--- a/ash/components/tap_visualizer/DEPS
+++ b/ash/components/tap_visualizer/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+services/ui/public",
+  "+services/ws/public",
   "+third_party/skia",
   "+ui/compositor",
 ]
diff --git a/ash/components/tap_visualizer/tap_visualizer_app.cc b/ash/components/tap_visualizer/tap_visualizer_app.cc
index d70edf5..288392ae 100644
--- a/ash/components/tap_visualizer/tap_visualizer_app.cc
+++ b/ash/components/tap_visualizer/tap_visualizer_app.cc
@@ -12,8 +12,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "services/service_manager/public/cpp/service_context.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/mus/property_converter.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
diff --git a/ash/content/content_gpu_interface_provider.cc b/ash/content/content_gpu_interface_provider.cc
index e9a29fe7..cd3e6727 100644
--- a/ash/content/content_gpu_interface_provider.cc
+++ b/ash/content/content_gpu_interface_provider.cc
@@ -10,7 +10,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/gpu_client.h"
 #include "content/public/browser/gpu_service_registry.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 
 namespace ash {
 
diff --git a/ash/display/cursor_window_controller.cc b/ash/display/cursor_window_controller.cc
index 5e4a199..301013b 100644
--- a/ash/display/cursor_window_controller.cc
+++ b/ash/display/cursor_window_controller.cc
@@ -20,7 +20,7 @@
 #include "base/command_line.h"
 #include "base/metrics/histogram_macros.h"
 #include "components/prefs/pref_service.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window_delegate.h"
 #include "ui/aura/window_event_dispatcher.h"
diff --git a/ash/frame/custom_frame_header.cc b/ash/frame/custom_frame_header.cc
index bec73f64..a2e53cb1 100644
--- a/ash/frame/custom_frame_header.cc
+++ b/ash/frame/custom_frame_header.cc
@@ -5,8 +5,10 @@
 #include "ash/frame/custom_frame_header.h"
 
 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
+#include "ash/frame/detached_title_area_renderer.h"
 #include "ash/frame/frame_header_util.h"
 #include "ash/public/cpp/ash_layout_constants.h"
+#include "ash/public/cpp/frame_utils.h"
 #include "ash/public/cpp/vector_icons/vector_icons.h"
 #include "base/logging.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -51,58 +53,20 @@
 void PaintFrameImagesInRoundRect(gfx::Canvas* canvas,
                                  const gfx::ImageSkia& frame_image,
                                  const gfx::ImageSkia& frame_overlay_image,
-                                 int alpha,
-                                 SkColor opaque_background_color,
+                                 SkColor background_color,
                                  const gfx::Rect& bounds,
-                                 int corner_radius,
                                  int image_inset_x,
-                                 int image_inset_y) {
+                                 int image_inset_y,
+                                 int alpha,
+                                 int corner_radius) {
   SkPath frame_path = MakeRoundRectPath(bounds, corner_radius, corner_radius);
   bool antialias = corner_radius > 0;
 
   gfx::ScopedCanvas scoped_save(canvas);
   canvas->ClipPath(frame_path, antialias);
 
-  // When no images are used, just draw a color, with the animation |alpha|
-  // applied.
-  if (frame_image.isNull() && frame_overlay_image.isNull()) {
-    // We use kPlus blending mode so that between the active and inactive
-    // background colors, the result is 255 alpha (i.e. opaque).
-    canvas->DrawColor(SkColorSetA(opaque_background_color, alpha),
-                      SkBlendMode::kPlus);
-    return;
-  }
-
-  // This handles the case where blending is required between one or more images
-  // and the background color. In this case we use a SaveLayerWithFlags() call
-  // to draw all 2-3 components into a single layer then apply the alpha to them
-  // together.
-  const bool blending_required =
-      alpha < 0xFF || (!frame_image.isNull() && !frame_overlay_image.isNull());
-  if (blending_required) {
-    cc::PaintFlags flags;
-    // We use kPlus blending mode so that between the active and inactive
-    // background colors, the result is 255 alpha (i.e. opaque).
-    flags.setBlendMode(SkBlendMode::kPlus);
-    flags.setAlpha(alpha);
-    canvas->SaveLayerWithFlags(flags);
-  }
-
-  // Images can be transparent and we expect the background color to be present
-  // behind them. Here the |alpha| will be applied to the background color by
-  // the SaveLayer call, so use |opaque_background_color|.
-  canvas->DrawColor(opaque_background_color);
-  if (!frame_image.isNull()) {
-    canvas->TileImageInt(frame_image, image_inset_x, image_inset_y, 0, 0,
-                         bounds.width(), bounds.height(), 1.0f,
-                         SkShader::kRepeat_TileMode,
-                         SkShader::kMirror_TileMode);
-  }
-  if (!frame_overlay_image.isNull())
-    canvas->DrawImageInt(frame_overlay_image, 0, 0);
-
-  if (blending_required)
-    canvas->Restore();
+  PaintThemedFrame(canvas, frame_image, frame_overlay_image, background_color,
+                   bounds, image_inset_x, image_inset_y, alpha);
 }
 
 }  // namespace
@@ -129,9 +93,14 @@
 // CustomFrameHeader, protected:
 
 void CustomFrameHeader::DoPaintHeader(gfx::Canvas* canvas) {
-  PaintFrameImages(canvas, false /* active */);
-  PaintFrameImages(canvas, true /* active */);
-  PaintTitleBar(canvas);
+  // If this is a detached title area renderer (i.e. for client-controlled
+  // immersive mode), then Mash only needs to render the window caption buttons.
+  if (!DetachedTitleAreaRendererForClient::ForWindow(
+          view()->GetWidget()->GetNativeView())) {
+    PaintFrameImages(canvas, false /* active */);
+    PaintFrameImages(canvas, true /* active */);
+    PaintTitleBar(canvas);
+  }
 }
 
 AshLayoutSize CustomFrameHeader::GetButtonLayoutSize() const {
@@ -170,19 +139,16 @@
       appearance_provider_->GetFrameHeaderImage(active);
   gfx::ImageSkia frame_overlay_image =
       appearance_provider_->GetFrameHeaderOverlayImage(active);
-  // We ensure that the background color is opaque.
-  SkColor opaque_background_color =
-      SkColorSetA(appearance_provider_->GetFrameHeaderColor(active), 0xFF);
 
   int corner_radius = 0;
   if (!target_widget()->IsMaximized() && !target_widget()->IsFullscreen())
     corner_radius = FrameHeaderUtil::GetTopCornerRadiusWhenRestored();
 
   PaintFrameImagesInRoundRect(
-      canvas, frame_image, frame_overlay_image, alpha, opaque_background_color,
-      GetPaintedBounds(), corner_radius,
+      canvas, frame_image, frame_overlay_image,
+      appearance_provider_->GetFrameHeaderColor(active), GetPaintedBounds(),
       FrameHeaderUtil::GetThemeBackgroundXInset(),
-      appearance_provider_->GetFrameHeaderImageYInset());
+      appearance_provider_->GetFrameHeaderImageYInset(), alpha, corner_radius);
 }
 
 }  // namespace ash
diff --git a/ash/frame/detached_title_area_renderer.cc b/ash/frame/detached_title_area_renderer.cc
index d651037a..d0f403a6 100644
--- a/ash/frame/detached_title_area_renderer.cc
+++ b/ash/frame/detached_title_area_renderer.cc
@@ -11,8 +11,8 @@
 #include "ash/shell.h"
 #include "ash/wm/property_util.h"
 #include "ash/wm/window_state.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
 #include "services/ui/ws2/window_properties.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/transient_window_client.h"
 #include "ui/aura/mus/property_converter.h"
diff --git a/ash/frame/detached_title_area_renderer.h b/ash/frame/detached_title_area_renderer.h
index 15f9a6d..62f06ae0 100644
--- a/ash/frame/detached_title_area_renderer.h
+++ b/ash/frame/detached_title_area_renderer.h
@@ -43,12 +43,13 @@
 // the immersive logic, including positioning of the reveal window.
 //
 // DetachedTitleAreaRenderer comes in two variants.
-// DetachedTitleAreaRendererForClient is used for clients that need to draw
+// 1. DetachedTitleAreaRendererForClient is used for clients that need to draw
 // into the non-client area of the widget (case 2). For example, Chrome browser
 // windows draw into the non-client area of tabbed browser widgets (the tab
 // strip is in the non-client area). In such a case
-// DetachedTitleAreaRendererForClient is used.
-// If the client does not need to draw to the non-client area (case 1) then
+// DetachedTitleAreaRendererForClient is used. For these, Mash only draws the
+// window controls --- the frame is rendered by the client.
+// 2. If the client does not need to draw to the non-client area (case 1) then
 // DetachedTitleAreaRendererInternal is used (and ash controls the whole
 // immersive experience). Which is used is determined by
 // |kRenderParentTitleArea_Property|. If |kRenderParentTitleArea_Property| is
diff --git a/ash/frame/non_client_frame_view_ash.cc b/ash/frame/non_client_frame_view_ash.cc
index 393e072..ee9d50a 100644
--- a/ash/frame/non_client_frame_view_ash.cc
+++ b/ash/frame/non_client_frame_view_ash.cc
@@ -13,7 +13,7 @@
 #include "ash/frame/default_frame_header.h"
 #include "ash/frame/header_view.h"
 #include "ash/public/cpp/ash_constants.h"
-#include "ash/public/cpp/frame_border_hit_test.h"
+#include "ash/public/cpp/frame_utils.h"
 #include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
 #include "ash/public/cpp/immersive/immersive_fullscreen_controller_delegate.h"
 #include "ash/public/cpp/window_properties.h"
diff --git a/ash/frame/non_client_frame_view_ash_unittest.cc b/ash/frame/non_client_frame_view_ash_unittest.cc
index 10a6862..bdb4a34 100644
--- a/ash/frame/non_client_frame_view_ash_unittest.cc
+++ b/ash/frame/non_client_frame_view_ash_unittest.cc
@@ -30,7 +30,7 @@
 #include "base/command_line.h"
 #include "base/containers/flat_set.h"
 #include "base/test/scoped_feature_list.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_targeter.h"
diff --git a/ash/host/ash_window_tree_host.h b/ash/host/ash_window_tree_host.h
index 903b1e1b..96d28ba 100644
--- a/ash/host/ash_window_tree_host.h
+++ b/ash/host/ash_window_tree_host.h
@@ -8,7 +8,7 @@
 #include <memory>
 
 #include "ash/ash_export.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 #include "ui/display/display.h"
 
 namespace aura {
diff --git a/ash/host/ash_window_tree_host_platform.cc b/ash/host/ash_window_tree_host_platform.cc
index 2a4ceb2..d0857b9 100644
--- a/ash/host/ash_window_tree_host_platform.cc
+++ b/ash/host/ash_window_tree_host_platform.cc
@@ -14,7 +14,7 @@
 #include "base/feature_list.h"
 #include "base/trace_event/trace_event.h"
 #include "services/ui/public/cpp/input_devices/input_device_controller_client.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/mus/input_method_mus.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host_platform.h"
diff --git a/ash/ime/ime_mode_indicator_view.cc b/ash/ime/ime_mode_indicator_view.cc
index 4b253d7..0564898d 100644
--- a/ash/ime/ime_mode_indicator_view.cc
+++ b/ash/ime/ime_mode_indicator_view.cc
@@ -10,7 +10,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/views/bubble/bubble_frame_view.h"
diff --git a/ash/login/ui/login_test_base.cc b/ash/login/ui/login_test_base.cc
index 4ea2a1a9..45ae5a8 100644
--- a/ash/login/ui/login_test_base.cc
+++ b/ash/login/ui/login_test_base.cc
@@ -13,7 +13,7 @@
 #include "ash/shell.h"
 #include "base/strings/strcat.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
diff --git a/ash/manifest.json b/ash/manifest.json
index b26a68b..c4060bf 100644
--- a/ash/manifest.json
+++ b/ash/manifest.json
@@ -63,10 +63,6 @@
       },
       "requires": {
         "*": [ "accessibility", "app" ],
-        "autoclick_app": [
-          "chromeos:autoclick",
-          "mash:launchable"
-        ],
         "ash_pref_connector": [ "pref_connector" ],
         "catalog": [ "directory" ],
         "local_state": [ "pref_client" ],
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index 77320d0..61bb13f 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -46,8 +46,8 @@
     "ash_typography.h",
     "ash_view_ids.h",
     "config.h",
-    "frame_border_hit_test.cc",
-    "frame_border_hit_test.h",
+    "frame_utils.cc",
+    "frame_utils.h",
     "immersive/immersive_context.cc",
     "immersive/immersive_context.h",
     "immersive/immersive_focus_watcher.h",
@@ -104,7 +104,7 @@
   deps = [
     "//chromeos:power_manager_proto",
     "//components/prefs",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia/public/interfaces",
     "//ui/aura",
     "//ui/chromeos/strings",
diff --git a/ash/public/cpp/frame_border_hit_test.cc b/ash/public/cpp/frame_border_hit_test.cc
deleted file mode 100644
index 6a97a816..0000000
--- a/ash/public/cpp/frame_border_hit_test.cc
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/public/cpp/frame_border_hit_test.h"
-
-#include "ash/public/cpp/ash_constants.h"
-#include "ui/aura/env.h"
-#include "ui/aura/window.h"
-#include "ui/base/hit_test.h"
-#include "ui/views/view_properties.h"
-#include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_delegate.h"
-#include "ui/views/window/hit_test_utils.h"
-#include "ui/views/window/non_client_view.h"
-
-namespace ash {
-
-int FrameBorderNonClientHitTest(views::NonClientFrameView* view,
-                                const gfx::Point& point_in_widget) {
-  gfx::Rect expanded_bounds = view->bounds();
-  int outside_bounds = kResizeOutsideBoundsSize;
-
-  if (view->GetWidget()->GetNativeWindow()->env()->is_touch_down())
-    outside_bounds *= kResizeOutsideBoundsScaleForTouch;
-  expanded_bounds.Inset(-outside_bounds, -outside_bounds);
-
-  if (!expanded_bounds.Contains(point_in_widget))
-    return HTNOWHERE;
-
-  // Check the frame first, as we allow a small area overlapping the contents
-  // to be used for resize handles.
-  views::Widget* widget = view->GetWidget();
-  bool can_ever_resize = widget->widget_delegate()->CanResize();
-  // Don't allow overlapping resize handles when the window is maximized or
-  // fullscreen, as it can't be resized in those states.
-  int resize_border = kResizeInsideBoundsSize;
-  if (widget->IsMaximized() || widget->IsFullscreen()) {
-    resize_border = 0;
-    can_ever_resize = false;
-  }
-  int frame_component = view->GetHTComponentForFrame(
-      point_in_widget, resize_border, resize_border, kResizeAreaCornerSize,
-      kResizeAreaCornerSize, can_ever_resize);
-  if (frame_component != HTNOWHERE)
-    return frame_component;
-
-  int client_component =
-      widget->client_view()->NonClientHitTest(point_in_widget);
-  if (client_component != HTNOWHERE)
-    return client_component;
-
-  // Check if it intersects with children (frame caption button, back button,
-  // etc.).
-  int hit_test_component =
-      views::GetHitTestComponent(widget->non_client_view(), point_in_widget);
-  if (hit_test_component != HTNOWHERE)
-    return hit_test_component;
-
-  // Caption is a safe default.
-  return HTCAPTION;
-}
-
-}  // namespace ash
diff --git a/ash/public/cpp/frame_border_hit_test.h b/ash/public/cpp/frame_border_hit_test.h
deleted file mode 100644
index f5c7a76..0000000
--- a/ash/public/cpp/frame_border_hit_test.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_PUBLIC_CPP_FRAME_BORDER_HIT_TEST_H_
-#define ASH_PUBLIC_CPP_FRAME_BORDER_HIT_TEST_H_
-
-#include "ash/public/cpp/ash_public_export.h"
-
-namespace gfx {
-class Point;
-}
-
-namespace views {
-class NonClientFrameView;
-}  // namespace views
-
-namespace ash {
-
-// Returns the HitTestCompat for the specified point.
-ASH_PUBLIC_EXPORT int FrameBorderNonClientHitTest(
-    views::NonClientFrameView* view,
-    const gfx::Point& point_in_widget);
-
-}  // namespace ash
-
-#endif  // ASH_PUBLIC_CPP_FRAME_BORDER_HIT_TEST_H_
diff --git a/ash/public/cpp/frame_utils.cc b/ash/public/cpp/frame_utils.cc
new file mode 100644
index 0000000..eb690904
--- /dev/null
+++ b/ash/public/cpp/frame_utils.cc
@@ -0,0 +1,120 @@
+// 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 "ash/public/cpp/frame_utils.h"
+
+#include "ash/public/cpp/ash_constants.h"
+#include "ui/aura/env.h"
+#include "ui/aura/window.h"
+#include "ui/base/hit_test.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/views/view_properties.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+#include "ui/views/window/hit_test_utils.h"
+#include "ui/views/window/non_client_view.h"
+
+namespace ash {
+
+int FrameBorderNonClientHitTest(views::NonClientFrameView* view,
+                                const gfx::Point& point_in_widget) {
+  gfx::Rect expanded_bounds = view->bounds();
+  int outside_bounds = kResizeOutsideBoundsSize;
+
+  if (view->GetWidget()->GetNativeWindow()->env()->is_touch_down())
+    outside_bounds *= kResizeOutsideBoundsScaleForTouch;
+  expanded_bounds.Inset(-outside_bounds, -outside_bounds);
+
+  if (!expanded_bounds.Contains(point_in_widget))
+    return HTNOWHERE;
+
+  // Check the frame first, as we allow a small area overlapping the contents
+  // to be used for resize handles.
+  views::Widget* widget = view->GetWidget();
+  bool can_ever_resize = widget->widget_delegate()->CanResize();
+  // Don't allow overlapping resize handles when the window is maximized or
+  // fullscreen, as it can't be resized in those states.
+  int resize_border = kResizeInsideBoundsSize;
+  if (widget->IsMaximized() || widget->IsFullscreen()) {
+    resize_border = 0;
+    can_ever_resize = false;
+  }
+  int frame_component = view->GetHTComponentForFrame(
+      point_in_widget, resize_border, resize_border, kResizeAreaCornerSize,
+      kResizeAreaCornerSize, can_ever_resize);
+  if (frame_component != HTNOWHERE)
+    return frame_component;
+
+  int client_component =
+      widget->client_view()->NonClientHitTest(point_in_widget);
+  if (client_component != HTNOWHERE)
+    return client_component;
+
+  // Check if it intersects with children (frame caption button, back button,
+  // etc.).
+  int hit_test_component =
+      views::GetHitTestComponent(widget->non_client_view(), point_in_widget);
+  if (hit_test_component != HTNOWHERE)
+    return hit_test_component;
+
+  // Caption is a safe default.
+  return HTCAPTION;
+}
+
+void PaintThemedFrame(gfx::Canvas* canvas,
+                      const gfx::ImageSkia& frame_image,
+                      const gfx::ImageSkia& frame_overlay_image,
+                      SkColor background_color,
+                      const gfx::Rect& bounds,
+                      int image_inset_x,
+                      int image_inset_y,
+                      int alpha) {
+  SkColor opaque_background_color =
+      SkColorSetA(background_color, SK_AlphaOPAQUE);
+
+  // When no images are used, just draw a color, with the animation |alpha|
+  // applied.
+  if (frame_image.isNull() && frame_overlay_image.isNull()) {
+    // We use kPlus blending mode so that between the active and inactive
+    // background colors, the result is 255 alpha (i.e. opaque).
+    canvas->DrawColor(SkColorSetA(opaque_background_color, alpha),
+                      SkBlendMode::kPlus);
+    return;
+  }
+
+  // This handles the case where blending is required between one or more images
+  // and the background color. In this case we use a SaveLayerWithFlags() call
+  // to draw all 2-3 components into a single layer then apply the alpha to them
+  // together.
+  const bool blending_required =
+      alpha < 0xFF || (!frame_image.isNull() && !frame_overlay_image.isNull());
+  if (blending_required) {
+    cc::PaintFlags flags;
+    // We use kPlus blending mode so that between the active and inactive
+    // background colors, the result is 255 alpha (i.e. opaque).
+    flags.setBlendMode(SkBlendMode::kPlus);
+    flags.setAlpha(alpha);
+    canvas->SaveLayerWithFlags(flags);
+  }
+
+  // Images can be transparent and we expect the background color to be present
+  // behind them. Here the |alpha| will be applied to the background color by
+  // the SaveLayer call, so use |opaque_background_color|.
+  canvas->DrawColor(opaque_background_color);
+  if (!frame_image.isNull()) {
+    canvas->TileImageInt(frame_image, image_inset_x, image_inset_y, 0, 0,
+                         bounds.width(), bounds.height(), 1.0f,
+                         SkShader::kRepeat_TileMode,
+                         SkShader::kMirror_TileMode);
+  }
+  if (!frame_overlay_image.isNull())
+    canvas->DrawImageInt(frame_overlay_image, 0, 0);
+
+  if (blending_required)
+    canvas->Restore();
+}
+
+}  // namespace ash
diff --git a/ash/public/cpp/frame_utils.h b/ash/public/cpp/frame_utils.h
new file mode 100644
index 0000000..0853382
--- /dev/null
+++ b/ash/public/cpp/frame_utils.h
@@ -0,0 +1,43 @@
+// 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 ASH_PUBLIC_CPP_FRAME_UTILS_H_
+#define ASH_PUBLIC_CPP_FRAME_UTILS_H_
+
+#include "ash/public/cpp/ash_public_export.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace gfx {
+class Canvas;
+class ImageSkia;
+class Point;
+class Rect;
+}  // namespace gfx
+
+namespace views {
+class NonClientFrameView;
+}
+
+namespace ash {
+
+// Returns the HitTestCompat for the specified point.
+ASH_PUBLIC_EXPORT int FrameBorderNonClientHitTest(
+    views::NonClientFrameView* view,
+    const gfx::Point& point_in_widget);
+
+// Paints the frame header images and background color for custom themes (i.e.
+// browser themes) into a canvas.
+ASH_PUBLIC_EXPORT void PaintThemedFrame(
+    gfx::Canvas* canvas,
+    const gfx::ImageSkia& frame_image,
+    const gfx::ImageSkia& frame_overlay_image,
+    SkColor background_color,
+    const gfx::Rect& bounds,
+    int image_inset_x,
+    int image_inset_y,
+    int alpha);
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_FRAME_UTILS_H_
diff --git a/ash/public/cpp/window_properties.cc b/ash/public/cpp/window_properties.cc
index ffb75774..d34dc2d7 100644
--- a/ash/public/cpp/window_properties.cc
+++ b/ash/public/cpp/window_properties.cc
@@ -12,7 +12,7 @@
 #include "ash/public/interfaces/window_properties.mojom.h"
 #include "ash/public/interfaces/window_state_type.mojom.h"
 #include "base/unguessable_token.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/mus/property_converter.h"
 #include "ui/aura/window.h"
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index b274e9b..9897204 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -215,6 +215,7 @@
     "unified_menu_volume_medium.icon",
     "unified_menu_volume_mute.icon",
     "unified_menu_vpn.icon",
+    "unified_menu_wifi_off.icon",
     "wallpaper.icon",
     "window_control_back.icon",
     "window_control_dezoom.icon",
diff --git a/ash/resources/vector_icons/unified_menu_wifi_off.icon b/ash/resources/vector_icons/unified_menu_wifi_off.icon
new file mode 100644
index 0000000..3c5e3f2
--- /dev/null
+++ b/ash/resources/vector_icons/unified_menu_wifi_off.icon
@@ -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.
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 12, 5,
+R_CUBIC_TO, -3.3f, 0, -6.17f, 0.98f, -8.55f, 2.45f,
+LINE_TO, 12, 18.36f,
+R_LINE_TO, 8.55f, -10.91f,
+CUBIC_TO, 18.17f, 5.98f, 15.3f, 5, 12, 5,
+CLOSE,
+R_MOVE_TO, 10.78f, 2.84f,
+LINE_TO, 12.76f, 20.62f,
+R_CUBIC_TO, -0.39f, 0.5f, -1.14f, 0.5f, -1.53f, 0,
+LINE_TO, 1.22f, 7.84f,
+R_CUBIC_TO, -0.36f, -0.46f, -0.26f, -1.13f, 0.21f, -1.46f,
+CUBIC_TO, 3.07f, 5.23f, 6.87f, 3, 12, 3,
+R_CUBIC_TO, 5.13f, 0, 8.93f, 2.23f, 10.57f, 3.38f,
+R_CUBIC_TO, 0.47f, 0.33f, 0.57f, 1, 0.21f, 1.46f,
+CLOSE
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 10, 6,
+R_CUBIC_TO, -2.04f, 0, -3.85f, 0.54f, -5.39f, 1.37f,
+R_LINE_TO, 5.39f, 6.83f,
+R_LINE_TO, 5.39f, -6.83f,
+CUBIC_TO, 13.85f, 6.54f, 12.04f, 6, 10, 6,
+CLOSE,
+R_MOVE_TO, 7.84f, 1.5f,
+R_LINE_TO, -7.29f, 9.23f,
+R_CUBIC_TO, -0.29f, 0.36f, -0.83f, 0.36f, -1.11f, 0,
+LINE_TO, 2.16f, 7.5f,
+R_CUBIC_TO, -0.26f, -0.33f, -0.19f, -0.82f, 0.15f, -1.06f,
+CUBIC_TO, 3.5f, 5.61f, 6.27f, 4, 10, 4,
+R_CUBIC_TO, 3.73f, 0, 6.5f, 1.61f, 7.69f, 2.44f,
+R_CUBIC_TO, 0.34f, 0.24f, 0.42f, 0.72f, 0.15f, 1.06f,
+CLOSE
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index b53ca5c..42ac5b4 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -45,7 +45,7 @@
 #include "base/run_loop.h"
 #include "base/stl_util.h"
 #include "base/test/scoped_feature_list.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/window_parenting_client.h"
 #include "ui/aura/window.h"
diff --git a/ash/shell.cc b/ash/shell.cc
index 0f719a0..e7357ad3 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -1038,8 +1038,7 @@
   accessibility_focus_ring_controller_ =
       std::make_unique<AccessibilityFocusRingController>();
   accessibility_delegate_.reset(shell_delegate_->CreateAccessibilityDelegate());
-  accessibility_controller_ =
-      std::make_unique<AccessibilityController>(connector_);
+  accessibility_controller_ = std::make_unique<AccessibilityController>();
   toast_manager_ = std::make_unique<ToastManager>();
 
   // Install the custom factory early on so that views::FocusManagers for Tray,
diff --git a/ash/shell/content/client/shell_content_browser_client.cc b/ash/shell/content/client/shell_content_browser_client.cc
index db6a07d..9163f50 100644
--- a/ash/shell/content/client/shell_content_browser_client.cc
+++ b/ash/shell/content/client/shell_content_browser_client.cc
@@ -27,8 +27,8 @@
 #include "content/public/common/service_names.mojom.h"
 #include "content/public/utility/content_utility_client.h"
 #include "services/ui/ime/test_ime_driver/public/mojom/constants.mojom.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
 #include "services/ui/ws2/window_service.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "storage/browser/quota/quota_settings.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/resource/resource_bundle.h"
diff --git a/ash/system/accessibility/select_to_speak_tray.cc b/ash/system/accessibility/select_to_speak_tray.cc
index cd50de3..88b6929 100644
--- a/ash/system/accessibility/select_to_speak_tray.cc
+++ b/ash/system/accessibility/select_to_speak_tray.cc
@@ -77,10 +77,6 @@
   CheckStatusAndUpdateIcon();
 }
 
-bool SelectToSpeakTray::ContainsPointInScreen(const gfx::Point& point) {
-  return GetBoundsInScreen().Contains(point);
-}
-
 void SelectToSpeakTray::UpdateIconsForSession() {
   session_manager::SessionState session_state =
       Shell::Get()->session_controller()->GetSessionState();
diff --git a/ash/system/accessibility/select_to_speak_tray.h b/ash/system/accessibility/select_to_speak_tray.h
index 3f38158..0fd4c1de 100644
--- a/ash/system/accessibility/select_to_speak_tray.h
+++ b/ash/system/accessibility/select_to_speak_tray.h
@@ -39,10 +39,6 @@
   // SessionObserver:
   void OnSessionStateChanged(session_manager::SessionState state) override;
 
-  // Returns true if the screen point passed in is contained within this tray's
-  // bounds.
-  bool ContainsPointInScreen(const gfx::Point& point);
-
  private:
   friend class SelectToSpeakTrayTest;
 
diff --git a/ash/system/accessibility/select_to_speak_tray_utils.cc b/ash/system/accessibility/select_to_speak_tray_utils.cc
deleted file mode 100644
index f459b629..0000000
--- a/ash/system/accessibility/select_to_speak_tray_utils.cc
+++ /dev/null
@@ -1,30 +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 "ash/system/accessibility/select_to_speak_tray_utils.h"
-
-#include "ash/session/session_controller.h"
-#include "ash/shelf/shelf.h"
-#include "ash/shell.h"
-#include "ash/system/accessibility/select_to_speak_tray.h"
-#include "ash/system/status_area_widget.h"
-#include "ui/display/display.h"
-#include "ui/gfx/geometry/point.h"
-
-namespace ash {
-namespace select_to_speak_tray_utils {
-
-bool SelectToSpeakTrayContainsPointInScreen(const gfx::Point& point) {
-  for (aura::Window* window : Shell::GetAllRootWindows()) {
-    SelectToSpeakTray* tray =
-        Shelf::ForWindow(window)->GetStatusAreaWidget()->select_to_speak_tray();
-    if (tray && tray->ContainsPointInScreen(point))
-      return true;
-  }
-
-  return false;
-}
-
-}  // namespace select_to_speak_tray_utils
-}  // namespace ash
\ No newline at end of file
diff --git a/ash/system/accessibility/select_to_speak_tray_utils.h b/ash/system/accessibility/select_to_speak_tray_utils.h
deleted file mode 100644
index efa545f..0000000
--- a/ash/system/accessibility/select_to_speak_tray_utils.h
+++ /dev/null
@@ -1,24 +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 ASH_SYSTEM_ACCESSIBILITY_SELECT_TO_SPEAK_TRAY_UTILS_H_
-#define ASH_SYSTEM_ACCESSIBILITY_SELECT_TO_SPEAK_TRAY_UTILS_H_
-
-#include "ash/ash_export.h"
-
-namespace gfx {
-class Point;
-}
-
-namespace ash {
-namespace select_to_speak_tray_utils {
-
-// Returns true if either the select-to-speak tray icon contains the
-// given point (in screen space).
-ASH_EXPORT bool SelectToSpeakTrayContainsPointInScreen(const gfx::Point& point);
-
-}  // namespace select_to_speak_tray_utils
-}  // namespace ash
-
-#endif  // ASH_SYSTEM_ACCESSIBILITY_SELECT_TO_SPEAK_TRAY_UTILS_H_
diff --git a/ash/system/network/network_icon.cc b/ash/system/network/network_icon.cc
index f78feac9..c688fe2 100644
--- a/ash/system/network/network_icon.cc
+++ b/ash/system/network/network_icon.cc
@@ -182,6 +182,10 @@
 }
 
 bool IconTypeIsDark(IconType icon_type) {
+  if (features::IsSystemTrayUnifiedEnabled()) {
+    // Dark icon is used for OOBE tray icon because the background is white.
+    return icon_type == ICON_TYPE_TRAY_OOBE;
+  }
   return icon_type != ICON_TYPE_TRAY_REGULAR;
 }
 
@@ -589,6 +593,12 @@
 }
 
 gfx::ImageSkia GetImageForWiFiEnabledState(bool enabled, IconType icon_type) {
+  if (features::IsSystemTrayUnifiedEnabled() && !enabled) {
+    return gfx::CreateVectorIcon(kUnifiedMenuWifiOffIcon,
+                                 GetSizeForIconType(icon_type).width(),
+                                 GetDefaultColorForIconType(icon_type));
+  }
+
   gfx::ImageSkia image =
       GetBasicImage(true /* connected */, icon_type, shill::kTypeWifi);
   Badges badges;
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc
index 1addc3a..325bfe2 100644
--- a/ash/test/ash_test_base.cc
+++ b/ash/test/ash_test_base.cc
@@ -38,12 +38,12 @@
 #include "components/user_manager/user_names.h"
 #include "mojo/public/cpp/bindings/map.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
 #include "services/ui/ws2/test_window_tree_client.h"
 #include "services/ui/ws2/window_service.h"
 #include "services/ui/ws2/window_tree.h"
 #include "services/ui/ws2/window_tree_test_helper.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/client/window_parenting_client.h"
diff --git a/ash/wm/ash_focus_rules_unittest.cc b/ash/wm/ash_focus_rules_unittest.cc
index 53dc1b9..52283ac9 100644
--- a/ash/wm/ash_focus_rules_unittest.cc
+++ b/ash/wm/ash_focus_rules_unittest.cc
@@ -14,7 +14,7 @@
 #include "ash/wm/lock_state_controller.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/window_parenting_client.h"
 #include "ui/base/ui_base_types.h"
diff --git a/ash/wm/lock_action_handler_layout_manager_unittest.cc b/ash/wm/lock_action_handler_layout_manager_unittest.cc
index ad73abb..60fe9de 100644
--- a/ash/wm/lock_action_handler_layout_manager_unittest.cc
+++ b/ash/wm/lock_action_handler_layout_manager_unittest.cc
@@ -27,7 +27,7 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/keyboard/keyboard_controller.h"
diff --git a/ash/wm/lock_layout_manager_unittest.cc b/ash/wm/lock_layout_manager_unittest.cc
index e01c535..0c9385d 100644
--- a/ash/wm/lock_layout_manager_unittest.cc
+++ b/ash/wm/lock_layout_manager_unittest.cc
@@ -10,7 +10,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/window_state.h"
 #include "base/command_line.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/display/manager/display_manager.h"
diff --git a/ash/wm/non_client_frame_controller.cc b/ash/wm/non_client_frame_controller.cc
index 4462f7d..e3906c0 100644
--- a/ash/wm/non_client_frame_controller.cc
+++ b/ash/wm/non_client_frame_controller.cc
@@ -24,9 +24,9 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
 #include "services/ui/ws2/window_properties.h"
 #include "services/ui/ws2/window_service.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/transient_window_client.h"
diff --git a/ash/wm/non_client_frame_controller_unittest.cc b/ash/wm/non_client_frame_controller_unittest.cc
index b687f8b..79eac98 100644
--- a/ash/wm/non_client_frame_controller_unittest.cc
+++ b/ash/wm/non_client_frame_controller_unittest.cc
@@ -15,9 +15,9 @@
 #include "cc/trees/layer_tree_settings.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/quads/solid_color_draw_quad.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
 #include "services/ui/ws2/test_change_tracker.h"
 #include "services/ui/ws2/test_window_tree_client.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/aura/client/aura_constants.h"
diff --git a/ash/wm/property_util.cc b/ash/wm/property_util.cc
index 1be1201..c5c62b5 100644
--- a/ash/wm/property_util.cc
+++ b/ash/wm/property_util.cc
@@ -7,7 +7,7 @@
 #include "ash/public/cpp/window_style.h"
 #include "ash/public/interfaces/window_style.mojom.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/mus/property_converter.h"
 #include "ui/aura/window.h"
 #include "ui/display/types/display_constants.h"
diff --git a/ash/wm/splitview/split_view_controller_unittest.cc b/ash/wm/splitview/split_view_controller_unittest.cc
index e3fe0c2..43ccf596 100644
--- a/ash/wm/splitview/split_view_controller_unittest.cc
+++ b/ash/wm/splitview/split_view_controller_unittest.cc
@@ -38,7 +38,7 @@
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
 #include "base/stl_util.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/test/test_windows.h"
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
index 93fbf8b..1b5da9f 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
@@ -32,7 +32,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/test/test_windows.h"
diff --git a/ash/wm/top_level_window_factory.cc b/ash/wm/top_level_window_factory.cc
index 3361431..9f19d2d 100644
--- a/ash/wm/top_level_window_factory.cc
+++ b/ash/wm/top_level_window_factory.cc
@@ -17,10 +17,10 @@
 #include "ash/wm/window_state.h"
 #include "mojo/public/cpp/bindings/type_converter.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
 #include "services/ui/ws2/window_delegate_impl.h"
 #include "services/ui/ws2/window_properties.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/mus/property_converter.h"
 #include "ui/aura/mus/property_utils.h"
diff --git a/ash/wm/top_level_window_factory_unittest.cc b/ash/wm/top_level_window_factory_unittest.cc
index cc02abb..41d77d8 100644
--- a/ash/wm/top_level_window_factory_unittest.cc
+++ b/ash/wm/top_level_window_factory_unittest.cc
@@ -16,8 +16,8 @@
 #include "ash/wm/window_properties.h"
 #include "mojo/public/cpp/bindings/map.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
 #include "services/ui/ws2/window_tree_test_helper.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/mus/property_converter.h"
 #include "ui/aura/window.h"
 #include "ui/display/screen.h"
diff --git a/ash/wm/toplevel_window_event_handler_unittest.cc b/ash/wm/toplevel_window_event_handler_unittest.cc
index 9483c9f..59f55826 100644
--- a/ash/wm/toplevel_window_event_handler_unittest.cc
+++ b/ash/wm/toplevel_window_event_handler_unittest.cc
@@ -17,7 +17,7 @@
 #include "ash/wm/workspace_controller.h"
 #include "base/compiler_specific.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/capture_client.h"
diff --git a/ash/wm/window_modality_controller_unittest.cc b/ash/wm/window_modality_controller_unittest.cc
index 66aa510..497a1e5 100644
--- a/ash/wm/window_modality_controller_unittest.cc
+++ b/ash/wm/window_modality_controller_unittest.cc
@@ -8,7 +8,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/test_child_modal_parent.h"
 #include "ash/wm/window_util.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/test/test_window_delegate.h"
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index 7e9c35e..02208a8 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -23,7 +23,7 @@
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
 #include "base/auto_reset.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/layout_manager.h"
 #include "ui/aura/window.h"
diff --git a/ash/wm/window_state_unittest.cc b/ash/wm/window_state_unittest.cc
index 6512970..2419952c 100644
--- a/ash/wm/window_state_unittest.cc
+++ b/ash/wm/window_state_unittest.cc
@@ -12,7 +12,7 @@
 #include "ash/wm/window_state_util.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/window.h"
diff --git a/ash/wm/workspace/workspace_event_handler_unittest.cc b/ash/wm/workspace/workspace_event_handler_unittest.cc
index 38a2787..01a7661 100644
--- a/ash/wm/workspace/workspace_event_handler_unittest.cc
+++ b/ash/wm/workspace/workspace_event_handler_unittest.cc
@@ -15,7 +15,7 @@
 #include "ash/wm/workspace_controller_test_api.h"
 #include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/window.h"
diff --git a/ash/wm/workspace/workspace_window_resizer_unittest.cc b/ash/wm/workspace/workspace_window_resizer_unittest.cc
index f4bfa76f..dc79df8 100644
--- a/ash/wm/workspace/workspace_window_resizer_unittest.cc
+++ b/ash/wm/workspace/workspace_window_resizer_unittest.cc
@@ -20,7 +20,7 @@
 #include "base/command_line.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/test/test_windows.h"
diff --git a/ash/ws/ash_gpu_interface_provider.h b/ash/ws/ash_gpu_interface_provider.h
index 021f395..c58347a 100644
--- a/ash/ws/ash_gpu_interface_provider.h
+++ b/ash/ws/ash_gpu_interface_provider.h
@@ -6,9 +6,9 @@
 #define ASH_WS_ASH_GPU_INTERFACE_PROVIDER_H_
 
 #include "components/discardable_memory/public/interfaces/discardable_shared_memory_manager.mojom.h"
-#include "services/ui/public/interfaces/arc.mojom.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
 #include "services/ui/ws2/gpu_interface_provider.h"
+#include "services/ws/public/mojom/arc.mojom.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 
 namespace discardable_memory {
 class DiscardableSharedMemoryManager;
diff --git a/ash/ws/window_service_delegate_impl.cc b/ash/ws/window_service_delegate_impl.cc
index e639399..1795217 100644
--- a/ash/ws/window_service_delegate_impl.cc
+++ b/ash/ws/window_service_delegate_impl.cc
@@ -16,8 +16,8 @@
 #include "ash/wm/window_util.h"
 #include "base/bind.h"
 #include "mojo/public/cpp/bindings/map.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/drag_drop_client.h"
 #include "ui/aura/env.h"
diff --git a/base/allocator/BUILD.gn b/base/allocator/BUILD.gn
index bc15d8b..8e72ebd 100644
--- a/base/allocator/BUILD.gn
+++ b/base/allocator/BUILD.gn
@@ -28,7 +28,10 @@
 }
 
 config("tcmalloc_flags") {
-  defines = []
+  defines = [
+    "TCMALLOC_USE_DOUBLYLINKED_FREELIST",
+    "TCMALLOC_DISABLE_HUGE_ALLOCATIONS",
+  ]
   if (enable_debugallocation) {
     defines += [
       # Use debugallocation for Debug builds to catch problems early
diff --git a/base/memory/discardable_shared_memory.cc b/base/memory/discardable_shared_memory.cc
index 9fe6832..f089506 100644
--- a/base/memory/discardable_shared_memory.cc
+++ b/base/memory/discardable_shared_memory.cc
@@ -264,7 +264,7 @@
   //
   // For more information, see
   // https://bugs.chromium.org/p/chromium/issues/detail?id=823915.
-  madvise(reinterpret_cast<char*>(shared_memory_mapping_.memory()) +
+  madvise(static_cast<char*>(shared_memory_mapping_.memory()) +
               AlignToPageSize(sizeof(SharedState)),
           AlignToPageSize(mapped_size_), MADV_FREE_REUSE);
   return DiscardableSharedMemory::SUCCESS;
@@ -335,7 +335,7 @@
 }
 
 void* DiscardableSharedMemory::memory() const {
-  return reinterpret_cast<uint8_t*>(shared_memory_mapping_.memory()) +
+  return static_cast<uint8_t*>(shared_memory_mapping_.memory()) +
          AlignToPageSize(sizeof(SharedState));
 }
 
@@ -385,7 +385,7 @@
   // Advise the kernel to remove resources associated with purged pages.
   // Subsequent accesses of memory pages will succeed, but might result in
   // zero-fill-on-demand pages.
-  if (madvise(reinterpret_cast<char*>(shared_memory_mapping_.memory()) +
+  if (madvise(static_cast<char*>(shared_memory_mapping_.memory()) +
                   AlignToPageSize(sizeof(SharedState)),
               AlignToPageSize(mapped_size_), MADV_PURGE_ARGUMENT)) {
     DPLOG(ERROR) << "madvise() failed";
@@ -401,7 +401,7 @@
             GetModuleHandle(L"kernel32.dll"), "DiscardVirtualMemory"));
     if (discard_virtual_memory) {
       DWORD discard_result = discard_virtual_memory(
-          reinterpret_cast<char*>(shared_memory_mapping_.memory()) +
+          static_cast<char*>(shared_memory_mapping_.memory()) +
               AlignToPageSize(sizeof(SharedState)),
           AlignToPageSize(mapped_size_));
       if (discard_result != ERROR_SUCCESS) {
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.cc b/base/sampling_heap_profiler/sampling_heap_profiler.cc
index 4323af2..a9b44d19 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler.cc
+++ b/base/sampling_heap_profiler/sampling_heap_profiler.cc
@@ -26,6 +26,10 @@
 #include "base/trace_event/cfi_backtrace_android.h"
 #endif
 
+#if defined(OS_MACOSX)
+#include <pthread.h>
+#endif
+
 namespace base {
 
 using base::allocator::AllocatorDispatch;
@@ -34,6 +38,46 @@
 
 namespace {
 
+#if defined(OS_MACOSX)
+
+// On MacOS the implementation of libmalloc sometimes calls malloc recursively,
+// delegating allocations between zones. That causes our hooks being called
+// twice. The scoped guard allows us to detect that.
+class ReentryGuard {
+ public:
+  ReentryGuard() : allowed_(!pthread_getspecific(entered_key_)) {
+    pthread_setspecific(entered_key_, reinterpret_cast<void*>(1));
+  }
+
+  ~ReentryGuard() {
+    if (LIKELY(allowed_))
+      pthread_setspecific(entered_key_, reinterpret_cast<void*>(0));
+  }
+
+  operator bool() { return allowed_; }
+
+  static void Init() {
+    int result = pthread_key_create(&entered_key_, nullptr);
+    DCHECK(!result);
+  }
+
+ private:
+  bool allowed_;
+  static pthread_key_t entered_key_;
+};
+
+pthread_key_t ReentryGuard::entered_key_;
+
+#else
+
+class ReentryGuard {
+ public:
+  operator bool() { return true; }
+  static void Init() {}
+};
+
+#endif
+
 // Control how many top frames to skip when recording call stack.
 // These frames correspond to the profiler own frames.
 const uint32_t kSkipBaseAllocatorFrames = 2;
@@ -56,10 +100,13 @@
 Atomic32 g_hooks_installed;
 
 void* AllocFn(const AllocatorDispatch* self, size_t size, void* context) {
+  ReentryGuard guard;
   void* address = self->next->alloc_function(self->next, size, context);
-  SamplingHeapProfiler::RecordAlloc(address, size,
-                                    SamplingHeapProfiler::kMalloc, nullptr,
-                                    kSkipBaseAllocatorFrames);
+  if (LIKELY(guard)) {
+    SamplingHeapProfiler::RecordAlloc(address, size,
+                                      SamplingHeapProfiler::kMalloc, nullptr,
+                                      kSkipBaseAllocatorFrames);
+  }
   return address;
 }
 
@@ -67,11 +114,14 @@
                              size_t n,
                              size_t size,
                              void* context) {
+  ReentryGuard guard;
   void* address =
       self->next->alloc_zero_initialized_function(self->next, n, size, context);
-  SamplingHeapProfiler::RecordAlloc(address, n * size,
-                                    SamplingHeapProfiler::kMalloc, nullptr,
-                                    kSkipBaseAllocatorFrames);
+  if (LIKELY(guard)) {
+    SamplingHeapProfiler::RecordAlloc(address, n * size,
+                                      SamplingHeapProfiler::kMalloc, nullptr,
+                                      kSkipBaseAllocatorFrames);
+  }
   return address;
 }
 
@@ -79,11 +129,14 @@
                      size_t alignment,
                      size_t size,
                      void* context) {
+  ReentryGuard guard;
   void* address =
       self->next->alloc_aligned_function(self->next, alignment, size, context);
-  SamplingHeapProfiler::RecordAlloc(address, size,
-                                    SamplingHeapProfiler::kMalloc, nullptr,
-                                    kSkipBaseAllocatorFrames);
+  if (LIKELY(guard)) {
+    SamplingHeapProfiler::RecordAlloc(address, size,
+                                      SamplingHeapProfiler::kMalloc, nullptr,
+                                      kSkipBaseAllocatorFrames);
+  }
   return address;
 }
 
@@ -91,16 +144,24 @@
                 void* address,
                 size_t size,
                 void* context) {
+  ReentryGuard guard;
   // Note: size == 0 actually performs free.
   SamplingHeapProfiler::RecordFree(address);
   address = self->next->realloc_function(self->next, address, size, context);
-  SamplingHeapProfiler::RecordAlloc(address, size,
-                                    SamplingHeapProfiler::kMalloc, nullptr,
-                                    kSkipBaseAllocatorFrames);
+  if (LIKELY(guard)) {
+    SamplingHeapProfiler::RecordAlloc(address, size,
+                                      SamplingHeapProfiler::kMalloc, nullptr,
+                                      kSkipBaseAllocatorFrames);
+  }
   return address;
 }
 
 void FreeFn(const AllocatorDispatch* self, void* address, void* context) {
+  // Note: The RecordFree should be called before free_function
+  // (here and in other places).
+  // That is because we need to remove the recorded allocation sample before
+  // free_function, as once the latter is executed the address becomes available
+  // and can be allocated by another thread. That would be racy otherwise.
   SamplingHeapProfiler::RecordFree(address);
   self->next->free_function(self->next, address, context);
 }
@@ -116,12 +177,15 @@
                        void** results,
                        unsigned num_requested,
                        void* context) {
+  ReentryGuard guard;
   unsigned num_allocated = self->next->batch_malloc_function(
       self->next, size, results, num_requested, context);
-  for (unsigned i = 0; i < num_allocated; ++i) {
-    SamplingHeapProfiler::RecordAlloc(results[i], size,
-                                      SamplingHeapProfiler::kMalloc, nullptr,
-                                      kSkipBaseAllocatorFrames);
+  if (LIKELY(guard)) {
+    for (unsigned i = 0; i < num_allocated; ++i) {
+      SamplingHeapProfiler::RecordAlloc(results[i], size,
+                                        SamplingHeapProfiler::kMalloc, nullptr,
+                                        kSkipBaseAllocatorFrames);
+    }
   }
   return num_allocated;
 }
@@ -201,6 +265,7 @@
   // Preallocate the TLS slot early, so it can't cause reentracy issues
   // when sampling is started.
   ignore_result(AccumulatedBytesTLS().Get());
+  ReentryGuard::Init();
 }
 
 // static
@@ -305,9 +370,6 @@
   if (UNLIKELY(base::ThreadLocalStorage::HasBeenDestroyed()))
     return;
 
-  // TODO(alph): On MacOS it may call the hook several times for a single
-  // allocation. Handle the case.
-
   intptr_t accumulated_bytes =
       reinterpret_cast<intptr_t>(AccumulatedBytesTLS().Get());
   accumulated_bytes += size;
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index d6033f0..2ec751f 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -129,6 +129,15 @@
   # Turn this on to use ghash feature of lld for faster debug link on Windows.
   # http://blog.llvm.org/2018/01/improving-link-time-on-windows-with.html
   use_ghash = false
+
+  # Whether to enable ThinLTO optimizations. Turning ThinLTO optimizations on
+  # can substantially increase link time and binary size, but they generally
+  # also make binaries a fair bit faster.
+  #
+  # TODO(gbiv): We disable optimizations by default on most platforms because
+  # the space overhead is too great. We should use some mixture of profiles and
+  # optimization settings to better tune the size increase.
+  thin_lto_enable_optimizations = is_chromeos
 }
 
 declare_args() {
@@ -656,10 +665,20 @@
       }
     }
 
-    # Disable optimization for now because they increase binary size by too
-    # much.
-    if (use_lld && (is_android || (is_linux && !is_chromeos))) {
-      ldflags += [ "-Wl,--lto-O0" ]
+    if (use_lld) {
+      if (thin_lto_enable_optimizations) {
+        ldflags += [ "-Wl,--lto-O2" ]
+        if (is_android) {
+          # TODO(gbiv): We ideally shouldn't need to specify this; ThinLTO
+          # should be able to better manage binary size increases on its own.
+          ldflags += [
+            "-Wl,-mllvm",
+            "-Wl,-import-instr-limit=5",
+          ]
+        }
+      } else {
+        ldflags += [ "-Wl,--lto-O0" ]
+      }
     }
 
     # TODO(pcc): Re-enable this flag on Android. This will require libc++ to be
diff --git a/build/config/linux/gtk/BUILD.gn b/build/config/linux/gtk/BUILD.gn
index efd1b9b3..a2b40d82 100644
--- a/build/config/linux/gtk/BUILD.gn
+++ b/build/config/linux/gtk/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/linux/gtk/gtk.gni")
 import("//build/config/linux/pkg_config.gni")
 
 assert(is_linux, "This file should only be referenced on Linux")
@@ -14,7 +15,7 @@
   # misconfigured systems.
   packages = [
     "gmodule-2.0",
-    "gtk+-3.0",
+    "gtk+-${gtk_version}.0",
     "gthread-2.0",
   ]
 }
@@ -42,7 +43,7 @@
 
 # Depend on "gtkprint" to get this.
 pkg_config("gtkprint_internal_config") {
-  packages = [ "gtk+-unix-print-3.0" ]
+  packages = [ "gtk+-unix-print-${gtk_version}.0" ]
 }
 
 group("gtkprint") {
diff --git a/build/config/linux/gtk/gtk.gni b/build/config/linux/gtk/gtk.gni
new file mode 100644
index 0000000..1e0e6ace0
--- /dev/null
+++ b/build/config/linux/gtk/gtk.gni
@@ -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.
+
+declare_args() {
+  # The (major) version of GTK to build against.
+  gtk_version = 3
+}
+
+assert(gtk_version >= 3 && gtk_version <= 4)
diff --git a/build/fuchsia/qemu_target.py b/build/fuchsia/qemu_target.py
index 38fceb0..c221ffa29 100644
--- a/build/fuchsia/qemu_target.py
+++ b/build/fuchsia/qemu_target.py
@@ -51,8 +51,9 @@
   # Used by the context manager to ensure that QEMU is killed when the Python
   # process exits.
   def __exit__(self, exc_type, exc_val, exc_tb):
-    if self.IsStarted():
-      self.Shutdown()
+    if self._IsQemuStillRunning():
+      logging.info('Shutting down QEMU.')
+      self._qemu_process.kill()
 
   def Start(self):
     qemu_path = os.path.join(QEMU_ROOT, 'bin',
@@ -154,10 +155,6 @@
                                           stdout=stdout, stderr=stderr)
     self._WaitUntilReady();
 
-  def Shutdown(self):
-    logging.info('Shutting down QEMU.')
-    self._qemu_process.kill()
-
   def _IsQemuStillRunning(self):
     return os.waitpid(self._qemu_process.pid, os.WNOHANG)[0] == 0
 
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 0c38111..933a8fe 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -425,6 +425,7 @@
       "/DELAYLOAD:comdlg32.dll",
       "/DELAYLOAD:crypt32.dll",
       "/DELAYLOAD:cryptui.dll",
+      "/DELAYLOAD:dbghelp.dll",
       "/DELAYLOAD:dhcpcsvc.dll",
       "/DELAYLOAD:dwmapi.dll",
       "/DELAYLOAD:imagehlp.dll",
diff --git a/chrome/VERSION b/chrome/VERSION
index 5ee5ed5..a027160 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=70
 MINOR=0
-BUILD=3531
+BUILD=3533
 PATCH=0
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedStorageBridge.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedStorageBridge.java
deleted file mode 100644
index b45d9719..0000000
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedStorageBridge.java
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.feed;
-
-import org.chromium.base.Callback;
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.base.annotations.JNINamespace;
-import org.chromium.chrome.browser.profiles.Profile;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Provides access to native implementations of content storage and journal storage.
- */
-@JNINamespace("feed")
-public class FeedStorageBridge {
-    private long mNativeFeedStorageBridge;
-
-    /**
-     * Creates a {@link FeedStorageBridge} for accessing native content and journal storage
-     * implementation for the current user, and initial native side bridge.
-     *
-     * @param profile {@link Profile} of the user we are rendering the Feed for.
-     */
-    public FeedStorageBridge() {}
-
-    /**
-     * Inits native side bridge.
-     *
-     * @param profile {@link Profile} of the user we are rendering the Feed for.
-     */
-    public void init(Profile profile) {
-        mNativeFeedStorageBridge = nativeInit(profile);
-    }
-
-    /** Cleans up native half of this bridge. */
-    public void destroy() {
-        assert mNativeFeedStorageBridge != 0;
-        nativeDestroy(mNativeFeedStorageBridge);
-        mNativeFeedStorageBridge = 0;
-    }
-
-    public void loadContent(List<String> keys, Callback<Map<String, byte[]>> callback) {
-        assert mNativeFeedStorageBridge != 0;
-        String[] keysArray = keys.toArray(new String[keys.size()]);
-        nativeLoadContent(mNativeFeedStorageBridge, keysArray, callback);
-    }
-
-    public void loadContentByPrefix(String prefix, Callback<Map<String, byte[]>> callback) {
-        assert mNativeFeedStorageBridge != 0;
-        nativeLoadContentByPrefix(mNativeFeedStorageBridge, prefix, callback);
-    }
-
-    public void loadAllContentKeys(Callback<List<String>> callback) {
-        assert mNativeFeedStorageBridge != 0;
-        nativeLoadAllContentKeys(mNativeFeedStorageBridge, callback);
-    }
-
-    public void saveContent(String[] keys, byte[][] data, Callback<Boolean> callback) {
-        assert mNativeFeedStorageBridge != 0;
-        nativeSaveContent(mNativeFeedStorageBridge, keys, data, callback);
-    }
-
-    public void deleteContent(List<String> keys, Callback<Boolean> callback) {
-        assert mNativeFeedStorageBridge != 0;
-        String[] keysArray = keys.toArray(new String[keys.size()]);
-        nativeDeleteContent(mNativeFeedStorageBridge, keysArray, callback);
-    }
-
-    public void deleteContentByPrefix(String prefix, Callback<Boolean> callback) {
-        assert mNativeFeedStorageBridge != 0;
-        nativeDeleteContentByPrefix(mNativeFeedStorageBridge, prefix, callback);
-    }
-
-    public void deleteAllContent(Callback<Boolean> callback) {
-        assert mNativeFeedStorageBridge != 0;
-        nativeDeleteAllContent(mNativeFeedStorageBridge, callback);
-    }
-
-    @CalledByNative
-    private static Object createKeyAndDataMap(String[] keys, byte[][] data) {
-        assert keys.length == data.length;
-        Map<String, byte[]> valueMap = new HashMap<>(keys.length);
-        for (int i = 0; i < keys.length && i < data.length; ++i) {
-            valueMap.put(keys[i], data[i]);
-        }
-        return valueMap;
-    }
-
-    @CalledByNative
-    private static List<String> createJavaList(String[] keys) {
-        return Arrays.asList(keys);
-    }
-
-    private native long nativeInit(Profile profile);
-    private native void nativeDestroy(long nativeFeedStorageBridge);
-    private native void nativeLoadContent(
-            long nativeFeedStorageBridge, String[] keys, Callback<Map<String, byte[]>> callback);
-    private native void nativeLoadContentByPrefix(
-            long nativeFeedStorageBridge, String prefix, Callback<Map<String, byte[]>> callback);
-    private native void nativeLoadAllContentKeys(
-            long nativeFeedStorageBridge, Callback<List<String>> callback);
-    private native void nativeSaveContent(
-            long nativeFeedStorageBridge, String[] keys, byte[][] data, Callback<Boolean> callback);
-    private native void nativeDeleteContent(
-            long nativeFeedStorageBridge, String[] keys, Callback<Boolean> callback);
-    private native void nativeDeleteContentByPrefix(
-            long nativeFeedStorageBridge, String prefix, Callback<Boolean> callback);
-    private native void nativeDeleteAllContent(
-            long nativeFeedStorageBridge, Callback<Boolean> callback);
-}
diff --git a/chrome/android/feed/feed_java_sources.gni b/chrome/android/feed/feed_java_sources.gni
index 6477e6e..b289598 100644
--- a/chrome/android/feed/feed_java_sources.gni
+++ b/chrome/android/feed/feed_java_sources.gni
@@ -24,7 +24,6 @@
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedRefreshTask.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedScheduler.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSchedulerBridge.java",
-    "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedStorageBridge.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/StreamLifecycleManager.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/TestFeedScheduler.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/TestNetworkClient.java",
diff --git a/chrome/android/java/res/drawable/data_reduction_big.xml b/chrome/android/java/res/drawable/data_reduction_big.xml
index 7fd6f90..4ee498d1 100644
--- a/chrome/android/java/res/drawable/data_reduction_big.xml
+++ b/chrome/android/java/res/drawable/data_reduction_big.xml
@@ -4,22 +4,25 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-  xmlns:tools="http://schemas.android.com/tools"
-  tools:targetApi="21"
-  android:width="64dp"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="21"
+    android:width="64dp"
     android:height="64dp"
     android:viewportWidth="24.0"
     android:viewportHeight="24.0">
 
-    <path
-      android:fillColor="@color/disabled_text_color"
-        android:fillType="evenOdd"
-        android:pathData="M8.907 1.945c2.541 0 4.762 1.372 5.978 3.41l1.604-1.018C14.937 1.769 12.127 0.047 8.907 0.047 a8.86 8.86 0 0 0-8.86 8.86c0 0.067 0.008 0.13 0.01 0.196l2.01-1.463c0.598-3.235 3.435-5.695 6.84-5.695" />
-    <path
-      android:fillColor="@color/disabled_text_color"
-        android:fillType="evenOdd"
-        android:pathData="M17.31 6.104l-7.208 4.369-4.92-2.817-4.818 3.572c1.02 3.766 4.454 6.54 8.543 6.54a8.861 8.861 0 0 0 8.862-8.86c0-0.981-0.166-1.922-0.46-2.804zM8.907 15.87c-2.779 0-5.173-1.64-6.288-4l2.8-2.038 4.928 2.82 5.506-3.415c-0.173 3.686-3.217 6.633-6.946 6.633z" />
-    <path
-        android:fillType="evenOdd"
-        android:pathData="M-3-3h24v24H-3z" />
+    <group
+        android:translateX="3.0">
+      <path
+          android:fillColor="@color/disabled_text_color"
+          android:fillType="evenOdd"
+          android:pathData="M8.907 1.945c2.541 0 4.762 1.372 5.978 3.41l1.604-1.018C14.937 1.769 12.127 0.047 8.907 0.047 a8.86 8.86 0 0 0-8.86 8.86c0 0.067 0.008 0.13 0.01 0.196l2.01-1.463c0.598-3.235 3.435-5.695 6.84-5.695" />
+      <path
+          android:fillColor="@color/disabled_text_color"
+          android:fillType="evenOdd"
+          android:pathData="M17.31 6.104l-7.208 4.369-4.92-2.817-4.818 3.572c1.02 3.766 4.454 6.54 8.543 6.54a8.861 8.861 0 0 0 8.862-8.86c0-0.981-0.166-1.922-0.46-2.804zM8.907 15.87c-2.779 0-5.173-1.64-6.288-4l2.8-2.038 4.928 2.82 5.506-3.415c-0.173 3.686-3.217 6.633-6.946 6.633z" />
+      <path
+          android:fillType="evenOdd"
+          android:pathData="M-3-3h24v24H-3z" />
+    </group>
 </vector>
diff --git a/chrome/android/java/res/layout/text_message_with_link_and_icon_preference.xml b/chrome/android/java/res/layout/text_message_with_link_and_icon_preference.xml
deleted file mode 100644
index 65c14a8..0000000
--- a/chrome/android/java/res/layout/text_message_with_link_and_icon_preference.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2016 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-
-<!-- Layout used by the TextMessageWithLinkAndIconPreference. -->
-
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    style="@style/PreferenceLayout"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingBottom="24dp">
-
-    <ImageView
-        android:id="@android:id/icon"
-        tools:ignore="ContentDescription"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top" />
-
-    <org.chromium.ui.widget.TextViewWithClickableSpans
-        android:id="@android:id/title"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_toEndOf="@android:id/icon"
-        android:layout_alignTop="@android:id/icon"
-        android:layout_marginStart="16dp"
-        style="@style/PreferenceTitle" />
-
-    <org.chromium.ui.widget.TextViewWithClickableSpans
-        android:id="@android:id/summary"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_below="@android:id/title"
-        android:layout_toEndOf="@android:id/icon"
-        android:layout_marginStart="16dp"
-        style="@style/PreferenceSummary" />
-
-</RelativeLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/values/attrs.xml b/chrome/android/java/res/values/attrs.xml
index 6bd3f3f..fc71116 100644
--- a/chrome/android/java/res/values/attrs.xml
+++ b/chrome/android/java/res/values/attrs.xml
@@ -69,14 +69,6 @@
         <attr name="titleText" format="string" />
     </declare-styleable>
 
-    <declare-styleable name="TextMessageWithLinkAndIconPreference">
-        <!-- Used on a TextMessageWithLinkAndIconPreference that is followed
-             by another TextMessageWithLinkAndIconPreference to remove the
-             bottom spacing, so that related paragraphs of text are closer
-             to each other. -->
-        <attr name="noBottomSpacing" format="boolean" />
-    </declare-styleable>
-
     <declare-styleable name="SpinnerPreference">
         <!-- Used to change the SpinnerPreference to display the TextView and the Spinner on the
              same line. Devices with a smallest width of less than 360dp will still use two separate
diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml
index aab23bc..8742d35 100644
--- a/chrome/android/java/res/xml/privacy_preferences.xml
+++ b/chrome/android/java/res/xml/privacy_preferences.xml
@@ -3,7 +3,10 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
     <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
         android:key="navigation_error"
         android:title="@string/navigation_error_title"
@@ -52,7 +55,7 @@
         android:title="@string/clear_browsing_data_title"
         android:summary="@string/clear_browsing_data_summary"
         android:fragment="org.chromium.chrome.browser.preferences.privacy.ClearBrowsingDataTabsFragment" />
-    <org.chromium.chrome.browser.preferences.TextMessageWithLinkAndIconPreference
+    <org.chromium.chrome.browser.preferences.TextMessagePreference
         android:key="sync_and_services_link"
-        android:summary="@string/privacy_sync_and_services_link"/>
+        tools:summary="@string/privacy_sync_and_services_link"/>
 </PreferenceScreen>
diff --git a/chrome/android/java/res/xml/sync_and_services_preferences.xml b/chrome/android/java/res/xml/sync_and_services_preferences.xml
index 702a825..9c54cfd 100644
--- a/chrome/android/java/res/xml/sync_and_services_preferences.xml
+++ b/chrome/android/java/res/xml/sync_and_services_preferences.xml
@@ -4,6 +4,10 @@
      found in the LICENSE file. -->
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <org.chromium.chrome.browser.preferences.SignInPreference
+        android:key="sign_in"
+        android:title="@string/sign_in_to_chrome"/>
+
     <org.chromium.chrome.browser.preferences.ChromeSwitchPreference
         android:persistent="false"
         android:key="use_sync_and_all_services"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/FlingingControllerBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/FlingingControllerBridge.java
index 426fe3e7..0807210 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/FlingingControllerBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/FlingingControllerBridge.java
@@ -46,6 +46,11 @@
         mFlingingController.getMediaController().seek(positionInMs);
     }
 
+    @CalledByNative
+    public long getApproximateCurrentTime() {
+        return mFlingingController.getApproximateCurrentTime();
+    }
+
     // MediaStatusObserver implementation.
     @Override
     public void onMediaStatusUpdate(MediaStatusBridge status) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java
index a9629d5..fbba05ee6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java
@@ -57,12 +57,14 @@
         int SIGNED_IN = 3;
     }
 
+    private boolean mPersonalizedPromoEnabled = true;
     private boolean mWasGenericSigninPromoDisplayed;
     private boolean mViewEnabled;
     private @Nullable SigninPromoController mSigninPromoController;
     private final ProfileDataCache mProfileDataCache;
     private @State int mState;
     private @Nullable Runnable mStateChangedCallback;
+    private boolean mObserversAdded;
 
     /**
      * Constructor for inflating from XML.
@@ -93,6 +95,7 @@
         if (syncService != null) {
             syncService.addSyncStateChangedListener(this);
         }
+        mObserversAdded = true;
 
         update();
     }
@@ -110,6 +113,7 @@
         if (syncService != null) {
             syncService.removeSyncStateChangedListener(this);
         }
+        mObserversAdded = false;
     }
 
     /**
@@ -130,6 +134,14 @@
         }
     }
 
+    /** Enables/disables personalized promo mode. */
+    public void setPersonalizedPromoEnabled(boolean personalizedPromoEnabled) {
+        if (mPersonalizedPromoEnabled == personalizedPromoEnabled) return;
+        mPersonalizedPromoEnabled = personalizedPromoEnabled;
+        // Can't update until observers are added.
+        if (mObserversAdded) update();
+    }
+
     /** Returns the state of the preference. Not valid until registerForUpdates is called. */
     @State
     public int getState() {
@@ -154,9 +166,9 @@
             return;
         }
 
-        if (ChromePreferenceManager.getInstance().readBoolean(
-                    ChromePreferenceManager.SETTINGS_PERSONALIZED_SIGNIN_PROMO_DISMISSED, false)) {
-            // Don't show the new promo if it was dismissed by the user.
+        boolean personalizedPromoDismissed = ChromePreferenceManager.getInstance().readBoolean(
+                ChromePreferenceManager.SETTINGS_PERSONALIZED_SIGNIN_PROMO_DISMISSED, false);
+        if (!mPersonalizedPromoEnabled || personalizedPromoDismissed) {
             setupGenericPromo();
             return;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncAndServicesPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncAndServicesPreferences.java
index c022c46..8d078f47 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncAndServicesPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncAndServicesPreferences.java
@@ -77,6 +77,7 @@
     @VisibleForTesting
     public static final String FRAGMENT_PASSPHRASE_TYPE = "password_type";
 
+    private static final String PREF_SIGNIN = "sign_in";
     private static final String PREF_USE_SYNC_AND_ALL_SERVICES = "use_sync_and_all_services";
 
     private static final String PREF_SYNC_AND_PERSONALIZATION = "sync_and_personalization";
@@ -124,6 +125,7 @@
     private final ManagedPreferenceDelegate mManagedPreferenceDelegate =
             createManagedPreferenceDelegate();
 
+    private SignInPreference mSigninPreference;
     private ChromeSwitchPreference mUseSyncAndAllServices;
 
     private SigninExpandablePreferenceGroup mSyncGroup;
@@ -170,6 +172,9 @@
 
         PreferenceUtils.addPreferencesFromResource(this, R.xml.sync_and_services_preferences);
 
+        mSigninPreference = (SignInPreference) findPreference(PREF_SIGNIN);
+        mSigninPreference.setPersonalizedPromoEnabled(false);
+
         mUseSyncAndAllServices =
                 (ChromeSwitchPreference) findPreference(PREF_USE_SYNC_AND_ALL_SERVICES);
         mUseSyncAndAllServices.setOnPreferenceChangeListener(this);
@@ -294,6 +299,8 @@
         mProfileSyncService.setSetupInProgress(true);
         mProfileSyncService.addSyncStateChangedListener(this);
         updateSyncStateFromAndroidSyncSettings();
+
+        mSigninPreference.registerForUpdates();
     }
 
     @Override
@@ -318,6 +325,8 @@
         // setting up. This means: 1) If the user leaves the Sync Settings screen (via back)
         // or, 2) If the user leaves the screen by tapping on "Manage Synced Data"
         mProfileSyncService.setSetupInProgress(false);
+
+        mSigninPreference.unregisterForUpdates();
     }
 
     @Override
@@ -777,7 +786,20 @@
     }
 
     private void updatePreferences() {
-        mUseSyncAndAllServices.setChecked(UnifiedConsentServiceBridge.isUnifiedConsentGiven());
+        boolean useSyncAndAllServices = UnifiedConsentServiceBridge.isUnifiedConsentGiven();
+        String signedInAccountName = ChromeSigninController.get().getSignedInAccountName();
+        if (signedInAccountName != null) {
+            getPreferenceScreen().removePreference(mSigninPreference);
+            getPreferenceScreen().addPreference(mUseSyncAndAllServices);
+
+            mUseSyncAndAllServices.setChecked(useSyncAndAllServices);
+            mSyncGroup.setEnabled(true);
+        } else {
+            getPreferenceScreen().addPreference(mSigninPreference);
+            getPreferenceScreen().removePreference(mUseSyncAndAllServices);
+            mSyncGroup.setExpanded(false);
+            mSyncGroup.setEnabled(false);
+        }
 
         mSearchSuggestions.setChecked(mPrefServiceBridge.isSearchSuggestEnabled());
         mNetworkPredictions.setChecked(mPrefServiceBridge.getNetworkPredictionEnabled());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessagePreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessagePreference.java
index 352c4af..5b33b6a3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessagePreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessagePreference.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.preferences;
 
 import android.content.Context;
+import android.text.TextUtils;
 import android.text.method.LinkMovementMethod;
 import android.util.AttributeSet;
 import android.view.View;
@@ -26,9 +27,18 @@
     protected void onBindView(View view) {
         super.onBindView(view);
 
-        TextView textView = (TextView) view.findViewById(android.R.id.title);
-        textView.setSingleLine(false);
-        textView.setMaxLines(Integer.MAX_VALUE);
-        textView.setMovementMethod(LinkMovementMethod.getInstance());
+        TextView titleView = (TextView) view.findViewById(android.R.id.title);
+        if (!TextUtils.isEmpty(getTitle())) {
+            titleView.setVisibility(View.VISIBLE);
+            titleView.setSingleLine(false);
+            titleView.setMaxLines(Integer.MAX_VALUE);
+            titleView.setMovementMethod(LinkMovementMethod.getInstance());
+        } else {
+            titleView.setVisibility(View.GONE);
+        }
+
+        TextView summaryView = (TextView) view.findViewById(android.R.id.summary);
+        // No need to manually toggle visibility for summary - it is done in super.onBindView.
+        summaryView.setMovementMethod(LinkMovementMethod.getInstance());
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessageWithLinkAndIconPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessageWithLinkAndIconPreference.java
deleted file mode 100644
index 3c5ce3c..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessageWithLinkAndIconPreference.java
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.preferences;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Paint.FontMetrics;
-import android.support.v4.view.ViewCompat;
-import android.text.SpannableString;
-import android.text.method.LinkMovementMethod;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import org.chromium.chrome.R;
-import org.chromium.ui.text.NoUnderlineClickableSpan;
-import org.chromium.ui.text.SpanApplier;
-
-/**
- * A text message whose summary can contain a link. The link should be denoted by "<link></link>"
- * tags and the action upon its clicking is defined by |setLinkClickDelegate()|.
- */
-public class TextMessageWithLinkAndIconPreference extends TextMessagePreference {
-    private Runnable mLinkClickDelegate;
-    private boolean mNoBottomSpacing;
-
-    /**
-     * Constructor for inflating from XML.
-     */
-    public TextMessageWithLinkAndIconPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setLayoutResource(R.layout.text_message_with_link_and_icon_preference);
-
-        TypedArray a = context.obtainStyledAttributes(
-                attrs, R.styleable.TextMessageWithLinkAndIconPreference);
-        mNoBottomSpacing = a.getBoolean(
-                R.styleable.TextMessageWithLinkAndIconPreference_noBottomSpacing, false);
-        a.recycle();
-
-        setSummary(getSummary()); // Apply spans to the summary loaded from XML.
-    }
-
-    /**
-     * @param delegate A delegate to handle link click.
-     */
-    public void setLinkClickDelegate(Runnable delegate) {
-        mLinkClickDelegate = delegate;
-    }
-
-    @Override
-    public void setSummary(CharSequence summary) {
-        // If there is no link in the summary, invoke the default behavior.
-        String summaryString = summary.toString();
-        if (!summaryString.contains("<link>") || !summaryString.contains("</link>")) {
-            super.setSummary(summary);
-            return;
-        }
-
-        // Linkify <link></link> span.
-        final SpannableString summaryWithLink = SpanApplier.applySpans(summaryString,
-                new SpanApplier.SpanInfo(
-                        "<link>", "</link>", new NoUnderlineClickableSpan((widget) -> {
-                            if (mLinkClickDelegate != null) mLinkClickDelegate.run();
-                        })));
-
-        super.setSummary(summaryWithLink);
-    }
-
-    @Override
-    public View onCreateView(ViewGroup parent) {
-        View view = super.onCreateView(parent);
-
-        if (mNoBottomSpacing) {
-            ViewCompat.setPaddingRelative(view, ViewCompat.getPaddingStart(view),
-                    view.getPaddingTop(), ViewCompat.getPaddingEnd(view), 0);
-        }
-
-        ((TextView) view.findViewById(android.R.id.summary)).setMovementMethod(
-                LinkMovementMethod.getInstance());
-
-        // The icon is aligned to the top of the text view, which can be higher than the
-        // ascender line of the text, and makes it look aligned improperly.
-        TextView textView = (TextView) view.findViewById(
-                getTitle() != null ? android.R.id.title : android.R.id.summary);
-        FontMetrics metrics = textView.getPaint().getFontMetrics();
-        ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
-        ViewCompat.setPaddingRelative(icon, 0, (int) Math.ceil(metrics.ascent - metrics.top), 0, 0);
-
-        return view;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java
index a84b85a4..bd5cfab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java
@@ -27,8 +27,9 @@
 import org.chromium.chrome.browser.preferences.PreferenceUtils;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.SyncAndServicesPreferences;
-import org.chromium.chrome.browser.preferences.TextMessageWithLinkAndIconPreference;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.ui.text.NoUnderlineClickableSpan;
+import org.chromium.ui.text.SpanApplier;
 
 /**
  * Fragment to keep track of the all the privacy related preferences.
@@ -81,13 +82,14 @@
             preferenceScreen.removePreference(findPreference(PREF_CONTEXTUAL_SEARCH));
             preferenceScreen.removePreference(findPreference(PREF_USAGE_AND_CRASH_REPORTING));
 
-            TextMessageWithLinkAndIconPreference syncAndServicesLink =
-                    (TextMessageWithLinkAndIconPreference) findPreference(
-                            PREF_SYNC_AND_SERVICES_LINK);
-            syncAndServicesLink.setLinkClickDelegate(() -> {
+            Preference syncAndServicesLink = findPreference(PREF_SYNC_AND_SERVICES_LINK);
+            NoUnderlineClickableSpan linkSpan = new NoUnderlineClickableSpan(view -> {
                 PreferencesLauncher.launchSettingsPage(
                         getActivity(), SyncAndServicesPreferences.class.getName());
             });
+            syncAndServicesLink.setSummary(
+                    SpanApplier.applySpans(getString(R.string.privacy_sync_and_services_link),
+                            new SpanApplier.SpanInfo("<link>", "</link>", linkSpan)));
 
             updateSummaries();
             return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java
new file mode 100644
index 0000000..e6890488
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.previews;
+
+import org.chromium.content_public.browser.WebContents;
+
+/**
+ * Java bridge class to C++ Previews code.
+ */
+public final class PreviewsAndroidBridge {
+    private static PreviewsAndroidBridge sBridge;
+
+    public static PreviewsAndroidBridge getInstance() {
+        if (sBridge == null) {
+            sBridge = new PreviewsAndroidBridge();
+        }
+        return sBridge;
+    }
+
+    private final long mNativePreviewsAndroidBridge;
+
+    private PreviewsAndroidBridge() {
+        mNativePreviewsAndroidBridge = nativeInit();
+    }
+
+    public boolean shouldShowPreviewUI(WebContents webContents) {
+        return nativeShouldShowPreviewUI(mNativePreviewsAndroidBridge, webContents);
+    }
+
+    private native long nativeInit();
+    private native boolean nativeShouldShowPreviewUI(
+            long nativePreviewsAndroidBridge, WebContents webContents);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModel.java
index f7fbe7a..e9c20c4c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModel.java
@@ -28,6 +28,7 @@
 import org.chromium.chrome.browser.omnibox.AutocompleteController;
 import org.chromium.chrome.browser.omnibox.OmniboxUrlEmphasizer;
 import org.chromium.chrome.browser.omnibox.UrlBarData;
+import org.chromium.chrome.browser.previews.PreviewsAndroidBridge;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
 import org.chromium.chrome.browser.tab.Tab;
@@ -325,8 +326,9 @@
 
     @Override
     public boolean isPreview() {
-        // TODO(crbug.com/871839): Link this up with the native previews state.
-        return false;
+        return hasTab() && mTab.getWebContents() != null && !mTab.isNativePage()
+                && !mTab.isShowingInterstitialPage()
+                && PreviewsAndroidBridge.getInstance().shouldShowPreviewUI(mTab.getWebContents());
     }
 
     @Override
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 1ee5f0db..c515180d 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1067,6 +1067,7 @@
   "java/src/org/chromium/chrome/browser/payments/SslValidityChecker.java",
   "java/src/org/chromium/chrome/browser/payments/UriUtils.java",
   "java/src/org/chromium/chrome/browser/payments/ui/ContactDetailsSection.java",
+  "java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java",
   "java/src/org/chromium/chrome/browser/widget/prefeditor/Completable.java",
   "java/src/org/chromium/chrome/browser/widget/prefeditor/DropdownFieldAdapter.java",
   "java/src/org/chromium/chrome/browser/widget/prefeditor/EditableOption.java",
@@ -1155,7 +1156,6 @@
   "java/src/org/chromium/chrome/browser/preferences/SyncedAccountPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/TextAndButtonPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/TextMessagePreference.java",
-  "java/src/org/chromium/chrome/browser/preferences/TextMessageWithLinkAndIconPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/TextScalePreference.java",
   "java/src/org/chromium/chrome/browser/preferences/autofill/AndroidPaymentAppPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/autofill/AndroidPaymentAppsFragment.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java
index 154c4461..5791e99 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java
@@ -32,6 +32,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.ntp.IncognitoNewTabPage;
 import org.chromium.chrome.browser.preferences.Preferences;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
@@ -536,7 +537,7 @@
 
     /**
      * Verifies that permissions granted outside of VR persist while in VR, even after the page is
-     * refreshed. Automation of a manutal test from https://crbug.com/861941.
+     * refreshed. Automation of a manual test from https://crbug.com/861941.
      */
     @Test
     @Restriction({RESTRICTION_TYPE_VIEWER_DAYDREAM})
@@ -576,4 +577,22 @@
         mVrBrowserTestFramework.endTest();
         server.stopAndDestroyServer();
     }
+
+    /**
+     * Verifies that an NFC scan in 2D Chrome while viewing a native page still successfully enters
+     * the VR browser. Automation of a manual test from https://crbug.com/862155.
+     */
+    @Test
+    @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM)
+    @MediumTest
+    public void testNfcScanOnNativePage() throws InterruptedException {
+        // We can't loop over all the native URLs since multiple NFC entries in a short timespan
+        // isn't possible. So, just pick the native history page as a suitable one.
+        mTestRule.loadUrl(UrlConstants.NATIVE_HISTORY_URL, PAGE_LOAD_TIMEOUT_S);
+        NfcSimUtils.simNfcScanUntilVrEntry(mTestRule.getActivity());
+        Assert.assertTrue("Browser is not in VR", VrShellDelegate.isInVr());
+        Assert.assertTrue("Browser entered VR, but is not on a native page",
+                mTestRule.getActivity().getActivityTab().isNativePage());
+        VrBrowserTransitionUtils.forceExitVr();
+    }
 }
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index e628696..aba1f45 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -383,7 +383,7 @@
     deps += [
       "//ash/components/quick_launch/public/mojom:constants",
       "//mash/common",
-      "//services/ui/public/interfaces:constants",
+      "//services/ws/public/mojom:constants",
     ]
   }
 }
@@ -453,7 +453,6 @@
   }
   if (is_chromeos) {
     packaged_services += [
-      "//ash/components/autoclick:manifest",
       "//ash/components/quick_launch:manifest",
       "//ash/components/shortcut_viewer:manifest",
       "//ash/components/tap_visualizer:manifest",
diff --git a/chrome/app/DEPS b/chrome/app/DEPS
index 71272b2..5b58f94 100644
--- a/chrome/app/DEPS
+++ b/chrome/app/DEPS
@@ -35,6 +35,7 @@
   "+remoting/client/plugin",
   "+sandbox",
   "+services/ui/public",
+  "+services/ws/public",
   "+third_party/breakpad/breakpad",
   "+third_party/crashpad/crashpad",
 ]
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 2cc6f89..969464d 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -3861,6 +3861,12 @@
         <message name="IDS_EXTENSIONS_LOAD_ERROR_MESSAGE" desc="The message which tells the user that an extension failed to load.">
           Failed to load extension from:
         </message>
+        <message name="IDS_EXTENSIONS_WANTS_ACCESS_TO_SITE" desc="The message shown on mouseover when an extension wants to run on a specific site.">
+          Wants access to this site
+        </message>
+        <message name="IDS_EXTENSIONS_HAS_ACCESS_TO_SITE" desc="The message shown on mouseover when an extension currently can run on a specific site.">
+          Has access to this site
+        </message>
         <if expr="not use_titlecase">
           <message name="IDS_EXTENSIONS_CONTEXT_MENU_PAGE_ACCESS" desc="The label in an extension's context menu for the submenu specifying whether or not the extension can run on the current page (sentence case).">
             Allow access
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 772d74d..978919b4 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2439,6 +2439,8 @@
       "policy/cloud/user_policy_signin_service_mobile.h",
       "prerender/external_prerender_handler_android.cc",
       "prerender/external_prerender_handler_android.h",
+      "previews/android/previews_android_bridge.cc",
+      "previews/android/previews_android_bridge.h",
       "profiles/profile_android.cc",
       "profiles/profile_android.h",
       "search/contextual_search_policy_handler_android.cc",
@@ -3087,8 +3089,8 @@
       "//components/services/font/public/interfaces",
       "//services/ui/public/cpp/input_devices",
       "//services/ui/public/cpp/input_devices:input_device_controller",
-      "//services/ui/public/interfaces",
       "//services/ui/ws2:lib",
+      "//services/ws/public/mojom",
       "//ui/ozone",
     ]
     allow_circular_includes_from += [ "//chrome/browser/chromeos" ]
@@ -4419,8 +4421,6 @@
       "android/feed/feed_network_bridge.h",
       "android/feed/feed_scheduler_bridge.cc",
       "android/feed/feed_scheduler_bridge.h",
-      "android/feed/feed_storage_bridge.cc",
-      "android/feed/feed_storage_bridge.h",
     ]
     deps += [ "//components/feed/core:feed_core" ]
   }
@@ -4633,6 +4633,7 @@
       "../android/java/src/org/chromium/chrome/browser/preferences/privacy/BrowsingDataCounterBridge.java",
       "../android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferenceBridge.java",
       "../android/java/src/org/chromium/chrome/browser/prerender/ExternalPrerenderHandler.java",
+      "../android/java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java",
       "../android/java/src/org/chromium/chrome/browser/profiles/Profile.java",
       "../android/java/src/org/chromium/chrome/browser/profiles/ProfileDownloader.java",
       "../android/java/src/org/chromium/chrome/browser/profiles/ProfileManagerUtils.java",
@@ -4691,7 +4692,6 @@
         "../android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalBridge.java",
         "../android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNetworkBridge.java",
         "../android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSchedulerBridge.java",
-        "../android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedStorageBridge.java",
       ]
     }
 
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 2276658..61b32da 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -80,6 +80,7 @@
   "+services/ui/common",
   "+services/ui/public",
   "+services/ui/service.h",
+  "+services/ws/public",
   "+services/video_capture/public",
   "+services/viz/privileged",
   "+skia/ext",
diff --git a/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.cc b/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.cc
index d52daf4..cf24a37a 100644
--- a/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.cc
+++ b/chrome/browser/android/contextual_suggestions/contextual_suggestions_bridge.cc
@@ -27,7 +27,6 @@
 #include "components/policy/core/common/policy_service.h"
 #include "components/policy/policy_constants.h"
 #include "components/ukm/content/source_url_recorder.h"
-#include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
 #include "jni/ContextualSuggestionsBridge_jni.h"
 #include "ui/gfx/android/java_bitmap.h"
@@ -40,13 +39,6 @@
 using base::android::ScopedJavaGlobalRef;
 using base::android::ScopedJavaLocalRef;
 
-namespace {
-// Keep this in sync with CHROME_CONTEXTUAL_SUGGESTIONS_REFERRER. They should
-// together.
-const char kContextualSuggestionsReferrerURL[] =
-    "https://goto.google.com/explore-on-content-viewer";
-}  // namespace
-
 namespace contextual_suggestions {
 
 // A whitelisted method to inject synthetic field trials to Chrome Metrics.
@@ -166,19 +158,8 @@
       static_cast<contextual_suggestions::ContextualSuggestionsEvent>(
           j_event_id);
 
-  const content::NavigationEntry* navigation_entry =
-      web_contents->GetController().GetVisibleEntry();
-  const std::string& referrer_url =
-      navigation_entry == nullptr ? ""
-                                  : navigation_entry->GetReferrer().url.spec();
-  ArticleSource article_source = ArticleSource::OTHER;
-  if (referrer_url == kContextualSuggestionsReferrerURL) {
-    article_source = ArticleSource::CONTEXTUAL_SUGGESTIONS;
-  }
-
-  service_proxy_->ReportEvent(ukm_source_id,
-                              web_contents->GetLastCommittedURL().spec(),
-                              article_source, event);
+  service_proxy_->ReportEvent(
+      ukm_source_id, web_contents->GetLastCommittedURL().spec(), event);
 }
 
 void ContextualSuggestionsBridge::OnSuggestionsAvailable(
diff --git a/chrome/browser/android/feed/feed_host_service_factory.cc b/chrome/browser/android/feed/feed_host_service_factory.cc
index 6a7a9eb..834785f 100644
--- a/chrome/browser/android/feed/feed_host_service_factory.cc
+++ b/chrome/browser/android/feed/feed_host_service_factory.cc
@@ -21,7 +21,6 @@
 #include "components/feed/core/feed_journal_database.h"
 #include "components/feed/core/feed_networking_host.h"
 #include "components/feed/core/feed_scheduler_host.h"
-#include "components/feed/core/feed_storage_database.h"
 #include "components/image_fetcher/core/image_fetcher_impl.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/version_info/version_info.h"
@@ -90,16 +89,14 @@
       profile->GetPrefs(), g_browser_process->local_state(),
       base::DefaultClock::GetInstance());
 
-  auto storage_database = std::make_unique<FeedStorageDatabase>(feed_dir);
-
   auto content_database = std::make_unique<FeedContentDatabase>(feed_dir);
 
   auto journal_database = std::make_unique<FeedJournalDatabase>(feed_dir);
 
   return new FeedHostService(
       std::move(image_manager), std::move(networking_host),
-      std::move(scheduler_host), std::move(storage_database),
-      std::move(content_database), std::move(journal_database));
+      std::move(scheduler_host), std::move(content_database),
+      std::move(journal_database));
 }
 
 content::BrowserContext* FeedHostServiceFactory::GetBrowserContextToUse(
diff --git a/chrome/browser/android/feed/feed_storage_bridge.cc b/chrome/browser/android/feed/feed_storage_bridge.cc
deleted file mode 100644
index 3393ad2..0000000
--- a/chrome/browser/android/feed/feed_storage_bridge.cc
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/android/feed/feed_storage_bridge.h"
-
-#include <jni.h>
-
-#include <string>
-#include <vector>
-
-#include "base/android/callback_android.h"
-#include "base/android/jni_array.h"
-#include "base/android/jni_string.h"
-#include "base/bind.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chrome/browser/android/feed/feed_host_service_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_android.h"
-#include "components/feed/core/feed_host_service.h"
-#include "components/feed/core/feed_storage_database.h"
-#include "jni/FeedStorageBridge_jni.h"
-#include "ui/gfx/android/java_bitmap.h"
-#include "ui/gfx/image/image.h"
-
-namespace feed {
-
-using base::android::AppendJavaStringArrayToStringVector;
-using base::android::AttachCurrentThread;
-using base::android::ConvertJavaStringToUTF8;
-using base::android::JavaArrayOfByteArrayToStringVector;
-using base::android::JavaIntArrayToIntVector;
-using base::android::JavaRef;
-using base::android::JavaParamRef;
-using base::android::ScopedJavaGlobalRef;
-using base::android::ScopedJavaLocalRef;
-using base::android::ToJavaArrayOfByteArray;
-using base::android::ToJavaArrayOfStrings;
-
-static jlong JNI_FeedStorageBridge_Init(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& j_this,
-    const JavaParamRef<jobject>& j_profile) {
-  Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
-  FeedHostService* host_service =
-      FeedHostServiceFactory::GetForBrowserContext(profile);
-  DCHECK(host_service);
-  FeedStorageDatabase* feed_storage_database =
-      host_service->GetStorageDatabase();
-  DCHECK(feed_storage_database);
-  FeedStorageBridge* native_storage_bridge =
-      new FeedStorageBridge(feed_storage_database);
-  return reinterpret_cast<intptr_t>(native_storage_bridge);
-}
-
-FeedStorageBridge::FeedStorageBridge(FeedStorageDatabase* feed_storage_database)
-    : feed_storage_database_(feed_storage_database), weak_ptr_factory_(this) {}
-
-FeedStorageBridge::~FeedStorageBridge() = default;
-
-void FeedStorageBridge::Destroy(JNIEnv* env, const JavaRef<jobject>& j_this) {
-  delete this;
-}
-
-void FeedStorageBridge::LoadContent(JNIEnv* j_env,
-                                    const JavaRef<jobject>& j_this,
-                                    const JavaRef<jobjectArray>& j_keys,
-                                    const JavaRef<jobject>& j_callback) {
-  std::vector<std::string> keys;
-  AppendJavaStringArrayToStringVector(j_env, j_keys.obj(), &keys);
-  ScopedJavaGlobalRef<jobject> callback(j_callback);
-
-  feed_storage_database_->LoadContent(
-      keys, base::BindOnce(&FeedStorageBridge::OnLoadContentDone,
-                           weak_ptr_factory_.GetWeakPtr(), callback));
-}
-
-void FeedStorageBridge::LoadContentByPrefix(
-    JNIEnv* j_env,
-    const JavaRef<jobject>& j_this,
-    const JavaRef<jstring>& j_prefix,
-    const JavaRef<jobject>& j_callback) {
-  std::string prefix = ConvertJavaStringToUTF8(j_env, j_prefix);
-  ScopedJavaGlobalRef<jobject> callback(j_callback);
-
-  feed_storage_database_->LoadContentByPrefix(
-      prefix, base::BindOnce(&FeedStorageBridge::OnLoadContentDone,
-                             weak_ptr_factory_.GetWeakPtr(), callback));
-}
-
-void FeedStorageBridge::LoadAllContentKeys(JNIEnv* j_env,
-                                           const JavaRef<jobject>& j_this,
-                                           const JavaRef<jobject>& j_callback) {
-  ScopedJavaGlobalRef<jobject> callback(j_callback);
-
-  feed_storage_database_->LoadAllContentKeys(
-      base::BindOnce(&FeedStorageBridge::OnLoadAllContentKeysDone,
-                     weak_ptr_factory_.GetWeakPtr(), callback));
-}
-
-void FeedStorageBridge::SaveContent(JNIEnv* j_env,
-                                    const JavaRef<jobject>& j_this,
-                                    const JavaRef<jobjectArray>& j_keys,
-                                    const JavaRef<jobjectArray>& j_data,
-                                    const JavaRef<jobject>& j_callback) {
-  std::vector<std::string> keys;
-  std::vector<std::string> data;
-  AppendJavaStringArrayToStringVector(j_env, j_keys.obj(), &keys);
-  JavaArrayOfByteArrayToStringVector(j_env, j_data.obj(), &data);
-  ScopedJavaGlobalRef<jobject> callback(j_callback);
-
-  DCHECK_EQ(keys.size(), data.size());
-  std::vector<FeedStorageDatabase::KeyAndData> pairs;
-  for (size_t i = 0; i < keys.size() && i < data.size(); ++i) {
-    pairs.emplace_back(keys[i], data[i]);
-  }
-
-  feed_storage_database_->SaveContent(
-      std::move(pairs),
-      base::BindOnce(&FeedStorageBridge::OnStorageCommitDone,
-                     weak_ptr_factory_.GetWeakPtr(), callback));
-}
-
-void FeedStorageBridge::DeleteContent(JNIEnv* j_env,
-                                      const JavaRef<jobject>& j_this,
-                                      const JavaRef<jobjectArray>& j_keys,
-                                      const JavaRef<jobject>& j_callback) {
-  std::vector<std::string> keys;
-  AppendJavaStringArrayToStringVector(j_env, j_keys.obj(), &keys);
-  ScopedJavaGlobalRef<jobject> callback(j_callback);
-
-  feed_storage_database_->DeleteContent(
-      keys, base::BindOnce(&FeedStorageBridge::OnStorageCommitDone,
-                           weak_ptr_factory_.GetWeakPtr(), callback));
-}
-
-void FeedStorageBridge::DeleteContentByPrefix(
-    JNIEnv* j_env,
-    const JavaRef<jobject>& j_this,
-    const JavaRef<jstring>& j_prefix,
-    const JavaRef<jobject>& j_callback) {
-  std::string prefix = ConvertJavaStringToUTF8(j_env, j_prefix);
-  ScopedJavaGlobalRef<jobject> callback(j_callback);
-
-  feed_storage_database_->DeleteContentByPrefix(
-      prefix, base::BindOnce(&FeedStorageBridge::OnStorageCommitDone,
-                             weak_ptr_factory_.GetWeakPtr(), callback));
-}
-
-void FeedStorageBridge::DeleteAllContent(JNIEnv* j_env,
-                                         const JavaRef<jobject>& j_this,
-                                         const JavaRef<jobject>& j_callback) {
-  ScopedJavaGlobalRef<jobject> callback(j_callback);
-
-  feed_storage_database_->DeleteAllContent(
-      base::BindOnce(&FeedStorageBridge::OnStorageCommitDone,
-                     weak_ptr_factory_.GetWeakPtr(), callback));
-}
-
-void FeedStorageBridge::OnLoadContentDone(
-    ScopedJavaGlobalRef<jobject> callback,
-    std::vector<FeedStorageDatabase::KeyAndData> pairs) {
-  std::vector<std::string> keys;
-  std::vector<std::string> data;
-  for (auto pair : pairs) {
-    keys.push_back(std::move(pair.first));
-    data.push_back(std::move(pair.second));
-  }
-
-  JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobjectArray> j_keys = ToJavaArrayOfStrings(env, keys);
-  ScopedJavaLocalRef<jobjectArray> j_data = ToJavaArrayOfByteArray(env, data);
-
-  // Ceate Java Map by JNI call.
-  ScopedJavaLocalRef<jobject> j_pairs =
-      Java_FeedStorageBridge_createKeyAndDataMap(env, j_keys, j_data);
-  RunObjectCallbackAndroid(callback, j_pairs);
-}
-
-void FeedStorageBridge::OnLoadAllContentKeysDone(
-    ScopedJavaGlobalRef<jobject> callback,
-    std::vector<std::string> keys) {
-  JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobjectArray> j_keys = ToJavaArrayOfStrings(env, keys);
-
-  // Ceate Java List by JNI call.
-  ScopedJavaLocalRef<jobject> j_keys_list =
-      Java_FeedStorageBridge_createJavaList(env, j_keys);
-  RunObjectCallbackAndroid(callback, j_keys_list);
-}
-
-void FeedStorageBridge::OnStorageCommitDone(
-    ScopedJavaGlobalRef<jobject> callback,
-    bool success) {
-  RunBooleanCallbackAndroid(callback, success);
-}
-
-}  // namespace feed
diff --git a/chrome/browser/android/feed/feed_storage_bridge.h b/chrome/browser/android/feed/feed_storage_bridge.h
deleted file mode 100644
index 19b7d8f..0000000
--- a/chrome/browser/android/feed/feed_storage_bridge.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ANDROID_FEED_FEED_STORAGE_BRIDGE_H_
-#define CHROME_BROWSER_ANDROID_FEED_FEED_STORAGE_BRIDGE_H_
-
-#include "base/android/scoped_java_ref.h"
-#include "base/memory/weak_ptr.h"
-#include "components/feed/core/feed_storage_database.h"
-
-namespace feed {
-
-class FeedStorageDatabase;
-
-// Native counterpart of FeedStorageBridge.java. Holds non-owning pointers
-// to native implementation, to which operations are delegated. Results are
-// passed back by a single argument callback so
-// base::android::RunBooleanCallbackAndroid() and
-// base::android::RunObjectCallbackAndroid() can be used. This bridge is
-// instantiated, owned, and destroyed from Java.
-class FeedStorageBridge {
- public:
-  explicit FeedStorageBridge(FeedStorageDatabase* feed_Storage_database);
-  ~FeedStorageBridge();
-
-  void Destroy(JNIEnv* j_env, const base::android::JavaRef<jobject>& j_this);
-
-  void LoadContent(JNIEnv* j_env,
-                   const base::android::JavaRef<jobject>& j_this,
-                   const base::android::JavaRef<jobjectArray>& j_keys,
-                   const base::android::JavaRef<jobject>& j_callback);
-  void LoadContentByPrefix(JNIEnv* j_env,
-                           const base::android::JavaRef<jobject>& j_this,
-                           const base::android::JavaRef<jstring>& j_prefix,
-                           const base::android::JavaRef<jobject>& j_callback);
-  void LoadAllContentKeys(JNIEnv* j_env,
-                          const base::android::JavaRef<jobject>& j_this,
-                          const base::android::JavaRef<jobject>& j_callback);
-  void SaveContent(JNIEnv* j_env,
-                   const base::android::JavaRef<jobject>& j_this,
-                   const base::android::JavaRef<jobjectArray>& j_keys,
-                   const base::android::JavaRef<jobjectArray>& j_data,
-                   const base::android::JavaRef<jobject>& j_callback);
-  void DeleteContent(JNIEnv* j_env,
-                     const base::android::JavaRef<jobject>& j_this,
-                     const base::android::JavaRef<jobjectArray>& j_keys,
-                     const base::android::JavaRef<jobject>& j_callback);
-  void DeleteContentByPrefix(JNIEnv* j_env,
-                             const base::android::JavaRef<jobject>& j_this,
-                             const base::android::JavaRef<jstring>& j_prefix,
-                             const base::android::JavaRef<jobject>& j_callback);
-  void DeleteAllContent(JNIEnv* j_env,
-                        const base::android::JavaRef<jobject>& j_this,
-                        const base::android::JavaRef<jobject>& j_callback);
-
- private:
-  void OnLoadContentDone(base::android::ScopedJavaGlobalRef<jobject> callback,
-                         std::vector<FeedStorageDatabase::KeyAndData> pairs);
-  void OnLoadAllContentKeysDone(
-      base::android::ScopedJavaGlobalRef<jobject> callback,
-      std::vector<std::string> keys);
-  void OnStorageCommitDone(base::android::ScopedJavaGlobalRef<jobject> callback,
-                           bool success);
-
-  FeedStorageDatabase* feed_storage_database_;
-
-  base::WeakPtrFactory<FeedStorageBridge> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(FeedStorageBridge);
-};
-
-}  // namespace feed
-
-#endif  // CHROME_BROWSER_ANDROID_FEED_FEED_STORAGE_BRIDGE_H_
diff --git a/chrome/browser/ash_service_registry.cc b/chrome/browser/ash_service_registry.cc
index e6fe748..9de58bc3 100644
--- a/chrome/browser/ash_service_registry.cc
+++ b/chrome/browser/ash_service_registry.cc
@@ -20,7 +20,7 @@
 #include "chrome/browser/chromeos/accessibility/ax_host_service.h"
 #include "chrome/browser/chromeos/prefs/pref_connector_service.h"
 #include "content/public/common/service_manager_connection.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/ui_base_features.h"
 
@@ -36,7 +36,6 @@
 
 // Services shared between mash and non-mash configs.
 constexpr Service kCommonServices[] = {
-    {"autoclick_app", IDS_ASH_AUTOCLICK_APP_NAME},
     {quick_launch::mojom::kServiceName, IDS_ASH_QUICK_LAUNCH_APP_NAME},
     {shortcut_viewer::mojom::kServiceName, IDS_ASH_SHORTCUT_VIEWER_APP_NAME},
     {tap_visualizer::mojom::kServiceName, IDS_ASH_TAP_VISUALIZER_APP_NAME},
diff --git a/chrome/browser/browser_process_platform_part_chromeos.cc b/chrome/browser/browser_process_platform_part_chromeos.cc
index 382deb1..31215f0 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.cc
+++ b/chrome/browser/browser_process_platform_part_chromeos.cc
@@ -46,7 +46,7 @@
 #include "services/service_manager/runner/common/client_util.h"
 #include "services/ui/public/cpp/input_devices/input_device_controller.h"
 #include "services/ui/public/cpp/input_devices/input_device_controller_client.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/base/ui_base_features.h"
 
 BrowserProcessPlatformPart::BrowserProcessPlatformPart()
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 7da42548..6018884 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -455,7 +455,7 @@
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 #if BUILDFLAG(ENABLE_MUS)
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #endif
 
 #if BUILDFLAG(ENABLE_PLUGINS)
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc
index c6ede814..4cc5bd3 100644
--- a/chrome/browser/chrome_content_browser_client_unittest.cc
+++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -49,7 +49,7 @@
 #if defined(OS_CHROMEOS)
 #include "ash/public/interfaces/constants.mojom.h"
 #include "content/public/common/service_names.mojom.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #endif
 
 using content::BrowsingDataFilterBuilder;
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json
index aa17b55..e63ab8b 100644
--- a/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -30,7 +30,6 @@
       },
       "requires": {
         "apps": [ "app_registry" ],
-        "autoclick_app": [ "chromeos:autoclick" ],
         "ash": [ "system_ui", "test", "display" ],
         // Only used in classic ash case.
         "ash_pref_connector": [ "pref_connector" ],
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index dd643d7..41d9e54 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -595,8 +595,6 @@
     "customization/customization_wallpaper_util.h",
     "dbus/chrome_features_service_provider.cc",
     "dbus/chrome_features_service_provider.h",
-    "dbus/chrome_proxy_resolution_service_provider_delegate.cc",
-    "dbus/chrome_proxy_resolution_service_provider_delegate.h",
     "dbus/component_updater_service_provider.cc",
     "dbus/component_updater_service_provider.h",
     "dbus/drive_file_stream_service_provider.cc",
@@ -2317,6 +2315,7 @@
     "//components/drive/chromeos/file_cache_unittest.cc",
     "//components/drive/directory_loader_unittest.cc",
     "//components/drive/drive_file_util_unittest.cc",
+    "//components/drive/drive_operation_queue_unittest.cc",
     "//components/drive/fake_file_system_unittest.cc",
     "//components/drive/file_change_unittest.cc",
     "//components/drive/file_system/copy_operation_unittest.cc",
diff --git a/chrome/browser/chromeos/accessibility/DEPS b/chrome/browser/chromeos/accessibility/DEPS
index b0f20e2..1269720 100644
--- a/chrome/browser/chromeos/accessibility/DEPS
+++ b/chrome/browser/chromeos/accessibility/DEPS
@@ -12,10 +12,6 @@
     "+ash/magnifier/magnification_controller.h",
     "+ash/shell.h",
   ],
-  "select_to_speak_event_handler\.cc": [
-    # TODO(mash): Port the EventHandler to ash. http://crbug.com/874295
-    "+ash/system/accessibility/select_to_speak_tray_utils.h",
-  ],
   "switch_access_event_handler\.cc": [
     # TODO(mash): Fix. https://crbug.com/854025
     "+ash/shell.h",
diff --git a/chrome/browser/chromeos/accessibility/dictation_chromeos.cc b/chrome/browser/chromeos/accessibility/dictation_chromeos.cc
index e574962..e26750d 100644
--- a/chrome/browser/chromeos/accessibility/dictation_chromeos.cc
+++ b/chrome/browser/chromeos/accessibility/dictation_chromeos.cc
@@ -32,14 +32,19 @@
   return user_locale.empty() ? kDefaultProfileLocale : user_locale;
 }
 
+// Returns the current input context. This may change during the session, even
+// if the IME engine does not change, because remote mojo applications have
+// their own instance of InputMethodChromeOS. See comment on InputMethodBridge.
+ui::IMEInputContextHandlerInterface* GetInputContext() {
+  return ui::IMEBridge::Get()->GetInputContextHandler();
+}
+
 }  // namespace
 
 DictationChromeos::DictationChromeos(Profile* profile)
-    : profile_(profile), weak_ptr_factory_(this) {
-  composition_ = std::make_unique<ui::CompositionText>();
-  input_context_ = ui::IMEBridge::Get()->GetInputContextHandler();
-  ui::IMEBridge::Get()->SetObserver(this);
-}
+    : composition_(std::make_unique<ui::CompositionText>()),
+      profile_(profile),
+      weak_ptr_factory_(this) {}
 
 DictationChromeos::~DictationChromeos() = default;
 
@@ -68,8 +73,9 @@
     if (AccessibilityManager::Get()->IsSpokenFeedbackEnabled())
       return;
 
-    if (input_context_)
-      input_context_->UpdateCompositionText(*composition_, 0, true);
+    ui::IMEInputContextHandlerInterface* input_context = GetInputContext();
+    if (input_context)
+      input_context->UpdateCompositionText(*composition_, 0, true);
     return;
   }
   DictationOff();
@@ -90,10 +96,6 @@
 void DictationChromeos::GetSpeechAuthParameters(std::string* auth_scope,
                                                 std::string* auth_token) {}
 
-void DictationChromeos::OnRequestSwitchEngine() {
-  input_context_ = ui::IMEBridge::Get()->GetInputContextHandler();
-}
-
 void DictationChromeos::DictationOff() {
   if (!speech_recognizer_)
     return;
@@ -101,8 +103,9 @@
   if (!composition_->text.empty()) {
     media::SoundsManager::Get()->Play(chromeos::SOUND_DICTATION_END);
 
-    if (input_context_)
-      input_context_->CommitText(base::UTF16ToASCII(composition_->text));
+    ui::IMEInputContextHandlerInterface* input_context = GetInputContext();
+    if (input_context)
+      input_context->CommitText(base::UTF16ToUTF8(composition_->text));
 
     composition_->text = base::string16();
   } else {
diff --git a/chrome/browser/chromeos/accessibility/dictation_chromeos.h b/chrome/browser/chromeos/accessibility/dictation_chromeos.h
index f2bb284..4aef3f6 100644
--- a/chrome/browser/chromeos/accessibility/dictation_chromeos.h
+++ b/chrome/browser/chromeos/accessibility/dictation_chromeos.h
@@ -11,11 +11,9 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/speech/speech_recognizer_delegate.h"
 #include "content/public/browser/speech_recognition_session_preamble.h"
-#include "ui/base/ime/ime_bridge_observer.h"
 
 namespace ui {
 struct CompositionText;
-class IMEInputContextHandlerInterface;
 }  // namespace ui
 
 class Profile;
@@ -24,8 +22,7 @@
 namespace chromeos {
 
 // Provides global dictation (type what you speak) on Chrome OS.
-class DictationChromeos : public SpeechRecognizerDelegate,
-                          ui::IMEBridgeObserver {
+class DictationChromeos : public SpeechRecognizerDelegate {
  public:
   explicit DictationChromeos(Profile* profile);
   ~DictationChromeos() override;
@@ -34,6 +31,8 @@
   bool OnToggleDictation();
 
  private:
+  friend class DictationTest;
+
   // SpeechRecognizerDelegate:
   void OnSpeechResult(const base::string16& query, bool is_final) override;
   void OnSpeechSoundLevelChanged(int16_t level) override;
@@ -42,21 +41,16 @@
   void GetSpeechAuthParameters(std::string* auth_scope,
                                std::string* auth_token) override;
 
-  // IMEBridgeObserver
-  void OnRequestSwitchEngine() override;
-
   // Saves current dictation result and stops listening.
   void DictationOff();
 
   std::unique_ptr<SpeechRecognizer> speech_recognizer_;
   std::unique_ptr<ui::CompositionText> composition_;
-  ui::IMEInputContextHandlerInterface* input_context_;
 
   Profile* profile_;
 
   base::WeakPtrFactory<DictationChromeos> weak_ptr_factory_;
 
-  friend class DictationTest;
   DISALLOW_COPY_AND_ASSIGN(DictationChromeos);
 };
 
diff --git a/chrome/browser/chromeos/accessibility/dictation_chromeos_browsertest.cc b/chrome/browser/chromeos/accessibility/dictation_chromeos_browsertest.cc
index d9344df..a094635 100644
--- a/chrome/browser/chromeos/accessibility/dictation_chromeos_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/dictation_chromeos_browsertest.cc
@@ -122,4 +122,27 @@
   EXPECT_EQ(kFinalSpeechResult, input_context_handler_->last_commit_text());
 }
 
+IN_PROC_BROWSER_TEST_F(DictationTest, SwitchInputContext) {
+  // Turn on dictation and say something.
+  AccessibilityManager::Get()->ToggleDictation();
+  SendSpeechResult(kFirstSpeechResult, true /* is_final */);
+
+  // Speech goes to the default IMEInputContextHandler.
+  EXPECT_EQ(kFirstSpeechResult, input_context_handler_->last_commit_text());
+
+  // Simulate a remote app instantiating a new IMEInputContextHandler, like the
+  // keyboard shortcut viewer app creating a second InputMethodChromeOS.
+  ui::MockIMEInputContextHandler input_context_handler2;
+  ui::IMEBridge::Get()->SetInputContextHandler(&input_context_handler2);
+
+  // Turn on dictation and say something else.
+  AccessibilityManager::Get()->ToggleDictation();
+  SendSpeechResult(kSecondSpeechResult, true /* is_final */);
+
+  // Speech goes to the new IMEInputContextHandler.
+  EXPECT_EQ(kSecondSpeechResult, input_context_handler2.last_commit_text());
+
+  ui::IMEBridge::Get()->SetInputContextHandler(nullptr);
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc
index 454527859..d96958e 100644
--- a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc
+++ b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc
@@ -8,7 +8,6 @@
 #include <string>
 #include <utility>
 
-#include "ash/system/accessibility/select_to_speak_tray_utils.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/accessibility/event_handler_common.h"
 #include "chrome/common/extensions/extension_constants.h"
@@ -163,13 +162,8 @@
 void SelectToSpeakEventHandler::OnMouseEvent(ui::MouseEvent* event) {
   DCHECK(IsSelectToSpeakEnabled());
   DCHECK(event);
-  if (state_ == INACTIVE) {
-    if (event->type() == ui::ET_MOUSE_PRESSED) {
-      // Check if the mouse event occurred on the tray button.
-      CancelEventIfOverSelectToSpeakTray(event);
-    }
+  if (state_ == INACTIVE)
     return;
-  }
 
   if (event->type() == ui::ET_MOUSE_PRESSED) {
     if (state_ == SEARCH_DOWN || state_ == MOUSE_RELEASED)
@@ -207,12 +201,6 @@
 void SelectToSpeakEventHandler::OnTouchEvent(ui::TouchEvent* event) {
   DCHECK(IsSelectToSpeakEnabled());
   DCHECK(event);
-
-  if (state_ == INACTIVE && event->type() == ui::ET_TOUCH_PRESSED) {
-    // Check if the touch event occurred on the tray button.
-    CancelEventIfOverSelectToSpeakTray(event);
-  }
-
   // Only capture touch events if selection was requested or we are capturing
   // touch events already.
   if (state_ != SELECTION_REQUESTED && state_ != CAPTURING_TOUCH_ONLY)
@@ -302,16 +290,4 @@
   }
 }
 
-// TODO(katie): Refactor this for mash, http://crbug.com/874295.
-void SelectToSpeakEventHandler::CancelEventIfOverSelectToSpeakTray(
-    ui::LocatedEvent* event) {
-  if (ash::select_to_speak_tray_utils::SelectToSpeakTrayContainsPointInScreen(
-          event->root_location())) {
-    // Cancel the event so it does not cause any UI changes after a button tap.
-    CancelEvent(event);
-    // Enter the selecting mode as if we've clicked or tapped the button.
-    chromeos::AccessibilityManager::Get()->RequestSelectToSpeakStateChange();
-  }
-}
-
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.h b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.h
index 0bac6d8..89922e4 100644
--- a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.h
+++ b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.h
@@ -52,11 +52,6 @@
   // Forwards a mouse event to the Select-to-Speak extension.
   void ForwardMouseEventToExtension(ui::MouseEvent* event);
 
-  // For touch and mouse events events, the Select-to-Speak tray needs to
-  // cancel all further event propagation so that dialogs and menus do not
-  // close and therefore can be read by the user.
-  void CancelEventIfOverSelectToSpeakTray(ui::LocatedEvent* event);
-
   enum State {
     // The search key is not down, no selection has been requested.
     // No other keys or mouse events are captured.
diff --git a/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc b/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
index d791f53a..24eba23 100644
--- a/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
+++ b/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
@@ -23,8 +23,8 @@
 #include "mojo/public/cpp/system/invitation.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ui/public/interfaces/arc.mojom.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/arc.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/base/ui_base_features.h"
 
 namespace arc {
diff --git a/chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.cc b/chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.cc
index 6ec3699..311783a5 100644
--- a/chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.cc
+++ b/chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.cc
@@ -4,16 +4,20 @@
 
 #include "chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.h"
 
+#include "base/bind.h"
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
+#include "chrome/browser/chromeos/child_accounts/usage_time_limit_processor.h"
 #include "chrome/browser/chromeos/policy/device_status_collector.h"
 #include "chrome/browser/chromeos/policy/status_uploader.h"
 #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
 #include "chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
 #include "chromeos/system/statistics_provider.h"
+#include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
+#include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
 
@@ -28,34 +32,66 @@
 }  // namespace
 
 ConsumerStatusReportingService::ConsumerStatusReportingService(
-    content::BrowserContext* context) {
+    content::BrowserContext* context)
+    : context_(context) {
   // If child user is registered with DMServer and has User Policy applied, it
   // should upload device status to the server.
-  policy::UserCloudPolicyManagerChromeOS* user_cloud_policy_manager =
+  user_cloud_policy_manager_ =
       policy::UserPolicyManagerFactoryChromeOS::GetCloudPolicyManagerForProfile(
-          Profile::FromBrowserContext(context));
-  if (!user_cloud_policy_manager) {
+          Profile::FromBrowserContext(context_));
+  if (!user_cloud_policy_manager_) {
     LOG(WARNING) << "Child user is not managed by User Policy - status reports "
                     "cannot be uploaded to the server. ";
     return;
   }
 
-  PrefService* pref_service = Profile::FromBrowserContext(context)->GetPrefs();
+  PrefService* pref_service = Profile::FromBrowserContext(context_)->GetPrefs();
   DCHECK(pref_service->GetInitializationStatus() !=
          PrefService::INITIALIZATION_STATUS_WAITING);
 
+  pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
+  pref_change_registrar_->Init(pref_service);
+  pref_change_registrar_->Add(
+      prefs::kUsageTimeLimit,
+      base::BindRepeating(
+          &ConsumerStatusReportingService::OnTimeLimitsPolicyChanged,
+          base::Unretained(this)));
+
+  CreateStatusUploaderIfNeeded(user_cloud_policy_manager_->core()->client());
+}
+
+ConsumerStatusReportingService::~ConsumerStatusReportingService() = default;
+
+void ConsumerStatusReportingService::CreateStatusUploaderIfNeeded(
+    policy::CloudPolicyClient* client) {
+  const base::DictionaryValue* time_limit =
+      pref_change_registrar_->prefs()->GetDictionary(prefs::kUsageTimeLimit);
+  const base::TimeDelta new_day_reset_time =
+      usage_time_limit::GetTimeUsageLimitResetTime(
+          time_limit->CreateDeepCopy());
+
+  // Day reset time did not change, there is no need to re-create StatusUploader
+  // if it already exists.
+  if (status_uploader_ && (day_reset_time_ == new_day_reset_time))
+    return;
+
+  VLOG(1) << "Creating status uploader for consumer status reporting.";
+  day_reset_time_ = new_day_reset_time;
   status_uploader_ = std::make_unique<policy::StatusUploader>(
-      user_cloud_policy_manager->core()->client(),
+      client,
       std::make_unique<policy::DeviceStatusCollector>(
-          pref_service, system::StatisticsProvider::GetInstance(),
+          pref_change_registrar_->prefs(),
+          system::StatisticsProvider::GetInstance(),
           policy::DeviceStatusCollector::VolumeInfoFetcher(),
           policy::DeviceStatusCollector::CPUStatisticsFetcher(),
           policy::DeviceStatusCollector::CPUTempFetcher(),
           policy::DeviceStatusCollector::AndroidStatusFetcher(),
-          false /* is_enterprise_reporting */),
+          day_reset_time_, false /* is_enterprise_reporting */),
       base::ThreadTaskRunnerHandle::Get(), kStatusUploadFrequency);
 }
 
-ConsumerStatusReportingService::~ConsumerStatusReportingService() = default;
+void ConsumerStatusReportingService::OnTimeLimitsPolicyChanged() {
+  CreateStatusUploaderIfNeeded(user_cloud_policy_manager_->core()->client());
+}
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.h b/chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.h
index e4b5c878..f860d8b 100644
--- a/chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.h
+++ b/chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.h
@@ -8,14 +8,19 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/time/time.h"
 #include "components/keyed_service/core/keyed_service.h"
 
+class PrefChangeRegistrar;
+
 namespace content {
 class BrowserContext;
 }
 
 namespace policy {
+class CloudPolicyClient;
 class StatusUploader;
+class UserCloudPolicyManagerChromeOS;
 }
 
 namespace chromeos {
@@ -31,10 +36,27 @@
   ~ConsumerStatusReportingService() override;
 
  private:
+  // Creates new status uploader if parameters changed.
+  void CreateStatusUploaderIfNeeded(policy::CloudPolicyClient* client);
+
+  // Called when the UsageTimeLimits policy changes.
+  void OnTimeLimitsPolicyChanged();
+
   // Helper object that controls device status collection/storage and uploads
   // gathered reports to the server.
   std::unique_ptr<policy::StatusUploader> status_uploader_;
 
+  // Preference changes observer.
+  std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
+
+  // Day start/reset time used for aggregating activity data for consumer status
+  // reporting.
+  base::TimeDelta day_reset_time_;
+
+  content::BrowserContext* const context_;
+
+  policy::UserCloudPolicyManagerChromeOS* user_cloud_policy_manager_;
+
   DISALLOW_COPY_AND_ASSIGN(ConsumerStatusReportingService);
 };
 
diff --git a/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.cc b/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.cc
index 89d25ef3..ab561dc 100644
--- a/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.cc
+++ b/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.cc
@@ -36,6 +36,9 @@
     "sunday",   "monday", "tuesday", "wednesday",
     "thursday", "friday", "saturday"};
 
+// Defaults to midnight.
+constexpr base::TimeDelta kDefaultUsageLimitResetTime;
+
 // Whether a timestamp is inside a window.
 bool ContainsTime(base::Time start, base::Time end, base::Time now) {
   return now >= start && now < end;
@@ -53,6 +56,15 @@
                               static_cast<int>(Weekday::kCount));
 }
 
+// Returns usage limit reset time or default value if |time_usage_limit| is
+// invalid.
+base::TimeDelta GetUsageLimitResetTime(
+    const base::Optional<internal::TimeUsageLimit>& time_usage_limit) {
+  if (time_usage_limit)
+    return time_usage_limit->resets_at;
+  return kDefaultUsageLimitResetTime;
+}
+
 // Helper class to process the UsageTimeLimit policy.
 class UsageTimeLimitProcessor {
  public:
@@ -737,9 +749,7 @@
 }
 
 base::TimeDelta UsageTimeLimitProcessor::UsageLimitResetTime() {
-  if (time_usage_limit_)
-    return time_usage_limit_->resets_at;
-  return base::TimeDelta::FromMinutes(0);
+  return GetUsageLimitResetTime(time_usage_limit_);
 }
 
 base::TimeDelta UsageTimeLimitProcessor::LockOverrideResetTime() {
@@ -1049,5 +1059,10 @@
       .GetExpectedResetTime();
 }
 
+base::TimeDelta GetTimeUsageLimitResetTime(
+    const std::unique_ptr<base::DictionaryValue>& time_limit) {
+  return internal::GetUsageLimitResetTime(TimeUsageLimitFromPolicy(time_limit));
+}
+
 }  // namespace usage_time_limit
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.h b/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.h
index c9c1aa2..ebcc23b 100644
--- a/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.h
+++ b/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.h
@@ -170,6 +170,11 @@
     base::Time current_time,
     const icu::TimeZone* const time_zone);
 
+// Returns time of the day when TimeUsageLimit policy is reset, represented by
+// the distance from midnight.
+base::TimeDelta GetTimeUsageLimitResetTime(
+    const std::unique_ptr<base::DictionaryValue>& time_limit);
+
 }  // namespace usage_time_limit
 }  // namespace chromeos
 
diff --git a/chrome/browser/chromeos/child_accounts/usage_time_limit_processor_unittest.cc b/chrome/browser/chromeos/child_accounts/usage_time_limit_processor_unittest.cc
index b3259bf..df7947af 100644
--- a/chrome/browser/chromeos/child_accounts/usage_time_limit_processor_unittest.cc
+++ b/chrome/browser/chromeos/child_accounts/usage_time_limit_processor_unittest.cc
@@ -767,5 +767,33 @@
   ASSERT_EQ(reset_time_two, TimeFromString("Wed, 3 Jan 2018 8:00 EST"));
 }
 
+TEST_F(UsageTimeLimitProcessorTest, GetTimeUsageLimitResetTime) {
+  // If there is no valid time usage limit in the policy, default value
+  // (midnight) should be returned.
+  auto empty_time_limit =
+      std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
+  auto empty_time_limit_dictionary =
+      base::DictionaryValue::From(std::move(empty_time_limit));
+
+  EXPECT_EQ(base::TimeDelta::FromHours(0),
+            GetTimeUsageLimitResetTime(empty_time_limit_dictionary));
+
+  // If reset time is specified in the time usage limit policy, its value should
+  // be returned.
+  const int kHour = 8;
+  const int kMinutes = 30;
+  auto time_usage_limit = base::Value(base::Value::Type::DICTIONARY);
+  time_usage_limit.SetKey("reset_at", CreateTime(kHour, kMinutes));
+  auto time_limit =
+      std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
+  time_limit->SetKey("time_usage_limit", std::move(time_usage_limit));
+  auto time_limit_dictionary =
+      base::DictionaryValue::From(std::move(time_limit));
+
+  EXPECT_EQ(base::TimeDelta::FromHours(kHour) +
+                base::TimeDelta::FromMinutes(kMinutes),
+            GetTimeUsageLimitResetTime(time_limit_dictionary));
+}
+
 }  // namespace usage_time_limit
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index c04f681..2ef649e 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -46,7 +46,6 @@
 #include "chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h"
 #include "chrome/browser/chromeos/boot_times_recorder.h"
 #include "chrome/browser/chromeos/dbus/chrome_features_service_provider.h"
-#include "chrome/browser/chromeos/dbus/chrome_proxy_resolution_service_provider_delegate.h"
 #include "chrome/browser/chromeos/dbus/component_updater_service_provider.h"
 #include "chrome/browser/chromeos/dbus/drive_file_stream_service_provider.h"
 #include "chrome/browser/chromeos/dbus/kiosk_info_service_provider.h"
@@ -328,9 +327,7 @@
     proxy_resolution_service_ = CrosDBusService::Create(
         kNetworkProxyServiceName, dbus::ObjectPath(kNetworkProxyServicePath),
         CrosDBusService::CreateServiceProviderList(
-            std::make_unique<ProxyResolutionServiceProvider>(
-                std::make_unique<
-                    ChromeProxyResolutionServiceProviderDelegate>())));
+            std::make_unique<ProxyResolutionServiceProvider>()));
 
     kiosk_info_service_ = CrosDBusService::Create(
         kKioskAppServiceName, dbus::ObjectPath(kKioskAppServicePath),
diff --git a/chrome/browser/chromeos/dbus/chrome_proxy_resolution_service_provider_delegate.cc b/chrome/browser/chromeos/dbus/chrome_proxy_resolution_service_provider_delegate.cc
deleted file mode 100644
index c5a6d3e..0000000
--- a/chrome/browser/chromeos/dbus/chrome_proxy_resolution_service_provider_delegate.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/dbus/chrome_proxy_resolution_service_provider_delegate.h"
-
-#include "chrome/browser/profiles/profile_manager.h"
-#include "net/url_request/url_request_context_getter.h"
-
-namespace chromeos {
-
-ChromeProxyResolutionServiceProviderDelegate::
-    ChromeProxyResolutionServiceProviderDelegate() {}
-
-ChromeProxyResolutionServiceProviderDelegate::
-    ~ChromeProxyResolutionServiceProviderDelegate() {}
-
-scoped_refptr<net::URLRequestContextGetter>
-ChromeProxyResolutionServiceProviderDelegate::GetRequestContext() {
-  Profile* profile = ProfileManager::GetPrimaryUserProfile();
-  return profile->GetRequestContext();
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/dbus/chrome_proxy_resolution_service_provider_delegate.h b/chrome/browser/chromeos/dbus/chrome_proxy_resolution_service_provider_delegate.h
deleted file mode 100644
index 79f330b..0000000
--- a/chrome/browser/chromeos/dbus/chrome_proxy_resolution_service_provider_delegate.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_DBUS_CHROME_PROXY_RESOLUTION_SERVICE_PROVIDER_DELEGATE_H_
-#define CHROME_BROWSER_CHROMEOS_DBUS_CHROME_PROXY_RESOLUTION_SERVICE_PROVIDER_DELEGATE_H_
-
-#include "base/macros.h"
-#include "chrome/browser/chromeos/dbus/proxy_resolution_service_provider.h"
-
-namespace chromeos {
-
-// Chrome's implementation of ProxyResolutionServiceProvider::Delegate.
-class ChromeProxyResolutionServiceProviderDelegate
-    : public ProxyResolutionServiceProvider::Delegate {
- public:
-  ChromeProxyResolutionServiceProviderDelegate();
-  ~ChromeProxyResolutionServiceProviderDelegate() override;
-
-  // ProxyResolutionServiceProvider::Delegate:
-  scoped_refptr<net::URLRequestContextGetter> GetRequestContext() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ChromeProxyResolutionServiceProviderDelegate);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_DBUS_CHROME_PROXY_RESOLUTION_SERVICE_PROVIDER_DELEGATE_H_
diff --git a/chrome/browser/chromeos/dbus/proxy_resolution_service_provider.cc b/chrome/browser/chromeos/dbus/proxy_resolution_service_provider.cc
index a8c6dbe..659202ab 100644
--- a/chrome/browser/chromeos/dbus/proxy_resolution_service_provider.cc
+++ b/chrome/browser/chromeos/dbus/proxy_resolution_service_provider.cc
@@ -11,6 +11,8 @@
 #include "base/bind_helpers.h"
 #include "base/macros.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "net/base/net_errors.h"
@@ -63,10 +65,8 @@
   DISALLOW_COPY_AND_ASSIGN(Request);
 };
 
-ProxyResolutionServiceProvider::ProxyResolutionServiceProvider(
-    std::unique_ptr<Delegate> delegate)
-    : delegate_(std::move(delegate)),
-      origin_thread_(base::ThreadTaskRunnerHandle::Get()),
+ProxyResolutionServiceProvider::ProxyResolutionServiceProvider()
+    : origin_thread_(base::ThreadTaskRunnerHandle::Get()),
       weak_ptr_factory_(this) {}
 
 ProxyResolutionServiceProvider::~ProxyResolutionServiceProvider() {
@@ -118,7 +118,9 @@
   std::unique_ptr<dbus::Response> response =
       dbus::Response::FromMethodCall(method_call);
   scoped_refptr<net::URLRequestContextGetter> context_getter =
-      delegate_->GetRequestContext();
+      request_context_getter_for_test_
+          ? request_context_getter_for_test_
+          : ProfileManager::GetPrimaryUserProfile()->GetRequestContext();
 
   std::unique_ptr<Request> request = std::make_unique<Request>(
       source_url, std::move(response), response_sender, context_getter);
diff --git a/chrome/browser/chromeos/dbus/proxy_resolution_service_provider.h b/chrome/browser/chromeos/dbus/proxy_resolution_service_provider.h
index 92ea2fb..8516cef 100644
--- a/chrome/browser/chromeos/dbus/proxy_resolution_service_provider.h
+++ b/chrome/browser/chromeos/dbus/proxy_resolution_service_provider.h
@@ -56,21 +56,14 @@
 class ProxyResolutionServiceProvider
     : public CrosDBusService::ServiceProviderInterface {
  public:
-  // Delegate interface providing additional resources to
-  // ProxyResolutionServiceProvider.
-  // TODO(derat): Move the delegate into this class.
-  class CHROMEOS_EXPORT Delegate {
-   public:
-    virtual ~Delegate() {}
-
-    // Returns the request context used to perform proxy resolution.
-    // Always called on UI thread.
-    virtual scoped_refptr<net::URLRequestContextGetter> GetRequestContext() = 0;
-  };
-
-  explicit ProxyResolutionServiceProvider(std::unique_ptr<Delegate> delegate);
+  ProxyResolutionServiceProvider();
   ~ProxyResolutionServiceProvider() override;
 
+  void set_request_context_getter_for_test(
+      const scoped_refptr<net::URLRequestContextGetter>& getter) {
+    request_context_getter_for_test_ = getter;
+  }
+
   // CrosDBusService::ServiceProviderInterface:
   void Start(scoped_refptr<dbus::ExportedObject> exported_object) override;
 
@@ -118,9 +111,9 @@
   // information to the client over D-Bus.
   void NotifyProxyResolved(std::unique_ptr<Request> request);
 
-  std::unique_ptr<Delegate> delegate_;
   scoped_refptr<dbus::ExportedObject> exported_object_;
   scoped_refptr<base::SingleThreadTaskRunner> origin_thread_;
+  scoped_refptr<net::URLRequestContextGetter> request_context_getter_for_test_;
   base::WeakPtrFactory<ProxyResolutionServiceProvider> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ProxyResolutionServiceProvider);
diff --git a/chrome/browser/chromeos/dbus/proxy_resolution_service_provider_unittest.cc b/chrome/browser/chromeos/dbus/proxy_resolution_service_provider_unittest.cc
index aaa5884..46c6722 100644
--- a/chrome/browser/chromeos/dbus/proxy_resolution_service_provider_unittest.cc
+++ b/chrome/browser/chromeos/dbus/proxy_resolution_service_provider_unittest.cc
@@ -115,88 +115,25 @@
   DISALLOW_COPY_AND_ASSIGN(TestProxyResolverFactory);
 };
 
-// Test ProxyResolutionServiceProvider::Delegate implementation.
-class TestDelegate : public ProxyResolutionServiceProvider::Delegate {
- public:
-  explicit TestDelegate(
-      const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner,
-      net::ProxyResolver* proxy_resolver)
-      : proxy_resolver_(proxy_resolver),
-        context_getter_(
-            new net::TestURLRequestContextGetter(network_task_runner)) {
-    network_task_runner->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            &TestDelegate::CreateProxyResolutionServiceOnNetworkThread,
-            base::Unretained(this)));
-    RunPendingTasks(network_task_runner);
-  }
-
-  ~TestDelegate() override {
-    context_getter_->GetNetworkTaskRunner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(&TestDelegate::DeleteProxyServiceOnNetworkThread,
-                       base::Unretained(this)));
-    RunPendingTasks(context_getter_->GetNetworkTaskRunner());
-  }
-
-  // ProxyResolutionServiceProvider::Delegate:
-  scoped_refptr<net::URLRequestContextGetter> GetRequestContext() override {
-    return context_getter_;
-  }
-
- private:
-  // Helper method for the constructor that initializes
-  // |proxy_resolution_service_| and injects it into |context_getter_|'s
-  // context.
-  void CreateProxyResolutionServiceOnNetworkThread() {
-    CHECK(context_getter_->GetNetworkTaskRunner()->BelongsToCurrentThread());
-
-    // Setting a mandatory PAC URL makes |proxy_resolution_service_| query
-    // |proxy_resolver_| and also lets us generate
-    // net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED errors.
-    net::ProxyConfig config;
-    config.set_pac_url(GURL("http://www.example.com"));
-    config.set_pac_mandatory(true);
-    proxy_resolution_service_ = std::make_unique<net::ProxyResolutionService>(
-        std::make_unique<net::ProxyConfigServiceFixed>(
-            net::ProxyConfigWithAnnotation(config,
-                                           TRAFFIC_ANNOTATION_FOR_TESTS)),
-        std::make_unique<TestProxyResolverFactory>(proxy_resolver_),
-        nullptr /* net_log */);
-    context_getter_->GetURLRequestContext()->set_proxy_resolution_service(
-        proxy_resolution_service_.get());
-  }
-
-  // Helper method for the destructor that resets |proxy_resolution_service_|.
-  void DeleteProxyServiceOnNetworkThread() {
-    CHECK(context_getter_->GetNetworkTaskRunner()->BelongsToCurrentThread());
-    proxy_resolution_service_.reset();
-  }
-
-  net::ProxyResolver* proxy_resolver_;  // Not owned.
-
-  // Created, used, and destroyed on the network thread (since
-  // net::ProxyResolutionService is thread-affine (uses ThreadChecker)).
-  std::unique_ptr<net::ProxyResolutionService> proxy_resolution_service_;
-
-  scoped_refptr<net::TestURLRequestContextGetter> context_getter_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestDelegate);
-};
-
 }  // namespace
 
 class ProxyResolutionServiceProviderTest : public testing::Test {
  public:
   ProxyResolutionServiceProviderTest() : network_thread_("NetworkThread") {
     CHECK(network_thread_.Start());
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+        network_thread_.task_runner();
 
-    proxy_resolver_ =
-        std::make_unique<TestProxyResolver>(network_thread_.task_runner());
-    service_provider_ = std::make_unique<ProxyResolutionServiceProvider>(
-        std::make_unique<TestDelegate>(network_thread_.task_runner(),
-                                       proxy_resolver_.get()));
+    task_runner->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ProxyResolutionServiceProviderTest ::
+                           CreateProxyResolutionServiceOnNetworkThread,
+                       base::Unretained(this)));
+    RunPendingTasks(task_runner);
+
+    service_provider_ = std::make_unique<ProxyResolutionServiceProvider>();
+    service_provider_->set_request_context_getter_for_test(context_getter_);
+
     test_helper_.SetUp(
         kNetworkProxyServiceName, dbus::ObjectPath(kNetworkProxyServicePath),
         kNetworkProxyServiceInterface, kNetworkProxyServiceResolveProxyMethod,
@@ -209,12 +146,50 @@
     // URLRequestContextGetter posts a task to delete itself to its task runner,
     // so give it a chance to do that.
     service_provider_.reset();
+    network_thread_.task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&ProxyResolutionServiceProviderTest::
+                                      DeleteProxyServiceOnNetworkThread,
+                                  base::Unretained(this)));
     RunPendingTasks(network_thread_.task_runner());
 
     network_thread_.Stop();
   }
 
  protected:
+  // Helper method for the constructor that initializes
+  // |proxy_resolution_service_| and injects it into |context_getter_|'s
+  // context. Also initializes |context_getter_| and |proxy_resolver_|.
+  void CreateProxyResolutionServiceOnNetworkThread() {
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+        network_thread_.task_runner();
+    CHECK(task_runner->BelongsToCurrentThread());
+
+    context_getter_ = new net::TestURLRequestContextGetter(task_runner);
+    proxy_resolver_ = std::make_unique<TestProxyResolver>(task_runner);
+
+    // Setting a mandatory PAC URL makes |proxy_resolution_service_| query
+    // |proxy_resolver_| and also lets us generate
+    // net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED errors.
+    net::ProxyConfig config;
+    config.set_pac_url(GURL("http://www.example.com"));
+    config.set_pac_mandatory(true);
+    proxy_resolution_service_ = std::make_unique<net::ProxyResolutionService>(
+        std::make_unique<net::ProxyConfigServiceFixed>(
+            net::ProxyConfigWithAnnotation(config,
+                                           TRAFFIC_ANNOTATION_FOR_TESTS)),
+        std::make_unique<TestProxyResolverFactory>(proxy_resolver_.get()),
+        nullptr /* net_log */);
+    context_getter_->GetURLRequestContext()->set_proxy_resolution_service(
+        proxy_resolution_service_.get());
+  }
+
+  // Helper method for the destructor that resets |proxy_resolution_service_|.
+  void DeleteProxyServiceOnNetworkThread() {
+    CHECK(network_thread_.task_runner()->BelongsToCurrentThread());
+    proxy_resolution_service_.reset();
+    context_getter_ = nullptr;
+  }
+
   // Makes a D-Bus call to |service_provider_|'s ResolveProxy method and returns
   // the response.
   std::unique_ptr<dbus::Response> CallMethod(const std::string& source_url) {
@@ -229,6 +204,12 @@
   base::Thread network_thread_;
 
   std::unique_ptr<TestProxyResolver> proxy_resolver_;
+
+  // Created, used, and destroyed on the network thread (since
+  // net::ProxyResolutionService is thread-affine (uses ThreadChecker)).
+  std::unique_ptr<net::ProxyResolutionService> proxy_resolution_service_;
+
+  scoped_refptr<net::TestURLRequestContextGetter> context_getter_;
   std::unique_ptr<ProxyResolutionServiceProvider> service_provider_;
   ServiceProviderTestHelper test_helper_;
 
diff --git a/chrome/browser/chromeos/drive/drive_integration_service.cc b/chrome/browser/chromeos/drive/drive_integration_service.cc
index 42d445c..1e07e24 100644
--- a/chrome/browser/chromeos/drive/drive_integration_service.cc
+++ b/chrome/browser/chromeos/drive/drive_integration_service.cc
@@ -559,6 +559,13 @@
                          : drive::util::GetDriveMountPointPath(profile_);
 }
 
+base::FilePath DriveIntegrationService::GetDriveFsLogPath() const {
+  return drivefs_holder_
+             ? drivefs_holder_->drivefs_host()->GetDataPath().Append(
+                   "Logs/drivefs.txt")
+             : base::FilePath();
+}
+
 bool DriveIntegrationService::GetRelativeDrivePath(
     const base::FilePath& local_path,
     base::FilePath* drive_path) const {
diff --git a/chrome/browser/chromeos/drive/drive_integration_service.h b/chrome/browser/chromeos/drive/drive_integration_service.h
index 02f6595..2ba4790 100644
--- a/chrome/browser/chromeos/drive/drive_integration_service.h
+++ b/chrome/browser/chromeos/drive/drive_integration_service.h
@@ -115,6 +115,9 @@
   // |IsMounted()|.
   base::FilePath GetMountPointPath() const;
 
+  // Returns the path of DriveFS log if enabled or empty path.
+  base::FilePath GetDriveFsLogPath() const;
+
   // Returns true if |local_path| resides inside |GetMountPointPath()|.
   // In this case |drive_path| will contain 'drive' path of this file, e.g.
   // reparented to the mount point.
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 5866c27..698ef746 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -564,6 +564,11 @@
     FilesAppBrowserTest,
     ::testing::Values(TestCase("installLinuxPackageDialog")));
 
+WRAPPED_INSTANTIATE_TEST_CASE_P(
+    LauncherSearch, /* launcher_search.js */
+    FilesAppBrowserTest,
+    ::testing::Values(TestCase("launcherOpenSearchResult")));
+
 // Structure to describe an account info.
 struct TestAccountInfo {
   const char* const gaia_id;
diff --git a/chrome/browser/chromeos/file_manager/gallery_jstest.cc b/chrome/browser/chromeos/file_manager/gallery_jstest.cc
index 2a3c58b..562cc758 100644
--- a/chrome/browser/chromeos/file_manager/gallery_jstest.cc
+++ b/chrome/browser/chromeos/file_manager/gallery_jstest.cc
@@ -38,7 +38,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(GalleryJsTest, GalleryItemTest) {
-  RunTest(base::FilePath(FILE_PATH_LITERAL("gallery_item_unittest.html")));
+  RunGeneratedTest("/gallery_item_unittest.html");
 }
 
 IN_PROC_BROWSER_TEST_F(GalleryJsTest, GalleryDataModelTest) {
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
index 8f070c94..2fde23a 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
@@ -85,7 +85,7 @@
 #include "content/public/browser/web_ui.h"
 #include "media/audio/sounds/sounds_manager.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/window.h"
 #include "ui/base/ime/chromeos/extension_ime_util.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
diff --git a/chrome/browser/chromeos/login/ui/login_web_dialog_browsertest.cc b/chrome/browser/chromeos/login/ui/login_web_dialog_browsertest.cc
index 519ad40..709e839 100644
--- a/chrome/browser/chromeos/login/ui/login_web_dialog_browsertest.cc
+++ b/chrome/browser/chromeos/login/ui/login_web_dialog_browsertest.cc
@@ -7,7 +7,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/events/test/event_generator.h"
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
index bc8ccde..9b6542e 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
@@ -71,6 +71,9 @@
 constexpr base::TimeDelta kDeviceStatusUploadFrequency =
     base::TimeDelta::FromHours(3);
 
+// Start of the day for activity data aggregation. Defaults to midnight.
+constexpr base::TimeDelta kActivityDayStart;
+
 // Fetches a machine statistic value from StatisticsProvider, returns an empty
 // string on failure.
 std::string GetMachineStatistic(const std::string& key) {
@@ -374,7 +377,7 @@
           DeviceStatusCollector::VolumeInfoFetcher(),
           DeviceStatusCollector::CPUStatisticsFetcher(),
           DeviceStatusCollector::CPUTempFetcher(),
-          DeviceStatusCollector::AndroidStatusFetcher(),
+          DeviceStatusCollector::AndroidStatusFetcher(), kActivityDayStart,
           true /* is_enterprise_device */),
       task_runner_, kDeviceStatusUploadFrequency));
 }
diff --git a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
index bc54873..71a0c761 100644
--- a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
@@ -57,7 +57,7 @@
   policies->Set(policy_name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
                 POLICY_SOURCE_CLOUD, std::move(value_to_set), nullptr);
   if (!error.empty())
-    policies->SetError(policy_name, error);
+    policies->AddError(policy_name, error);
 }
 
 // Decodes a protobuf integer to an IntegerValue. Returns NULL in case the input
diff --git a/chrome/browser/chromeos/policy/device_status_collector.cc b/chrome/browser/chromeos/policy/device_status_collector.cc
index a866f83..cf548ece 100644
--- a/chrome/browser/chromeos/policy/device_status_collector.cc
+++ b/chrome/browser/chromeos/policy/device_status_collector.cc
@@ -447,8 +447,12 @@
   };
 
   // Creates activity storage. Activity data will be stored in the given
-  // |pref_service| under |pref_name| preference.
-  ActivityStorage(PrefService* pref_service, const std::string& pref_name);
+  // |pref_service| under |pref_name| preference. Activity data are aggregated
+  // by day. The start of the new day is counted from |activity_day_start| that
+  // represents the distance from midnight.
+  ActivityStorage(PrefService* pref_service,
+                  const std::string& pref_name,
+                  TimeDelta activity_day_start);
   ~ActivityStorage();
 
   // Adds an activity period. Accepts empty |active_user_email| if it should not
@@ -482,9 +486,6 @@
   std::vector<ActivityPeriod> GetFilteredActivityPeriods(bool omit_emails);
 
  private:
-  // Determine the day key (milliseconds since epoch for corresponding day
-  // in UTC) for a given |timestamp|.
-  static int64_t TimestampToDayKey(Time timestamp);
   static std::string MakeActivityPeriodPrefKey(int64_t start,
                                                const std::string& user_email);
   static bool ParseActivityPeriodPrefKey(const std::string& key,
@@ -494,16 +495,27 @@
                               const std::vector<std::string>& reporting_users,
                               base::DictionaryValue* const filtered_times);
 
+  // Determine the day key (milliseconds since epoch for corresponding
+  // |day_start_| in UTC) for a given |timestamp|.
+  int64_t TimestampToDayKey(Time timestamp);
+
   PrefService* const pref_service_ = nullptr;
   const std::string pref_name_;
 
+  // New day start time used for aggregating data represented by the distance
+  // from midnight.
+  const TimeDelta day_start_;
+
   DISALLOW_COPY_AND_ASSIGN(ActivityStorage);
 };
 
 DeviceStatusCollector::ActivityStorage::ActivityStorage(
     PrefService* pref_service,
-    const std::string& pref_name)
-    : pref_service_(pref_service), pref_name_(pref_name) {
+    const std::string& pref_name,
+    TimeDelta activity_day_start)
+    : pref_service_(pref_service),
+      pref_name_(pref_name),
+      day_start_(activity_day_start) {
   DCHECK(pref_service_);
   const PrefService::PrefInitializationStatus pref_service_status =
       pref_service_->GetInitializationStatus();
@@ -524,16 +536,18 @@
   base::DictionaryValue* activity_times = update.Get();
 
   // Assign the period to day buckets in local time.
-  Time midnight = start.LocalMidnight();
-  while (midnight < end) {
-    midnight += TimeDelta::FromDays(1);
-    int64_t activity = (std::min(end, midnight) - start).InMilliseconds();
+  Time day_start = start.LocalMidnight() + day_start_;
+  if (start < day_start)
+    day_start -= TimeDelta::FromDays(1);
+  while (day_start < end) {
+    day_start += TimeDelta::FromDays(1);
+    int64_t activity = (std::min(end, day_start) - start).InMilliseconds();
     const std::string key =
         MakeActivityPeriodPrefKey(TimestampToDayKey(start), active_user_email);
     int previous_activity = 0;
     activity_times->GetInteger(key, &previous_activity);
     activity_times->SetInteger(key, previous_activity + activity);
-    start = midnight;
+    start = day_start;
   }
 }
 
@@ -617,17 +631,6 @@
 }
 
 // static
-int64_t DeviceStatusCollector::ActivityStorage::TimestampToDayKey(
-    Time timestamp) {
-  Time::Exploded exploded;
-  timestamp.LocalMidnight().LocalExplode(&exploded);
-  Time out_time;
-  bool conversion_success = Time::FromUTCExploded(exploded, &out_time);
-  DCHECK(conversion_success);
-  return out_time.ToJavaTime();
-}
-
-// static
 std::string DeviceStatusCollector::ActivityStorage::MakeActivityPeriodPrefKey(
     int64_t start,
     const std::string& user_email) {
@@ -682,6 +685,19 @@
   }
 }
 
+int64_t DeviceStatusCollector::ActivityStorage::TimestampToDayKey(
+    Time timestamp) {
+  Time::Exploded exploded;
+  Time day_start = timestamp.LocalMidnight() + day_start_;
+  if (timestamp < day_start)
+    day_start -= TimeDelta::FromDays(1);
+  day_start.LocalExplode(&exploded);
+  Time out_time;
+  bool conversion_success = Time::FromUTCExploded(exploded, &out_time);
+  DCHECK(conversion_success);
+  return out_time.ToJavaTime();
+}
+
 DeviceStatusCollector::DeviceStatusCollector(
     PrefService* pref_service,
     chromeos::system::StatisticsProvider* provider,
@@ -689,6 +705,7 @@
     const CPUStatisticsFetcher& cpu_statistics_fetcher,
     const CPUTempFetcher& cpu_temp_fetcher,
     const AndroidStatusFetcher& android_status_fetcher,
+    TimeDelta activity_day_start,
     bool is_enterprise_reporting)
     : max_stored_past_activity_interval_(kMaxStoredPastActivityInterval),
       max_stored_future_activity_interval_(kMaxStoredFutureActivityInterval),
@@ -783,8 +800,10 @@
   DCHECK(pref_service_->GetInitializationStatus() !=
          PrefService::INITIALIZATION_STATUS_WAITING);
   activity_storage_ = std::make_unique<ActivityStorage>(
-      pref_service_, (is_enterprise_reporting_ ? prefs::kDeviceActivityTimes
-                                               : prefs::kUserActivityTimes));
+      pref_service_,
+      (is_enterprise_reporting_ ? prefs::kDeviceActivityTimes
+                                : prefs::kUserActivityTimes),
+      activity_day_start);
 }
 
 DeviceStatusCollector::~DeviceStatusCollector() {}
diff --git a/chrome/browser/chromeos/policy/device_status_collector.h b/chrome/browser/chromeos/policy/device_status_collector.h
index 6011885..70437ed 100644
--- a/chrome/browser/chromeos/policy/device_status_collector.h
+++ b/chrome/browser/chromeos/policy/device_status_collector.h
@@ -92,14 +92,17 @@
   // testing. A null callback can be passed for any *Fetcher parameter, to use
   // the default implementation. These callbacks are always executed on Blocking
   // Pool. Caller is responsible for passing already initialized |pref_service|.
-  // If |is_enterprise_device| additional enterprise relevant status data will
-  // be reported.
+  // |activity_day_start| indicates what time does the new day start for
+  // activity reporting daily data aggregation. It is represented by the
+  // distance from midnight. If |is_enterprise_device| additional enterprise
+  // relevant status data will be reported.
   DeviceStatusCollector(PrefService* pref_service,
                         chromeos::system::StatisticsProvider* provider,
                         const VolumeInfoFetcher& volume_info_fetcher,
                         const CPUStatisticsFetcher& cpu_statistics_fetcher,
                         const CPUTempFetcher& cpu_temp_fetcher,
                         const AndroidStatusFetcher& android_status_fetcher,
+                        base::TimeDelta activity_day_start,
                         bool is_enterprise_reporting);
   virtual ~DeviceStatusCollector();
 
diff --git a/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc
index 9df7b25..cc57c91 100644
--- a/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc
@@ -87,6 +87,12 @@
 
 namespace {
 
+// Time delta representing midnight 00:00.
+constexpr TimeDelta kMidnight;
+
+// Time delta representing 1 hour time interval.
+constexpr TimeDelta kHour = TimeDelta::FromHours(1);
+
 const int64_t kMillisecondsPerDay = Time::kMicrosecondsPerDay / 1000;
 const char kKioskAccountId[] = "kiosk_user@localhost";
 const char kArcKioskAccountId[] = "arc_kiosk_user@localhost";
@@ -120,6 +126,7 @@
       const policy::DeviceStatusCollector::CPUTempFetcher& cpu_temp_fetcher,
       const policy::DeviceStatusCollector::AndroidStatusFetcher&
           android_status_fetcher,
+      TimeDelta activity_day_start,
       bool is_enterprise_device)
       : policy::DeviceStatusCollector(pref_service,
                                       provider,
@@ -127,10 +134,11 @@
                                       cpu_fetcher,
                                       cpu_temp_fetcher,
                                       android_status_fetcher,
+                                      activity_day_start,
                                       is_enterprise_device) {
-    // Set the baseline time to a fixed value (1 AM) to prevent test flakiness
-    // due to a single activity period spanning two days.
-    SetBaselineTime(Time::Now().LocalMidnight() + TimeDelta::FromHours(1));
+    // Set the baseline time to a fixed value (1 hour after day start) to
+    // prevent test flakiness due to a single activity period spanning two days.
+    SetBaselineTime(Time::Now().LocalMidnight() + activity_day_start + kHour);
   }
 
   void Simulate(ui::IdleState* states, int len) {
@@ -420,7 +428,7 @@
     std::vector<em::VolumeInfo> expected_volume_info;
     status_collector_.reset(new TestingDeviceStatusCollector(
         &local_state_, &fake_statistics_provider_, volume_info, cpu_stats,
-        cpu_temp_fetcher, android_status_fetcher,
+        cpu_temp_fetcher, android_status_fetcher, kMidnight,
         true /* is_enterprise_device */));
   }
 
@@ -1664,6 +1672,226 @@
   shill::kStateOffline, em::NetworkState::OFFLINE, "", ""
 };
 
+// Tests activity reporting day start correctness.
+class DeviceStatusCollectorDayStartTest : public DeviceStatusCollectorTest {
+ protected:
+  DeviceStatusCollectorDayStartTest() = default;
+  ~DeviceStatusCollectorDayStartTest() override = default;
+
+  void SetUp() override {
+    DeviceStatusCollectorTest::SetUp();
+    settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
+  }
+
+  // Restarts device status collector for activity reporting tests with given
+  // |activity_day_start|.
+  void RestartStatusCollectorWithDayStart(TimeDelta activity_day_start) {
+    status_collector_ = std::make_unique<TestingDeviceStatusCollector>(
+        &local_state_, &fake_statistics_provider_,
+        base::BindRepeating(&GetEmptyVolumeInfo),
+        base::BindRepeating(&GetEmptyCPUStatistics),
+        base::BindRepeating(&GetEmptyCPUTempInfo),
+        base::BindRepeating(&GetEmptyAndroidStatus), activity_day_start,
+        true /* is_enterprise_reporting */);
+  }
+
+  // Sets current test time to |time_since_midnight|.
+  void SetCurrentTime(TimeDelta time_since_midnight) {
+    status_collector_->SetBaselineTime(Time::Now().LocalMidnight() +
+                                       time_since_midnight);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DeviceStatusCollectorDayStartTest);
+};
+
+TEST_F(DeviceStatusCollectorDayStartTest, ArbitraryActivityDayStart) {
+  ui::IdleState test_states[] = {ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_IDLE,
+                                 ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE};
+
+  const TimeDelta kNoon = TimeDelta::FromHours(12);
+  RestartStatusCollectorWithDayStart(kNoon);
+
+  // Test a single active sample.
+  status_collector_->Simulate(test_states, 1);
+  GetStatus();
+  EXPECT_EQ(1, device_status_.active_period_size());
+  EXPECT_EQ(1 * ActivePeriodMilliseconds(),
+            GetActiveMilliseconds(device_status_));
+  device_status_.clear_active_period();  // Clear the result protobuf.
+
+  // Test multiple consecutive active samples.
+  status_collector_->Simulate(test_states, 4);
+  GetStatus();
+  EXPECT_EQ(1, device_status_.active_period_size());
+  EXPECT_EQ(4 * ActivePeriodMilliseconds(),
+            GetActiveMilliseconds(device_status_));
+}
+
+TEST_F(DeviceStatusCollectorDayStartTest, ActivityCrossingDayStart) {
+  ui::IdleState test_states[] = {ui::IDLE_STATE_ACTIVE};
+
+  const TimeDelta kDayStart = TimeDelta::FromHours(6);
+  RestartStatusCollectorWithDayStart(kDayStart);
+  // Set time to 10 seconds after day start.
+  SetCurrentTime(kDayStart + TimeDelta::FromSeconds(10));
+  status_collector_->Simulate(test_states, 1);
+
+  GetStatus();
+
+  ASSERT_EQ(2, device_status_.active_period_size());
+
+  em::ActiveTimePeriod period0 = device_status_.active_period(0);
+  em::ActiveTimePeriod period1 = device_status_.active_period(1);
+  EXPECT_EQ(ActivePeriodMilliseconds() - 10000, period0.active_duration());
+  EXPECT_EQ(10000, period1.active_duration());
+
+  em::TimePeriod time_period0 = period0.time_period();
+  em::TimePeriod time_period1 = period1.time_period();
+  EXPECT_EQ(time_period0.end_timestamp(), time_period1.start_timestamp());
+  // Ensure that the start and end times for the period are a day apart.
+  EXPECT_EQ(time_period0.end_timestamp() - time_period0.start_timestamp(),
+            kMillisecondsPerDay);
+  EXPECT_EQ(time_period1.end_timestamp() - time_period1.start_timestamp(),
+            kMillisecondsPerDay);
+}
+
+TEST_F(DeviceStatusCollectorDayStartTest, ActivityDayStartChangesToLater) {
+  ui::IdleState test_states[] = {
+      ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE,
+  };
+
+  const TimeDelta kDayStart; /* Midnight */
+  RestartStatusCollectorWithDayStart(kDayStart);
+  // Set clock to 1h after day start and report 2 activities.
+  SetCurrentTime(kDayStart + kHour);
+  status_collector_->Simulate(test_states, 2);
+
+  // Move day starts to later hour.
+  const TimeDelta kLaterDayStart = kDayStart + TimeDelta::FromHours(6);
+  RestartStatusCollectorWithDayStart(kLaterDayStart);
+  // Set clock before day start and report 1 activity.
+  SetCurrentTime(kLaterDayStart - kHour);
+  status_collector_->Simulate(test_states, 1);
+
+  GetStatus();
+
+  ASSERT_EQ(2, device_status_.active_period_size());
+  EXPECT_EQ(3 * ActivePeriodMilliseconds(),
+            GetActiveMilliseconds(device_status_));
+  EXPECT_EQ(1 * ActivePeriodMilliseconds(),
+            device_status_.active_period(0).active_duration());
+  EXPECT_EQ(2 * ActivePeriodMilliseconds(),
+            device_status_.active_period(1).active_duration());
+
+  // Set clock after day start and report 1 activity.
+  SetCurrentTime(kLaterDayStart + kHour);
+  status_collector_->Simulate(test_states, 1);
+
+  GetStatus();
+
+  ASSERT_EQ(3, device_status_.active_period_size());
+  EXPECT_EQ(4 * ActivePeriodMilliseconds(),
+            GetActiveMilliseconds(device_status_));
+  EXPECT_EQ(1 * ActivePeriodMilliseconds(),
+            device_status_.active_period(0).active_duration());
+  EXPECT_EQ(2 * ActivePeriodMilliseconds(),
+            device_status_.active_period(1).active_duration());
+  EXPECT_EQ(1 * ActivePeriodMilliseconds(),
+            device_status_.active_period(2).active_duration());
+}
+
+TEST_F(DeviceStatusCollectorDayStartTest, ActivityDayStartChangesToEarlier) {
+  ui::IdleState test_states[] = {
+      ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE,
+  };
+
+  const TimeDelta kDayStart = TimeDelta::FromHours(6);
+  RestartStatusCollectorWithDayStart(kDayStart);
+  // Set clock after day start and report 2 activities.
+  SetCurrentTime(kDayStart + kHour);
+  status_collector_->Simulate(test_states, 2);
+
+  // Move day starts to earlier hour.
+  const TimeDelta kEarlierDayStart = kDayStart - TimeDelta::FromHours(3);
+  RestartStatusCollectorWithDayStart(kEarlierDayStart);
+  // Set clock before day start and report 1 activity.
+  SetCurrentTime(kEarlierDayStart - kHour);
+  status_collector_->Simulate(test_states, 1);
+
+  GetStatus();
+
+  ASSERT_EQ(2, device_status_.active_period_size());
+  EXPECT_EQ(3 * ActivePeriodMilliseconds(),
+            GetActiveMilliseconds(device_status_));
+  EXPECT_EQ(1 * ActivePeriodMilliseconds(),
+            device_status_.active_period(0).active_duration());
+  EXPECT_EQ(2 * ActivePeriodMilliseconds(),
+            device_status_.active_period(1).active_duration());
+
+  // Set clock after day start and report 1 activity.
+  SetCurrentTime(kEarlierDayStart + kHour);
+  status_collector_->Simulate(test_states, 1);
+
+  GetStatus();
+
+  ASSERT_EQ(3, device_status_.active_period_size());
+  EXPECT_EQ(4 * ActivePeriodMilliseconds(),
+            GetActiveMilliseconds(device_status_));
+  EXPECT_EQ(1 * ActivePeriodMilliseconds(),
+            device_status_.active_period(0).active_duration());
+  EXPECT_EQ(1 * ActivePeriodMilliseconds(),
+            device_status_.active_period(1).active_duration());
+  EXPECT_EQ(2 * ActivePeriodMilliseconds(),
+            device_status_.active_period(2).active_duration());
+}
+
+TEST_F(DeviceStatusCollectorDayStartTest,
+       ActivityDayStartGetsBackToTheSameValue) {
+  ui::IdleState test_states[] = {
+      ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE,
+  };
+
+  const TimeDelta kDayStart = TimeDelta::FromHours(0);
+  RestartStatusCollectorWithDayStart(kDayStart);
+  // Set clock after day start report 2 activities.
+  SetCurrentTime(kDayStart + kHour);
+  status_collector_->Simulate(test_states, 2);
+
+  // Move day starts to later hour.
+  const TimeDelta kLaterDayStart = kDayStart + TimeDelta::FromHours(6);
+  RestartStatusCollectorWithDayStart(kLaterDayStart);
+  // Set clock after day start and report 1 activity.
+  SetCurrentTime(kLaterDayStart + kHour);
+  status_collector_->Simulate(test_states, 1);
+
+  GetStatus();
+
+  ASSERT_EQ(2, device_status_.active_period_size());
+  EXPECT_EQ(3 * ActivePeriodMilliseconds(),
+            GetActiveMilliseconds(device_status_));
+  EXPECT_EQ(2 * ActivePeriodMilliseconds(),
+            device_status_.active_period(0).active_duration());
+  EXPECT_EQ(1 * ActivePeriodMilliseconds(),
+            device_status_.active_period(1).active_duration());
+
+  // Move day start back.
+  RestartStatusCollectorWithDayStart(kDayStart);
+  // Progress clock from the previous report.
+  SetCurrentTime(kLaterDayStart + 2 * kHour);
+  status_collector_->Simulate(test_states, 1);
+
+  GetStatus();
+
+  ASSERT_EQ(2, device_status_.active_period_size());
+  EXPECT_EQ(4 * ActivePeriodMilliseconds(),
+            GetActiveMilliseconds(device_status_));
+  EXPECT_EQ(3 * ActivePeriodMilliseconds(),
+            device_status_.active_period(0).active_duration());
+  EXPECT_EQ(1 * ActivePeriodMilliseconds(),
+            device_status_.active_period(1).active_duration());
+}
+
 class DeviceStatusCollectorNetworkInterfacesTest
     : public DeviceStatusCollectorTest {
  protected:
@@ -1914,7 +2142,7 @@
           android_status_fetcher) override {
     status_collector_ = std::make_unique<TestingDeviceStatusCollector>(
         &profile_pref_service_, &fake_statistics_provider_, volume_info,
-        cpu_stats, cpu_temp_fetcher, android_status_fetcher,
+        cpu_stats, cpu_temp_fetcher, android_status_fetcher, kMidnight,
         false /* is_enterprise_reporting */);
   }
 
diff --git a/chrome/browser/chromeos/policy/status_uploader_unittest.cc b/chrome/browser/chromeos/policy/status_uploader_unittest.cc
index 334db9d..7a03327 100644
--- a/chrome/browser/chromeos/policy/status_uploader_unittest.cc
+++ b/chrome/browser/chromeos/policy/status_uploader_unittest.cc
@@ -55,6 +55,7 @@
             policy::DeviceStatusCollector::CPUStatisticsFetcher(),
             policy::DeviceStatusCollector::CPUTempFetcher(),
             policy::DeviceStatusCollector::AndroidStatusFetcher(),
+            base::TimeDelta(), /* Day starts at midnight */
             true /* is_enterprise_device */) {}
 
   MOCK_METHOD1(GetDeviceAndSessionStatusAsync,
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
index 6884dddd..b2853331 100644
--- a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
+++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -227,6 +227,12 @@
   return DevToolsBrowserContextManager::GetInstance().GetBrowserContexts();
 }
 
+content::BrowserContext*
+ChromeDevToolsManagerDelegate::GetDefaultBrowserContext() {
+  return DevToolsBrowserContextManager::GetInstance()
+      .GetDefaultBrowserContext();
+}
+
 content::BrowserContext* ChromeDevToolsManagerDelegate::CreateBrowserContext() {
   return DevToolsBrowserContextManager::GetInstance().CreateBrowserContext();
 }
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.h b/chrome/browser/devtools/chrome_devtools_manager_delegate.h
index 087d00c..7be478c 100644
--- a/chrome/browser/devtools/chrome_devtools_manager_delegate.h
+++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.h
@@ -60,6 +60,7 @@
   std::string GetTargetTitle(content::WebContents* web_contents) override;
 
   std::vector<content::BrowserContext*> GetBrowserContexts() override;
+  content::BrowserContext* GetDefaultBrowserContext() override;
   content::BrowserContext* CreateBrowserContext() override;
   void DisposeBrowserContext(content::BrowserContext*,
                              DisposeCallback callback) override;
diff --git a/chrome/browser/devtools/device/port_forwarding_controller.cc b/chrome/browser/devtools/device/port_forwarding_controller.cc
index 601373da4..3990c17 100644
--- a/chrome/browser/devtools/device/port_forwarding_controller.cc
+++ b/chrome/browser/devtools/device/port_forwarding_controller.cc
@@ -20,7 +20,10 @@
 #include "chrome/common/pref_names.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/storage_partition.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "net/base/address_list.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
@@ -142,41 +145,51 @@
             "here."
         })");
 
-class SocketTunnel {
+class SocketTunnel : public network::mojom::ResolveHostClient {
  public:
-  static void StartTunnel(const std::string& host,
+  static void StartTunnel(Profile* profile,
+                          const std::string& host,
                           int port,
                           int result,
                           std::unique_ptr<net::StreamSocket> socket) {
     if (result == net::OK)
-      new SocketTunnel(std::move(socket), host, port);
+      new SocketTunnel(profile, std::move(socket), host, port);
   }
 
  private:
-  SocketTunnel(std::unique_ptr<net::StreamSocket> socket,
+  SocketTunnel(Profile* profile,
+               std::unique_ptr<net::StreamSocket> socket,
                const std::string& host,
                int port)
-      : remote_socket_(std::move(socket)),
+      : binding_(this),
+        remote_socket_(std::move(socket)),
         pending_writes_(0),
         pending_destruction_(false) {
-    host_resolver_ = net::HostResolver::CreateDefaultResolver(nullptr);
-    net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port));
-    int result = host_resolver_->Resolve(
-        request_info, net::DEFAULT_PRIORITY, &address_list_,
-        base::Bind(&SocketTunnel::OnResolved, base::Unretained(this)),
-        &request_, net::NetLogWithSource());
-    if (result != net::ERR_IO_PENDING)
-      OnResolved(result);
+    DCHECK(!binding_);
+    network::mojom::ResolveHostClientPtr client_ptr;
+    binding_.Bind(mojo::MakeRequest(&client_ptr));
+    binding_.set_connection_error_handler(
+        base::BindOnce(&SocketTunnel::OnComplete, base::Unretained(this),
+                       net::ERR_FAILED, base::nullopt));
+    net::HostPortPair host_port_pair(host, port);
+    content::BrowserContext::GetDefaultStoragePartition(profile)
+        ->GetNetworkContext()
+        ->ResolveHost(host_port_pair, nullptr, std::move(client_ptr));
   }
 
-  void OnResolved(int result) {
+  // network::mojom::ResolveHostClient:
+  void OnComplete(
+      int result,
+      const base::Optional<net::AddressList>& resolved_addresses) override {
     if (result < 0) {
       SelfDestruct();
       return;
     }
 
-    host_socket_.reset(new net::TCPClientSocket(address_list_, nullptr, nullptr,
-                                                net::NetLogSource()));
+    DCHECK(resolved_addresses && !resolved_addresses->empty());
+
+    host_socket_.reset(new net::TCPClientSocket(
+        resolved_addresses.value(), nullptr, nullptr, net::NetLogSource()));
     result = host_socket_->Connect(base::Bind(&SocketTunnel::OnConnected,
                                               base::Unretained(this)));
     if (result != net::ERR_IO_PENDING)
@@ -270,11 +283,9 @@
     delete this;
   }
 
+  mojo::Binding<network::mojom::ResolveHostClient> binding_;
   std::unique_ptr<net::StreamSocket> remote_socket_;
   std::unique_ptr<net::StreamSocket> host_socket_;
-  std::unique_ptr<net::HostResolver> host_resolver_;
-  std::unique_ptr<net::HostResolver::Request> request_;
-  net::AddressList address_list_;
   int pending_writes_;
   bool pending_destruction_;
 };
@@ -284,7 +295,8 @@
 class PortForwardingController::Connection
     : public AndroidDeviceManager::AndroidWebSocket::Delegate {
  public:
-  Connection(Registry* registry,
+  Connection(Profile* profile,
+             Registry* registry,
              scoped_refptr<AndroidDeviceManager::Device> device,
              scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
              const ForwardingMap& forwarding_map);
@@ -322,6 +334,7 @@
   void OnFrameRead(const std::string& message) override;
   void OnSocketClosed() override;
 
+  Profile* profile_;
   PortForwardingController::Registry* registry_;
   scoped_refptr<AndroidDeviceManager::Device> device_;
   scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser_;
@@ -336,11 +349,13 @@
 };
 
 PortForwardingController::Connection::Connection(
+    Profile* profile,
     Registry* registry,
     scoped_refptr<AndroidDeviceManager::Device> device,
     scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
     const ForwardingMap& forwarding_map)
-    : registry_(registry),
+    : profile_(profile),
+      registry_(registry),
       device_(device),
       browser_(browser),
       command_id_(0),
@@ -503,15 +518,13 @@
     return;
   std::string destination_host = tokens[0];
 
-  device_->OpenSocket(
-      connection_id.c_str(),
-      base::Bind(&SocketTunnel::StartTunnel,
-                 destination_host,
-                 destination_port));
+  device_->OpenSocket(connection_id.c_str(),
+                      base::Bind(&SocketTunnel::StartTunnel, profile_,
+                                 destination_host, destination_port));
 }
 
 PortForwardingController::PortForwardingController(Profile* profile)
-    : pref_service_(profile->GetPrefs()) {
+    : profile_(profile), pref_service_(profile->GetPrefs()) {
   pref_change_registrar_.Init(pref_service_);
   base::Closure callback = base::Bind(
       &PortForwardingController::OnPrefsChange, base::Unretained(this));
@@ -538,8 +551,8 @@
     Registry::iterator rit = registry_.find(remote_device->serial());
     if (rit == registry_.end()) {
       if (remote_device->browsers().size() > 0) {
-        new Connection(&registry_, device, remote_device->browsers()[0],
-                       forwarding_map_);
+        new Connection(profile_, &registry_, device,
+                       remote_device->browsers()[0], forwarding_map_);
       }
     } else {
       status.push_back(std::make_pair(rit->second->browser(),
diff --git a/chrome/browser/devtools/device/port_forwarding_controller.h b/chrome/browser/devtools/device/port_forwarding_controller.h
index 6d9e2d5..765b190 100644
--- a/chrome/browser/devtools/device/port_forwarding_controller.h
+++ b/chrome/browser/devtools/device/port_forwarding_controller.h
@@ -40,6 +40,7 @@
 
   void UpdateConnections();
 
+  Profile* profile_;
   PrefService* pref_service_;
   PrefChangeRegistrar pref_change_registrar_;
   Registry registry_;
diff --git a/chrome/browser/devtools/devtools_browser_context_manager.cc b/chrome/browser/devtools/devtools_browser_context_manager.cc
index b3d629cc..f762f80 100644
--- a/chrome/browser/devtools/devtools_browser_context_manager.cc
+++ b/chrome/browser/devtools/devtools_browser_context_manager.cc
@@ -51,6 +51,11 @@
   return result;
 }
 
+content::BrowserContext*
+DevToolsBrowserContextManager::GetDefaultBrowserContext() {
+  return ProfileManager::GetActiveUserProfile()->GetOriginalProfile();
+}
+
 void DevToolsBrowserContextManager::DisposeBrowserContext(
     content::BrowserContext* context,
     content::DevToolsManagerDelegate::DisposeCallback callback) {
diff --git a/chrome/browser/devtools/devtools_browser_context_manager.h b/chrome/browser/devtools/devtools_browser_context_manager.h
index 6fdaaca..6a00185 100644
--- a/chrome/browser/devtools/devtools_browser_context_manager.h
+++ b/chrome/browser/devtools/devtools_browser_context_manager.h
@@ -16,6 +16,7 @@
 
   Profile* GetProfileById(const std::string& browser_context_id);
   std::vector<content::BrowserContext*> GetBrowserContexts();
+  content::BrowserContext* GetDefaultBrowserContext();
   content::BrowserContext* CreateBrowserContext();
   void DisposeBrowserContext(
       content::BrowserContext* context,
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
index 090b3a16..0bb3df3 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -1838,7 +1838,7 @@
     EXPECT_EQ(1, tester.GetBucketCount(
                      "Extensions.DeclarativeNetRequest.LoadRulesetResult",
                      RulesetMatcher::LoadRulesetResult::
-                         kLoadErrorRulesetVerification /*sample*/));
+                         kLoadErrorChecksumMismatch /*sample*/));
     EXPECT_EQ(1,
               tester.GetBucketCount(
                   "Extensions.DeclarativeNetRequest.LoadRulesetResult",
@@ -1898,7 +1898,7 @@
   EXPECT_EQ(1, tester.GetBucketCount(
                    "Extensions.DeclarativeNetRequest.LoadRulesetResult",
                    RulesetMatcher::LoadRulesetResult::
-                       kLoadErrorRulesetVerification /*sample*/));
+                       kLoadErrorChecksumMismatch /*sample*/));
 
   // Verify that re-indexing the ruleset failed.
   tester.ExpectUniqueSample(
diff --git a/chrome/browser/extensions/api/declarative_net_request/ruleset_matcher_unittest.cc b/chrome/browser/extensions/api/declarative_net_request/ruleset_matcher_unittest.cc
index 4e6f395..2948dde 100644
--- a/chrome/browser/extensions/api/declarative_net_request/ruleset_matcher_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/ruleset_matcher_unittest.cc
@@ -146,7 +146,7 @@
   data = GetVersionHeaderForTesting() + "invalid data";
   ASSERT_EQ(static_cast<int>(data.size()),
             base::WriteFile(indexed_ruleset_path, data.c_str(), data.size()));
-  EXPECT_EQ(RulesetMatcher::kLoadErrorRulesetVerification,
+  EXPECT_EQ(RulesetMatcher::kLoadErrorChecksumMismatch,
             RulesetMatcher::CreateVerifiedMatcher(indexed_ruleset_path,
                                                   expected_checksum, &matcher));
 }
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
index f98b368..bd14c121 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/prerender/prerender_manager_factory.h"
 #include "chrome/browser/prerender/prerender_resource_throttle.h"
 #include "chrome/browser/prerender/prerender_util.h"
+#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_io_data.h"
 #include "chrome/browser/renderer_host/chrome_navigation_ui_data.h"
@@ -797,6 +798,13 @@
   content::PreviewsState previews_state =
       data_reduction_proxy::ContentLoFiDecider::
           DetermineCommittedServerPreviewsState(*request, initial_state);
+
+  // TODO(crbug.com/842233): This should be removed in the previews s13n work.
+  if (PreviewsLitePageNavigationThrottle::GetOriginalURL(
+          request->url(), nullptr /* original_url */)) {
+    previews_state = previews_state & content::LITE_PAGE_REDIRECT_ON;
+  }
+
   return previews::DetermineCommittedClientPreviewsState(
       *request, previews_state, previews_decider);
 }
diff --git a/chrome/browser/media/android/remote/flinging_controller_bridge.cc b/chrome/browser/media/android/remote/flinging_controller_bridge.cc
index 32904fd..abd04f5c 100644
--- a/chrome/browser/media/android/remote/flinging_controller_bridge.cc
+++ b/chrome/browser/media/android/remote/flinging_controller_bridge.cc
@@ -88,8 +88,13 @@
 }
 
 base::TimeDelta FlingingControllerBridge::GetApproximateCurrentTime() {
-  // TODO(https://crbug.com/830871): Implement this method.
-  return base::TimeDelta();
+  JNIEnv* env = base::android::AttachCurrentThread();
+  DCHECK(env);
+
+  long time_in_ms = Java_FlingingControllerBridge_getApproximateCurrentTime(
+      env, j_flinging_controller_bridge_);
+
+  return base::TimeDelta::FromMilliseconds(time_in_ms);
 }
 
 }  // namespace media_router
diff --git a/chrome/browser/policy/chrome_browser_policy_connector.cc b/chrome/browser/policy/chrome_browser_policy_connector.cc
index 939e8dcb..c545fe2 100644
--- a/chrome/browser/policy/chrome_browser_policy_connector.cc
+++ b/chrome/browser/policy/chrome_browser_policy_connector.cc
@@ -111,6 +111,7 @@
   auto providers = BrowserPolicyConnector::CreatePolicyProviders();
   std::unique_ptr<ConfigurationPolicyProvider> platform_provider =
       CreatePlatformProvider();
+  // TODO(crbug/869958): Add migrators here.
   if (platform_provider) {
     platform_provider_ = platform_provider.get();
     // PlatformProvider should be before all other providers (highest priority).
@@ -121,6 +122,7 @@
   std::unique_ptr<MachineLevelUserCloudPolicyManager>
       machine_level_user_cloud_policy_manager =
           MachineLevelUserCloudPolicyController::CreatePolicyManager();
+  // TODO(crbug/869958): Add migrators here.
   if (machine_level_user_cloud_policy_manager) {
     machine_level_user_cloud_policy_manager_ =
         machine_level_user_cloud_policy_manager.get();
diff --git a/chrome/browser/policy/policy_conversions.cc b/chrome/browser/policy/policy_conversions.cc
index a507889..4d7c648 100644
--- a/chrome/browser/policy/policy_conversions.cc
+++ b/chrome/browser/policy/policy_conversions.cc
@@ -165,8 +165,10 @@
     } else {
       // The PolicyMap contains errors about retrieving the policy, while the
       // PolicyErrorMap contains validation errors. Give priority to PolicyMap.
-      error = !policy.error.empty() ? base::UTF8ToUTF16(policy.error)
-                                    : errors->GetErrors(policy_name);
+      error = policy.GetLocalizedErrors(
+          base::BindRepeating(&l10n_util::GetStringUTF16));
+      if (error.empty())
+        error = errors->GetErrors(policy_name);
     }
     if (!error.empty())
       value.SetKey("error", Value(error));
diff --git a/chrome/browser/prefs/pref_service_incognito_whitelist.cc b/chrome/browser/prefs/pref_service_incognito_whitelist.cc
index e232f45..4c896a97a 100644
--- a/chrome/browser/prefs/pref_service_incognito_whitelist.cc
+++ b/chrome/browser/prefs/pref_service_incognito_whitelist.cc
@@ -13,13 +13,10 @@
 #include "components/consent_auditor/pref_names.h"
 #include "components/flags_ui/flags_ui_pref_names.h"
 #include "components/google/core/browser/google_pref_names.h"
-#include "components/invalidation/impl/invalidation_prefs.h"
 #include "components/metrics/metrics_pref_names.h"
-#include "components/omnibox/browser/omnibox_pref_names.h"
 #include "components/onc/onc_pref_names.h"
 #include "components/rappor/rappor_pref_names.h"
 #include "components/reading_list/core/reading_list_pref_names.h"
-#include "components/search_engines/search_engines_pref_names.h"
 #include "components/ukm/ukm_pref_names.h"
 #include "components/variations/pref_names.h"
 #include "components/web_resource/web_resource_pref_names.h"
@@ -579,24 +576,10 @@
     // components/flags_ui/flags_ui_pref_names.h
     flags_ui::prefs::kEnabledLabsExperiments,
 
-    // components/invalidation/impl/invalidation_prefs.h
-    invalidation::prefs::kInvalidatorClientId,
-    invalidation::prefs::kInvalidatorInvalidationState,
-    invalidation::prefs::kInvalidatorSavedInvalidations,
-    invalidation::prefs::kInvalidationServiceUseGCMChannel,
-
-    // components/omnibox/browser/omnibox_pref_names.h
-    omnibox::kZeroSuggestCachedResults,
-
     // components/onc/onc_pref_names.h
     onc::prefs::kDeviceOpenNetworkConfiguration,
     onc::prefs::kOpenNetworkConfiguration,
 
-    // components/search_engines/search_engines_pref_names.h
-    prefs::kSyncedDefaultSearchProviderGUID,
-    prefs::kDefaultSearchProviderEnabled, prefs::kSearchProviderOverrides,
-    prefs::kSearchProviderOverridesVersion, prefs::kCountryIDAtInstall,
-
     // components/web_resource/web_resource_pref_names.h
     prefs::kEulaAccepted,
 };
diff --git a/chrome/browser/previews/android/previews_android_bridge.cc b/chrome/browser/previews/android/previews_android_bridge.cc
new file mode 100644
index 0000000..35fa1142
--- /dev/null
+++ b/chrome/browser/previews/android/previews_android_bridge.cc
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/previews/android/previews_android_bridge.h"
+
+#include <memory>
+
+#include "base/android/jni_android.h"
+#include "chrome/browser/previews/previews_ui_tab_helper.h"
+#include "content/public/browser/web_contents.h"
+#include "jni/PreviewsAndroidBridge_jni.h"
+
+static jlong JNI_PreviewsAndroidBridge_Init(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj) {
+  return reinterpret_cast<intptr_t>(new PreviewsAndroidBridge(env, obj));
+}
+
+PreviewsAndroidBridge::PreviewsAndroidBridge(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj)
+    : weak_factory_(this) {}
+
+PreviewsAndroidBridge::~PreviewsAndroidBridge() {}
+
+jboolean PreviewsAndroidBridge::ShouldShowPreviewUI(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj,
+    const base::android::JavaParamRef<jobject>& j_web_contents) {
+  content::WebContents* web_contents =
+      content::WebContents::FromJavaWebContents(j_web_contents);
+  if (!web_contents)
+    return false;
+
+  PreviewsUITabHelper* tab_helper =
+      PreviewsUITabHelper::FromWebContents(web_contents);
+  if (!tab_helper)
+    return false;
+
+  return tab_helper->should_display_android_omnibox_badge();
+}
diff --git a/chrome/browser/previews/android/previews_android_bridge.h b/chrome/browser/previews/android/previews_android_bridge.h
new file mode 100644
index 0000000..480f8057
--- /dev/null
+++ b/chrome/browser/previews/android/previews_android_bridge.h
@@ -0,0 +1,32 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PREVIEWS_ANDROID_PREVIEWS_ANDROID_BRIDGE_H_
+#define CHROME_BROWSER_PREVIEWS_ANDROID_PREVIEWS_ANDROID_BRIDGE_H_
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/android/jni_weak_ref.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+
+class PreviewsAndroidBridge {
+ public:
+  PreviewsAndroidBridge(JNIEnv* env,
+                        const base::android::JavaParamRef<jobject>& obj);
+  virtual ~PreviewsAndroidBridge();
+
+  jboolean ShouldShowPreviewUI(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj,
+      const base::android::JavaParamRef<jobject>& j_web_contents);
+
+ private:
+  base::WeakPtrFactory<PreviewsAndroidBridge> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PreviewsAndroidBridge);
+};
+
+#endif  // CHROME_BROWSER_PREVIEWS_ANDROID_PREVIEWS_ANDROID_BRIDGE_H_
diff --git a/chrome/browser/previews/previews_browsertest.cc b/chrome/browser/previews/previews_browsertest.cc
index 503c6a64..d00b9e5 100644
--- a/chrome/browser/previews/previews_browsertest.cc
+++ b/chrome/browser/previews/previews_browsertest.cc
@@ -386,7 +386,8 @@
           base::FieldTrialList::CreateFieldTrial("TrialName1", "GroupName1");
       std::map<std::string, std::string> feature_parameters = {
           {"previews_host", previews_server().spec()},
-          {"blacklisted_path_suffixes", ".mp4,.jpg"}};
+          {"blacklisted_path_suffixes", ".mp4,.jpg"},
+          {"trigger_on_localhost", "true"}};
       base::FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams(
           "TrialName1", "GroupName1", feature_parameters);
 
@@ -549,6 +550,15 @@
   ui_test_utils::NavigateToURL(browser(), previews_server());
   EXPECT_EQ(NavigatedURL(), previews_server());
 
+  // Verify the preview is not triggered when navigating to a private IP.
+  ui_test_utils::NavigateToURL(browser(), GURL("https://0.0.0.0/"));
+  VerifyPreviewNotLoaded();
+
+  // Verify the preview is not triggered when navigating to a domain without a
+  // dot.
+  ui_test_utils::NavigateToURL(browser(), GURL("https://no-dots-here/"));
+  VerifyPreviewNotLoaded();
+
   // Verify a subframe navigation does not trigger a preview.
   const int starting_https_url_count = https_url_count();
   ui_test_utils::NavigateToURL(browser(), subframe_url());
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
index a340acfc..3ecfc865 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
@@ -9,6 +9,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "components/base32/base32.h"
 #include "components/previews/core/previews_experiments.h"
@@ -20,6 +21,7 @@
 #include "content/public/common/referrer.h"
 #include "crypto/sha2.h"
 #include "net/base/escape.h"
+#include "net/base/ip_address.h"
 #include "net/base/url_util.h"
 #include "net/http/http_status_code.h"
 #include "net/http/http_util.h"
@@ -34,6 +36,22 @@
          url.EffectiveIntPort() == previews_host.EffectiveIntPort();
 }
 
+bool IsPrivateDomain(const GURL& url) {
+  if (url.host().find(".") == base::StringPiece::npos)
+    return true;
+
+  // Allow localhost check to be skipped if needed, like in testing.
+  if (net::IsLocalhost(url))
+    return !previews::params::LitePagePreviewsTriggerOnLocalhost();
+
+  net::IPAddress ip_addr;
+  if (url.HostIsIPAddress() && ip_addr.AssignFromIPLiteral(url.host()) &&
+      !ip_addr.IsPubliclyRoutable()) {
+    return true;
+  }
+  return false;
+}
+
 content::OpenURLParams MakeOpenURLParams(content::NavigationHandle* handle,
                                          GURL url) {
   content::OpenURLParams url_params(
@@ -101,6 +119,9 @@
   if (IsPreviewsDomain(url))
     return false;
 
+  if (IsPrivateDomain(url))
+    return false;
+
   std::vector<std::string> blacklisted_path_suffixes =
       previews::params::LitePagePreviewsBlacklistedPathSuffixes();
   for (std::string suffix : blacklisted_path_suffixes) {
@@ -127,7 +148,8 @@
   if (!net::GetValueForKeyInQuery(url, "u", &original_url_query_param))
     return false;
 
-  *original_url = original_url_query_param;
+  if (original_url)
+    *original_url = original_url_query_param;
   return true;
 }
 
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.h b/chrome/browser/previews/previews_lite_page_navigation_throttle.h
index 228ec04..1ec8cc91 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.h
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.h
@@ -27,7 +27,8 @@
   ~PreviewsLitePageNavigationThrottle() override;
 
   // Attempts to extract the original URL from the given Previews URL. Returns
-  // false if |url| is not a valid Preview URL.
+  // false if |url| is not a valid Preview URL. It is ok to pass nullptr for
+  // |original_url| if you only want the boolean return value.
   static bool GetOriginalURL(GURL url, std::string* original_url);
 
  private:
diff --git a/chrome/browser/previews/previews_ui_tab_helper.cc b/chrome/browser/previews/previews_ui_tab_helper.cc
index e060e08b..4851efc 100644
--- a/chrome/browser/previews/previews_ui_tab_helper.cc
+++ b/chrome/browser/previews/previews_ui_tab_helper.cc
@@ -6,6 +6,8 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/feature_list.h"
+#include "build/build_config.h"
 #include "chrome/browser/loader/chrome_navigation_data.h"
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h"
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h"
@@ -21,6 +23,7 @@
 #include "components/previews/content/previews_content_util.h"
 #include "components/previews/content/previews_ui_service.h"
 #include "components/previews/core/previews_experiments.h"
+#include "components/previews/core/previews_features.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
@@ -68,15 +71,21 @@
   previews::PreviewsUIService* previews_ui_service =
       previews_service ? previews_service->previews_ui_service() : nullptr;
 
+#if defined(OS_ANDROID)
+  if (base::FeatureList::IsEnabled(
+          previews::features::kAndroidOmniboxPreviewsBadge)) {
+    should_display_android_omnibox_badge_ = true;
+    return;
+  }
+#endif
+
   PreviewsInfoBarDelegate::Create(
       web_contents(), previews_type, previews_freshness, is_data_saver_user,
       is_reload, std::move(on_dismiss_callback), previews_ui_service);
 }
 
 PreviewsUITabHelper::PreviewsUITabHelper(content::WebContents* web_contents)
-    : content::WebContentsObserver(web_contents),
-      displayed_preview_ui_(false),
-      displayed_preview_timestamp_(false) {
+    : content::WebContentsObserver(web_contents) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 }
 
@@ -88,6 +97,7 @@
     return;
 
   previews_user_data_.reset();
+  should_display_android_omnibox_badge_ = false;
   // Store Previews information for this navigation.
   ChromeNavigationData* nav_data = static_cast<ChromeNavigationData*>(
       navigation_handle->GetNavigationData());
diff --git a/chrome/browser/previews/previews_ui_tab_helper.h b/chrome/browser/previews/previews_ui_tab_helper.h
index bbec8027d..14c4a4e6 100644
--- a/chrome/browser/previews/previews_ui_tab_helper.h
+++ b/chrome/browser/previews/previews_ui_tab_helper.h
@@ -41,6 +41,12 @@
     displayed_preview_ui_ = displayed;
   }
 
+  // Indicates whether the Android Omnibox badge should be shown as the Previews
+  // UI.
+  bool should_display_android_omnibox_badge() const {
+    return should_display_android_omnibox_badge_;
+  }
+
   // Sets whether the timestamp on the UI for a preview has been shown for
   // the page. |displayed_preview_timestamp_| is reset to false on
   // DidStartProvisionalLoadForFrame for the main frame.
@@ -65,10 +71,13 @@
       content::NavigationHandle* navigation_handle) override;
 
   // True if the UI for a preview has been shown for the page.
-  bool displayed_preview_ui_;
+  bool displayed_preview_ui_ = false;
 
   // True if the UI with a timestamp was shown for the page.
-  bool displayed_preview_timestamp_;
+  bool displayed_preview_timestamp_ = false;
+
+  // True if the Android Omnibox badge should be shown as the Previews UI.
+  bool should_display_android_omnibox_badge_ = false;
 
   // The Previews information related to the navigation that was most recently
   // finished.
diff --git a/chrome/browser/previews/previews_ui_tab_helper_unittest.cc b/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
index f29381a5..3876c39 100644
--- a/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
+++ b/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
@@ -10,7 +10,9 @@
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
 #include "chrome/browser/infobars/mock_infobar_service.h"
 #include "chrome/browser/loader/chrome_navigation_data.h"
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h"
@@ -28,6 +30,7 @@
 #include "components/offline_pages/core/offline_page_item.h"
 #include "components/offline_pages/core/request_header/offline_page_header.h"
 #include "components/prefs/pref_registry_simple.h"
+#include "components/previews/core/previews_features.h"
 #include "components/previews/core/previews_user_data.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
 #include "content/public/browser/navigation_handle.h"
@@ -169,6 +172,28 @@
   EXPECT_FALSE(ui_tab_helper->displayed_preview_ui());
 }
 
+#if defined(OS_ANDROID)
+TEST_F(PreviewsUITabHelperUnitTest, DidFinishNavigationDisplaysOmniboxBadge) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      previews::features::kAndroidOmniboxPreviewsBadge);
+
+  PreviewsUITabHelper* ui_tab_helper =
+      PreviewsUITabHelper::FromWebContents(web_contents());
+  EXPECT_FALSE(ui_tab_helper->displayed_preview_ui());
+  EXPECT_FALSE(ui_tab_helper->should_display_android_omnibox_badge());
+  EXPECT_EQ(0U, infobar_service()->infobar_count());
+
+  SetCommittedPreviewsType(previews::PreviewsType::LITE_PAGE);
+  SimulateWillProcessResponse();
+  CallDidFinishNavigation();
+
+  EXPECT_TRUE(ui_tab_helper->should_display_android_omnibox_badge());
+  EXPECT_FALSE(ui_tab_helper->displayed_preview_ui());
+  EXPECT_EQ(0U, infobar_service()->infobar_count());
+}
+#endif
+
 TEST_F(PreviewsUITabHelperUnitTest,
        DidFinishNavigationCreatesNoScriptPreviewsInfoBar) {
   PreviewsUITabHelper* ui_tab_helper =
diff --git a/chrome/browser/resources/chromeos/drive_internals.html b/chrome/browser/resources/chromeos/drive_internals.html
index d0e0ae0..c67a0c0 100644
--- a/chrome/browser/resources/chromeos/drive_internals.html
+++ b/chrome/browser/resources/chromeos/drive_internals.html
@@ -142,5 +142,9 @@
     <h2 id="event-log-section">Event Log</h2>
     <ul id="event-log">
     </ul>
+
+    <h2 id="service-log-section">Service Log</h2>
+    <ul id="service-log">
+    </ul>
   </body>
 </html>
diff --git a/chrome/browser/resources/chromeos/drive_internals.js b/chrome/browser/resources/chromeos/drive_internals.js
index b49c598a..5860cc6 100644
--- a/chrome/browser/resources/chromeos/drive_internals.js
+++ b/chrome/browser/resources/chromeos/drive_internals.js
@@ -222,6 +222,15 @@
 }
 
 /**
+ * Updates the service log section.
+ * @param {Array} log Log lines.
+ */
+function updateServiceLog(log) {
+  var ul = $('service-log');
+  updateKeyValueList(ul, log);
+}
+
+/**
  * Creates an element named |elementName| containing the content |text|.
  * @param {string} elementName Name of the new element to be created.
  * @param {string} text Text to be contained in the new element.
diff --git a/chrome/browser/resources/policy.css b/chrome/browser/resources/policy.css
index be7e1e4..0744dea6 100644
--- a/chrome/browser/resources/policy.css
+++ b/chrome/browser/resources/policy.css
@@ -38,4 +38,8 @@
 
 section.status-box-section {
   clear: both;
-}
\ No newline at end of file
+}
+
+div.preformatted {
+  white-space: pre;
+}
diff --git a/chrome/browser/resources/policy.html b/chrome/browser/resources/policy.html
index b4dd54c..b142a6ab 100644
--- a/chrome/browser/resources/policy.html
+++ b/chrome/browser/resources/policy.html
@@ -123,7 +123,7 @@
             </div>
           </td>
           <td class="status-column">
-            <div class="status elide"></div>
+            <div class="status elide preformatted"></div>
           </td>
         </tr>
         <tr class="expanded-value-container">
diff --git a/chrome/browser/resources/print_preview/new/model.js b/chrome/browser/resources/print_preview/new/model.js
index bbe12e5..eb01df7 100644
--- a/chrome/browser/resources/print_preview/new/model.js
+++ b/chrome/browser/resources/print_preview/new/model.js
@@ -360,10 +360,12 @@
 
   /** @private */
   updateSettingsAvailabilityFromDestinationAndDocumentInfo_: function() {
-    const knownSizeToSaveAsPdf = this.destination.id ==
-            print_preview.Destination.GooglePromotedId.SAVE_AS_PDF &&
+    const isSaveAsPDF = this.destination.id ==
+        print_preview.Destination.GooglePromotedId.SAVE_AS_PDF;
+    const knownSizeToSaveAsPdf = isSaveAsPDF &&
         (!this.documentInfo.isModifiable ||
          this.documentInfo.hasCssMediaStyles);
+    this.set('settings.fitToPage.unavailableValue', !isSaveAsPDF);
     this.set(
         'settings.fitToPage.available',
         !knownSizeToSaveAsPdf && !this.documentInfo.isModifiable);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 27ca8b6..40ae6791 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2169,7 +2169,7 @@
       "//services/device/public/cpp:device_features",
       "//services/device/public/mojom",
       "//services/ui/public/cpp",
-      "//services/ui/public/interfaces",
+      "//services/ws/public/mojom",
       "//ui/base/ime",
       "//ui/chromeos",
       "//ui/chromeos/events",
@@ -3376,7 +3376,7 @@
       if (use_aura) {
         deps += [
           "//services/ui/public/cpp",
-          "//services/ui/public/interfaces",
+          "//services/ws/public/mojom",
         ]
       }
     }
diff --git a/chrome/browser/ui/ash/accelerator_commands_browsertest.cc b/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
index 5d863ff..a2b5761 100644
--- a/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
+++ b/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
@@ -19,7 +19,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/native_app_window.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/views/widget/widget.h"
diff --git a/chrome/browser/ui/ash/ash_util.cc b/chrome/browser/ui/ash/ash_util.cc
index 07f3253..b037cf5 100644
--- a/chrome/browser/ui/ash/ash_util.cc
+++ b/chrome/browser/ui/ash/ash_util.cc
@@ -13,7 +13,7 @@
 #include "content/public/common/service_manager_connection.h"
 #include "mojo/public/cpp/bindings/type_converter.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/display/display.h"
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index 7496085..ff89ba2 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -59,8 +59,8 @@
 #include "content/public/common/service_manager_connection.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/user_activity_monitor.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/user_activity_monitor.mojom.h"
 #include "ui/aura/mus/property_converter.h"
 #include "ui/aura/mus/user_activity_forwarder.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
diff --git a/chrome/browser/ui/ash/launcher/settings_window_observer.cc b/chrome/browser/ui/ash/launcher/settings_window_observer.cc
index cf69678..9b74d61 100644
--- a/chrome/browser/ui/ash/launcher/settings_window_observer.cc
+++ b/chrome/browser/ui/ash/launcher/settings_window_observer.cc
@@ -12,7 +12,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "components/strings/grit/components_strings.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/base/class_property.h"
diff --git a/chrome/browser/ui/ash/system_tray_client.cc b/chrome/browser/ui/ash/system_tray_client.cc
index 9e19346..f1597a5c 100644
--- a/chrome/browser/ui/ash/system_tray_client.cc
+++ b/chrome/browser/ui/ash/system_tray_client.cc
@@ -54,7 +54,7 @@
 #include "net/base/escape.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/events/event_constants.h"
diff --git a/chrome/browser/ui/extensions/extension_action_view_controller.cc b/chrome/browser/ui/extensions/extension_action_view_controller.cc
index 2c7bfa4d..ef2c9dd 100644
--- a/chrome/browser/ui/extensions/extension_action_view_controller.cc
+++ b/chrome/browser/ui/extensions/extension_action_view_controller.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/api/commands/command_service.h"
 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
@@ -24,11 +25,13 @@
 #include "chrome/browser/ui/toolbar/toolbar_action_view_delegate.h"
 #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
 #include "chrome/common/extensions/api/extension_action/action_info.h"
+#include "chrome/grit/generated_resources.h"
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_features.h"
 #include "extensions/common/manifest_constants.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_skia_operations.h"
 
@@ -103,9 +106,44 @@
   if (!ExtensionIsValid())
     return base::string16();
 
+  // GetAccessibleName() can (surprisingly) be called during browser
+  // teardown. Handle this gracefully.
+  if (!web_contents)
+    return base::UTF8ToUTF16(extension()->name());
+
   std::string title = extension_action()->GetTitle(
       SessionTabHelper::IdForTab(web_contents).id());
-  return base::UTF8ToUTF16(title.empty() ? extension()->name() : title);
+
+  base::string16 title_utf16 =
+      base::UTF8ToUTF16(title.empty() ? extension()->name() : title);
+
+  // With runtime host permissions, include a "host access" portion of the
+  // tooltip if the extension has or wants access to the site.
+  if (base::FeatureList::IsEnabled(
+          extensions::features::kRuntimeHostPermissions)) {
+    PageInteractionStatus interaction_status =
+        GetPageInteractionStatus(web_contents);
+    int interaction_status_description_id = -1;
+    switch (interaction_status) {
+      case PageInteractionStatus::kNone:
+        // No string for neither having nor wanting access.
+        break;
+      case PageInteractionStatus::kPending:
+        interaction_status_description_id = IDS_EXTENSIONS_WANTS_ACCESS_TO_SITE;
+        break;
+      case PageInteractionStatus::kActive:
+        interaction_status_description_id = IDS_EXTENSIONS_HAS_ACCESS_TO_SITE;
+        break;
+    }
+
+    if (interaction_status_description_id != -1) {
+      title_utf16 = base::StrCat(
+          {title_utf16, base::UTF8ToUTF16("\n"),
+           l10n_util::GetStringUTF16(interaction_status_description_id)});
+    }
+  }
+
+  return title_utf16;
 }
 
 base::string16 ExtensionActionViewController::GetTooltip(
@@ -251,26 +289,21 @@
 
 ExtensionActionViewController::PageInteractionStatus
 ExtensionActionViewController::GetPageInteractionStatus(
-    content::WebContents* web_contents) {
+    content::WebContents* web_contents) const {
   // We give priority to kPending, because it's the one that's most important
   // for users to see.
   if (HasBeenBlocked(web_contents))
     return PageInteractionStatus::kPending;
 
-  // We consider an extension active on a page if either the extension action
-  // is active (in which case it can be clicked) or if the extension has
-  // permission to acccess the page (in which case it can inject scripts and
-  // intercept webRequests).
   // NOTE(devlin): We could theoretically adjust this to only be considered
   // active if the extension *did* act on the page, rather than if it *could*.
   // This is a bit more complex, and it's unclear if this is a better UX, since
   // it would lead to much less determinism in terms of what extensions look
   // like on a given host.
   int tab_id = SessionTabHelper::IdForTab(web_contents).id();
-  if (extension_action_->GetIsVisible(tab_id) ||
-      extension_->permissions_data()->GetPageAccess(
+  if (extension_->permissions_data()->GetPageAccess(
           web_contents->GetLastCommittedURL(), tab_id, /*error=*/nullptr) ==
-          extensions::PermissionsData::PageAccess::kAllowed) {
+      extensions::PermissionsData::PageAccess::kAllowed) {
     return PageInteractionStatus::kActive;
   }
 
@@ -417,18 +450,20 @@
 
   bool grayscale = false;
   bool was_blocked = false;
+  bool action_is_visible = extension_action_->GetIsVisible(tab_id);
   if (base::FeatureList::IsEnabled(
           extensions::features::kRuntimeHostPermissions)) {
     PageInteractionStatus interaction_status =
         GetPageInteractionStatus(web_contents);
     // With the runtime host permissions feature, we only grayscale the icon if
     // it cannot interact with the page and the icon is disabled.
-    grayscale = interaction_status == PageInteractionStatus::kNone;
+    grayscale = interaction_status == PageInteractionStatus::kNone &&
+                !action_is_visible;
     was_blocked = interaction_status == PageInteractionStatus::kPending;
   } else {
     // Without runtime host permissions enabled, grayscaling is purely used to
     // indicate "clickability", and not any kind of access.
-    grayscale = !extension_action_->GetIsVisible(tab_id);
+    grayscale = !action_is_visible;
     // was_blocked is always false without runtime host permissions.
   }
 
diff --git a/chrome/browser/ui/extensions/extension_action_view_controller.h b/chrome/browser/ui/extensions/extension_action_view_controller.h
index dbef20f..9c8abfe 100644
--- a/chrome/browser/ui/extensions/extension_action_view_controller.h
+++ b/chrome/browser/ui/extensions/extension_action_view_controller.h
@@ -97,20 +97,20 @@
   // ExtensionHostObserver:
   void OnExtensionHostDestroyed(const extensions::ExtensionHost* host) override;
 
-  // The status of the extension's interaction for the page.
+  // The status of the extension's interaction for the page. This is independent
+  // of the action's clickability.
   enum class PageInteractionStatus {
-    // The extension cannot run on the page and cannot be clicked on the page.
+    // The extension cannot run on the page.
     kNone,
-    // The extension tried to inject on the page, but is pending user approval.
+    // The extension tried to access the page, but is pending user approval.
     kPending,
-    // The extension has permission to run on the page, or is clickable on the
-    // page and has no pending injections.
+    // The extension has permission to run on the page.
     kActive,
   };
 
   // Returns the PageInteractionStatus for the current page.
   PageInteractionStatus GetPageInteractionStatus(
-      content::WebContents* web_contents);
+      content::WebContents* web_contents) const;
 
   // Checks if the associated |extension| is still valid by checking its
   // status in the registry. Since the OnExtensionUnloaded() notifications are
diff --git a/chrome/browser/ui/extensions/extension_action_view_controller_unittest.cc b/chrome/browser/ui/extensions/extension_action_view_controller_unittest.cc
index 423877d..7f6d63d2 100644
--- a/chrome/browser/ui/extensions/extension_action_view_controller_unittest.cc
+++ b/chrome/browser/ui/extensions/extension_action_view_controller_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/run_loop.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/extensions/extension_action.h"
 #include "chrome/browser/extensions/extension_action_manager.h"
@@ -321,3 +322,57 @@
     action_runner->ClearInjectionsForTesting(*extension);
   }
 }
+
+// Tests the tooltip shown when an extension requests or has access to a page
+// with the runtime host permissions feature enabled.
+TEST_P(ToolbarActionsBarUnitTest, RuntimeHostsTooltip) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      extensions::features::kRuntimeHostPermissions);
+
+  scoped_refptr<const extensions::Extension> extension =
+      extensions::ExtensionBuilder("extension name")
+          .SetAction(extensions::ExtensionBuilder::ActionType::BROWSER_ACTION)
+          .SetLocation(extensions::Manifest::INTERNAL)
+          .AddPermission("https://www.google.com/*")
+          .Build();
+  extensions::ExtensionService* service =
+      extensions::ExtensionSystem::Get(profile())->extension_service();
+  service->GrantPermissions(extension.get());
+  service->AddExtension(extension.get());
+
+  extensions::ScriptingPermissionsModifier permissions_modifier(profile(),
+                                                                extension);
+  permissions_modifier.SetWithholdHostPermissions(true);
+  ASSERT_EQ(1u, toolbar_actions_bar()->GetIconCount());
+  const GURL kUrl("https://www.google.com/");
+  AddTab(browser(), kUrl);
+
+  auto* controller = static_cast<ExtensionActionViewController*>(
+      toolbar_actions_bar()->GetActions()[0]);
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  int tab_id = SessionTabHelper::IdForTab(web_contents).id();
+
+  // Page access should already be withheld.
+  EXPECT_EQ(extensions::PermissionsData::PageAccess::kWithheld,
+            extension->permissions_data()->GetPageAccess(kUrl, tab_id,
+                                                         /*error=*/nullptr));
+  EXPECT_EQ("extension name",
+            base::UTF16ToUTF8(controller->GetTooltip(web_contents)));
+
+  // Request access.
+  extensions::ExtensionActionRunner* action_runner =
+      extensions::ExtensionActionRunner::GetForWebContents(web_contents);
+  action_runner->RequestScriptInjectionForTesting(
+      extension.get(), extensions::UserScript::DOCUMENT_IDLE,
+      base::DoNothing());
+  EXPECT_EQ("extension name\nWants access to this site",
+            base::UTF16ToUTF8(controller->GetTooltip(web_contents)));
+
+  // Grant access.
+  action_runner->ClearInjectionsForTesting(*extension);
+  permissions_modifier.GrantHostPermission(kUrl);
+  EXPECT_EQ("extension name\nHas access to this site",
+            base::UTF16ToUTF8(controller->GetTooltip(web_contents)));
+}
diff --git a/chrome/browser/ui/libgtkui/BUILD.gn b/chrome/browser/ui/libgtkui/BUILD.gn
index acf574b..3932e0c 100644
--- a/chrome/browser/ui/libgtkui/BUILD.gn
+++ b/chrome/browser/ui/libgtkui/BUILD.gn
@@ -5,6 +5,7 @@
 assert(is_linux, "This file should only be referenced on Linux")
 
 import("//build/config/features.gni")
+import("//build/config/linux/gtk/gtk.gni")
 import("//printing/buildflags/buildflags.gni")
 
 component("libgtkui") {
@@ -13,8 +14,6 @@
     "app_indicator_icon.h",
     "app_indicator_icon_menu.cc",
     "app_indicator_icon_menu.h",
-    "chrome_gtk_frame.cc",
-    "chrome_gtk_frame.h",
     "chrome_gtk_menu_subclasses.cc",
     "chrome_gtk_menu_subclasses.h",
     "gtk_background_painter.cc",
@@ -24,8 +23,6 @@
     "gtk_key_bindings_handler.cc",
     "gtk_key_bindings_handler.h",
     "gtk_signal.h",
-    "gtk_status_icon.cc",
-    "gtk_status_icon.h",
     "gtk_ui.cc",
     "gtk_ui.h",
     "gtk_util.cc",
@@ -57,6 +54,13 @@
     "x11_input_method_context_impl_gtk.h",
   ]
 
+  if (gtk_version <= 3) {
+    sources += [
+      "gtk_status_icon.cc",
+      "gtk_status_icon.h",
+    ]
+  }
+
   configs += [
     "//build/config/linux/pangocairo",
     "//build/config/linux:x11",
diff --git a/chrome/browser/ui/libgtkui/chrome_gtk_frame.cc b/chrome/browser/ui/libgtkui/chrome_gtk_frame.cc
deleted file mode 100644
index 6820a47..0000000
--- a/chrome/browser/ui/libgtkui/chrome_gtk_frame.cc
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright (c) 2012 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/libgtkui/chrome_gtk_frame.h"
-
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-
-G_BEGIN_DECLS
-
-// MetaFrames declaration
-G_DEFINE_TYPE(MetaFrames, meta_frames, GTK_TYPE_WINDOW)
-
-static void meta_frames_class_init(MetaFramesClass* frames_class) {
-  // Noop since we don't declare anything.
-}
-
-static void meta_frames_init(MetaFrames* button) {
-}
-
-
-// ChromeGtkFrame declaration
-G_DEFINE_TYPE(ChromeGtkFrame, chrome_gtk_frame, meta_frames_get_type())
-
-static void chrome_gtk_frame_class_init(ChromeGtkFrameClass* frame_class) {
-  GtkWidgetClass* widget_class = reinterpret_cast<GtkWidgetClass*>(frame_class);
-
-  // Frame tints:
-  gtk_widget_class_install_style_property(
-      widget_class,
-      g_param_spec_boxed(
-          "frame-color",
-          "Frame Color",
-          "The color that the chrome frame will be. (If unspecified, "
-            " Chrome will take ChromeGtkFrame::bg[SELECTED] and slightly darken"
-            " it.)",
-          GDK_TYPE_COLOR,
-          G_PARAM_READABLE));
-  gtk_widget_class_install_style_property(
-      widget_class,
-      g_param_spec_boxed(
-          "inactive-frame-color",
-          "Inactive Frame Color",
-          "The color that the inactive chrome frame will be. (If"
-            " unspecified, Chrome will take ChromeGtkFrame::bg[INSENSITIVE]"
-            " and slightly darken it.)",
-          GDK_TYPE_COLOR,
-          G_PARAM_READABLE));
-  gtk_widget_class_install_style_property(
-      widget_class,
-      g_param_spec_boxed(
-          "incognito-frame-color",
-          "Incognito Frame Color",
-          "The color that the incognito frame will be. (If unspecified,"
-            " Chrome will take the frame color and tint it by Chrome's default"
-            " incognito tint.)",
-          GDK_TYPE_COLOR,
-          G_PARAM_READABLE));
-  gtk_widget_class_install_style_property(
-      widget_class,
-      g_param_spec_boxed(
-          "incognito-inactive-frame-color",
-          "Incognito Inactive Frame Color",
-          "The color that the inactive incognito frame will be. (If"
-            " unspecified, Chrome will take the frame color and tint it by"
-            " Chrome's default incognito tint.)",
-          GDK_TYPE_COLOR,
-          G_PARAM_READABLE));
-
-  // Frame gradient control:
-  gtk_widget_class_install_style_property(
-      widget_class,
-      g_param_spec_int(
-          "frame-gradient-size",
-          "Chrome Frame Gradient Size",
-          "The size of the gradient on top of the frame image. Specify 0 to"
-            " make the frame a solid color.",
-          0,      // 0 disables the gradient
-          128,    // The frame image is only up to 128 pixels tall.
-          16,     // By default, gradients are 16 pixels high.
-          G_PARAM_READABLE));
-  gtk_widget_class_install_style_property(
-      widget_class,
-      g_param_spec_boxed(
-          "frame-gradient-color",
-          "Frame Gradient Color",
-          "The top color of the chrome frame gradient. (If unspecified,"
-            " chrome will create a lighter tint of frame-color",
-          GDK_TYPE_COLOR,
-          G_PARAM_READABLE));
-  gtk_widget_class_install_style_property(
-      widget_class,
-      g_param_spec_boxed(
-          "inactive-frame-gradient-color",
-          "Inactive Frame Gradient Color",
-          "The top color of the inactive chrome frame gradient. (If"
-            " unspecified, chrome will create a lighter tint of frame-color",
-          GDK_TYPE_COLOR,
-          G_PARAM_READABLE));
-  gtk_widget_class_install_style_property(
-      widget_class,
-      g_param_spec_boxed(
-          "incognito-frame-gradient-color",
-          "Incognito Frame Gradient Color",
-          "The top color of the incognito chrome frame gradient. (If"
-            " unspecified, chrome will create a lighter tint of frame-color",
-          GDK_TYPE_COLOR,
-          G_PARAM_READABLE));
-  gtk_widget_class_install_style_property(
-      widget_class,
-      g_param_spec_boxed(
-          "incognito-inactive-frame-gradient-color",
-          "Incognito Inactive Frame Gradient Color",
-          "The top color of the incognito inactive chrome frame gradient. (If"
-            " unspecified, chrome will create a lighter tint of frame-color",
-          GDK_TYPE_COLOR,
-          G_PARAM_READABLE));
-
-  // Scrollbar color properties:
-  gtk_widget_class_install_style_property(
-      widget_class,
-      g_param_spec_boxed(
-          "scrollbar-slider-prelight-color",
-          "Scrollbar Slider Prelight Color",
-          "The color applied to the mouse is above the tab",
-          GDK_TYPE_COLOR,
-          G_PARAM_READABLE));
-  gtk_widget_class_install_style_property(
-      widget_class,
-      g_param_spec_boxed(
-          "scrollbar-slider-normal-color",
-          "Scrollbar Slider Normal Color",
-          "The color applied to the slider normally",
-          GDK_TYPE_COLOR,
-          G_PARAM_READABLE));
-  gtk_widget_class_install_style_property(
-      widget_class,
-      g_param_spec_boxed(
-          "scrollbar-trough-color",
-          "Scrollbar Trough Color",
-          "The background color of the slider track",
-          GDK_TYPE_COLOR,
-          G_PARAM_READABLE));
-}
-
-static void chrome_gtk_frame_init(ChromeGtkFrame* frame) {
-}
-
-GtkWidget* chrome_gtk_frame_new(void) {
-  return GTK_WIDGET(g_object_new(chrome_gtk_frame_get_type(), "type",
-                                 GTK_WINDOW_TOPLEVEL, nullptr));
-}
-
-G_END_DECLS
diff --git a/chrome/browser/ui/libgtkui/chrome_gtk_frame.h b/chrome/browser/ui/libgtkui/chrome_gtk_frame.h
deleted file mode 100644
index 2d78345..0000000
--- a/chrome/browser/ui/libgtkui/chrome_gtk_frame.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2012 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_LIBGTKUI_CHROME_GTK_FRAME_H_
-#define CHROME_BROWSER_UI_LIBGTKUI_CHROME_GTK_FRAME_H_
-
-#include <gtk/gtk.h>
-
-G_BEGIN_DECLS
-
-// This file declares two subclasses of GtkWindow for easier gtk+ theme
-// integration.
-//
-// The first is "MetaFrames," which is (was?) the name of a gobject class in
-// the metacity window manager. To actually get at those values, we need to
-// have an object whose gobject class name string matches the definitions in
-// the gtkrc file. MetaFrames derives from GtkWindow.
-//
-// Metaframes can not be instantiated. It has no constructor; instantiate
-// ChromeGtkFrame instead.
-typedef struct _MetaFrames       MetaFrames;
-typedef struct _MetaFramesClass  MetaFramesClass;
-
-struct _MetaFrames {
-  GtkWindow window;
-};
-
-struct _MetaFramesClass {
-  GtkWindowClass parent_class;
-};
-
-
-// The second is ChromeGtkFrame, which defines a number of optional style
-// properties so theme authors can control how chromium appears in gtk-theme
-// mode.  It derives from MetaFrames in chrome so older themes that declare a
-// MetaFrames theme will still work. New themes should target this class.
-typedef struct _ChromeGtkFrame       ChromeGtkFrame;
-typedef struct _ChromeGtkFrameClass  ChromeGtkFrameClass;
-
-struct _ChromeGtkFrame {
-  MetaFrames frames;
-};
-
-struct _ChromeGtkFrameClass {
-  MetaFramesClass frames_class;
-};
-
-// Creates a GtkWindow object the the class name "ChromeGtkFrame".
-GtkWidget* chrome_gtk_frame_new();
-
-G_END_DECLS
-
-#endif  // CHROME_BROWSER_UI_LIBGTKUI_CHROME_GTK_FRAME_H_
diff --git a/chrome/browser/ui/libgtkui/gtk_key_bindings_handler.cc b/chrome/browser/ui/libgtkui/gtk_key_bindings_handler.cc
index f85c638..95264b3 100644
--- a/chrome/browser/ui/libgtkui/gtk_key_bindings_handler.cc
+++ b/chrome/browser/ui/libgtkui/gtk_key_bindings_handler.cc
@@ -28,10 +28,22 @@
 // linking requirements regarding GTK+ as we don't have a libgtkui_unittests
 // yet. http://crbug.com/358297.
 
+namespace {
+
+GtkWidget* CreateInvisibleWindow() {
+#if GTK_CHECK_VERSION(3, 90, 0)
+  return gtk_invisible_new();
+#else
+  return gtk_offscreen_window_new();
+#endif
+}
+
+}  // namespace
+
 namespace libgtkui {
 
 GtkKeyBindingsHandler::GtkKeyBindingsHandler()
-    : fake_window_(gtk_offscreen_window_new()),
+    : fake_window_(CreateInvisibleWindow()),
       handler_(CreateNewHandler()),
       has_xkb_(false) {
   gtk_container_add(GTK_CONTAINER(fake_window_), handler_);
@@ -65,11 +77,7 @@
   // will be emitted.
 
   gtk_bindings_activate_event(
-#if GDK_MAJOR_VERSION >= 3
       G_OBJECT(handler_),
-#else
-      GTK_OBJECT(handler_),
-#endif
       &gdk_event);
 
   bool matched = !edit_commands_.empty();
@@ -90,7 +98,9 @@
 
   // Prevents it from handling any events by itself.
   gtk_widget_set_sensitive(GTK_WIDGET(handler), FALSE);
+#if !GTK_CHECK_VERSION(3, 90, 0)
   gtk_widget_set_events(GTK_WIDGET(handler), 0);
+#endif
   gtk_widget_set_can_focus(GTK_WIDGET(handler), TRUE);
 
   return GTK_WIDGET(handler);
@@ -145,7 +155,6 @@
 
 void GtkKeyBindingsHandler::HandlerClassInit(HandlerClass* klass) {
   GtkTextViewClass* text_view_class = GTK_TEXT_VIEW_CLASS(klass);
-  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
 
   // Overrides all virtual methods related to editor key bindings.
   text_view_class->backspace = BackSpace;
@@ -157,7 +166,10 @@
   text_view_class->paste_clipboard = PasteClipboard;
   text_view_class->set_anchor = SetAnchor;
   text_view_class->toggle_overwrite = ToggleOverwrite;
+#if !GTK_CHECK_VERSION(3, 90, 0)
+  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
   widget_class->show_help = ShowHelp;
+#endif
 
   // "move-focus", "move-viewport", "select-all" and "toggle-cursor-visible"
   // have no corresponding virtual methods. Since glib 2.18 (gtk 2.14),
@@ -418,11 +430,13 @@
   // Not supported by webkit.
 }
 
+#if !GTK_CHECK_VERSION(3, 90, 0)
 gboolean GtkKeyBindingsHandler::ShowHelp(GtkWidget* widget,
                                          GtkWidgetHelpType arg1) {
   // Just for disabling the default handler.
   return FALSE;
 }
+#endif
 
 void GtkKeyBindingsHandler::MoveFocus(GtkWidget* widget,
                                       GtkDirectionType arg1) {
diff --git a/chrome/browser/ui/libgtkui/gtk_key_bindings_handler.h b/chrome/browser/ui/libgtkui/gtk_key_bindings_handler.h
index 7a31e2a..055487c6 100644
--- a/chrome/browser/ui/libgtkui/gtk_key_bindings_handler.h
+++ b/chrome/browser/ui/libgtkui/gtk_key_bindings_handler.h
@@ -128,8 +128,10 @@
   // Handler of "toggle-overwrite" signal.
   static void ToggleOverwrite(GtkTextView* text_view);
 
+#if !GTK_CHECK_VERSION(3, 90, 0)
   // Handler of "show-help" signal.
   static gboolean ShowHelp(GtkWidget* widget, GtkWidgetHelpType arg1);
+#endif
 
   // Handler of "move-focus" signal.
   static void MoveFocus(GtkWidget* widget, GtkDirectionType arg1);
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.cc b/chrome/browser/ui/libgtkui/gtk_ui.cc
index c0eba77f..89b62285 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.cc
+++ b/chrome/browser/ui/libgtkui/gtk_ui.cc
@@ -30,7 +30,6 @@
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/libgtkui/app_indicator_icon.h"
-#include "chrome/browser/ui/libgtkui/chrome_gtk_frame.h"
 #include "chrome/browser/ui/libgtkui/gtk_event_loop.h"
 #include "chrome/browser/ui/libgtkui/gtk_key_bindings_handler.h"
 #include "chrome/browser/ui/libgtkui/gtk_status_icon.h"
@@ -149,6 +148,7 @@
     if (focus_) {
       gfx::Rect focus_rect(width, height);
 
+#if !GTK_CHECK_VERSION(3, 90, 0)
       if (!GtkVersionCheck(3, 14)) {
         gint focus_pad;
         gtk_style_context_get_style(context, "focus-padding", &focus_pad,
@@ -166,10 +166,15 @@
             focus_rect.Offset(child_displacement_x, child_displacement_y);
         }
       }
+#endif
 
       if (!GtkVersionCheck(3, 20)) {
         GtkBorder border;
+#if GTK_CHECK_VERSION(3, 90, 0)
+        gtk_style_context_get_border(context, &border);
+#else
         gtk_style_context_get_border(context, state_flags, &border);
+#endif
         focus_rect.Inset(border.left, border.top, border.right, border.bottom);
       }
 
@@ -216,17 +221,23 @@
 };
 struct GtkIconInfoDeleter {
   void operator()(GtkIconInfo* ptr) {
+#if GTK_CHECK_VERSION(3, 90, 0)
+    g_object_unref(ptr);
+#else
     G_GNUC_BEGIN_IGNORE_DEPRECATIONS
     gtk_icon_info_free(ptr);
     G_GNUC_END_IGNORE_DEPRECATIONS
+#endif
   }
 };
 typedef std::unique_ptr<GIcon, GObjectDeleter> ScopedGIcon;
 typedef std::unique_ptr<GtkIconInfo, GtkIconInfoDeleter> ScopedGtkIconInfo;
 typedef std::unique_ptr<GdkPixbuf, GObjectDeleter> ScopedGdkPixbuf;
 
+#if !GTK_CHECK_VERSION(3, 90, 0)
 // Prefix for app indicator ids
 const char kAppIndicatorIdPrefix[] = "chrome_app_indicator_";
+#endif
 
 // Number of app indicators used (used as part of app-indicator id).
 int indicators_count;
@@ -574,12 +585,26 @@
 }
 
 bool GtkUi::IsStatusIconSupported() const {
+#if GTK_CHECK_VERSION(3, 90, 0)
+  // TODO(thomasanderson): Provide some sort of status icon for GTK4.  The GTK3
+  // config has two options.  The first is to use GTK status icons, but these
+  // were removed in GTK4.  The second is to use libappindicator.  However, that
+  // library has a dependency on GTK3, and loading multiple versions of GTK into
+  // the same process is explicitly unsupported.
+  NOTIMPLEMENTED();
+  return false;
+#else
   return true;
+#endif
 }
 
 std::unique_ptr<views::StatusIconLinux> GtkUi::CreateLinuxStatusIcon(
     const gfx::ImageSkia& image,
     const base::string16& tool_tip) const {
+#if GTK_CHECK_VERSION(3, 90, 0)
+  NOTIMPLEMENTED();
+  return nullptr;
+#else
   if (AppIndicatorIcon::CouldOpen()) {
     ++indicators_count;
     return std::unique_ptr<views::StatusIconLinux>(new AppIndicatorIcon(
@@ -589,6 +614,7 @@
     return std::unique_ptr<views::StatusIconLinux>(
         new GtkStatusIcon(image, tool_tip));
   }
+#endif
 }
 
 gfx::Image GtkUi::GetIconForContentType(const std::string& content_type,
diff --git a/chrome/browser/ui/libgtkui/gtk_util.cc b/chrome/browser/ui/libgtkui/gtk_util.cc
index a96bab06..eb9b1ef 100644
--- a/chrome/browser/ui/libgtkui/gtk_util.cc
+++ b/chrome/browser/ui/libgtkui/gtk_util.cc
@@ -33,8 +33,10 @@
 
 const char kAuraTransientParent[] = "aura-transient-parent";
 
-void CommonInitFromCommandLine(const base::CommandLine& command_line,
-                               void (*init_func)(gint*, gchar***)) {
+void CommonInitFromCommandLine(const base::CommandLine& command_line) {
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_init();
+#else
   const std::vector<std::string>& args = command_line.argv();
   int argc = args.size();
   std::unique_ptr<char* []> argv(new char*[argc + 1]);
@@ -49,11 +51,12 @@
   {
     // http://crbug.com/423873
     ANNOTATE_SCOPED_MEMORY_LEAK;
-    init_func(&argc, &argv_pointer);
+    gtk_init(&argc, &argv_pointer);
   }
   for (size_t i = 0; i < args.size(); ++i) {
     free(argv[i]);
   }
+#endif
 }
 
 }  // namespace
@@ -115,7 +118,7 @@
 }
 
 void GtkInitFromCommandLine(const base::CommandLine& command_line) {
-  CommonInitFromCommandLine(command_line, gtk_init);
+  CommonInitFromCommandLine(command_line);
 }
 
 // TODO(erg): This method was copied out of shell_integration_linux.cc. Because
@@ -494,8 +497,12 @@
 
 SkColor GetFgColorFromStyleContext(GtkStyleContext* context) {
   GdkRGBA color;
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_style_context_get_color(context, &color);
+#else
   gtk_style_context_get_color(context, gtk_style_context_get_state(context),
                               &color);
+#endif
   return GdkRgbaToSkColor(color);
 }
 
@@ -524,9 +531,13 @@
 
 ScopedCssProvider GetCssProvider(const std::string& css) {
   GtkCssProvider* provider = gtk_css_provider_new();
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_css_provider_load_from_data(provider, css.c_str(), -1);
+#else
   GError* error = nullptr;
   gtk_css_provider_load_from_data(provider, css.c_str(), -1, &error);
   DCHECK(!error);
+#endif
   return ScopedCssProvider(provider);
 }
 
@@ -574,8 +585,12 @@
   // This is verbatim how Gtk gets the selection color on versions before 3.20.
   GdkRGBA selection_color;
   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_style_context_get_background_color(context, &selection_color);
+#else
   gtk_style_context_get_background_color(
       context, gtk_style_context_get_state(context), &selection_color);
+#endif
   G_GNUC_END_IGNORE_DEPRECATIONS;
   return GdkRgbaToSkColor(selection_color);
 }
@@ -592,12 +607,18 @@
 
   auto context = GetStyleContextFromCss(css_selector);
   int w = 1, h = 1;
+  GtkBorder border, padding;
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_style_context_get(context, "min-width", &w, "min-height", &h, nullptr);
+  gtk_style_context_get_border(context, &border);
+  gtk_style_context_get_padding(context, &padding);
+#else
   gtk_style_context_get(context, gtk_style_context_get_state(context),
                         "min-width", &w, "min-height", &h, nullptr);
-  GtkBorder border, padding;
   GtkStateFlags state = gtk_style_context_get_state(context);
   gtk_style_context_get_border(context, state, &border);
   gtk_style_context_get_padding(context, state, &padding);
+#endif
   w += border.left + padding.left + padding.right + border.right;
   h += border.top + padding.top + padding.bottom + border.bottom;
 
diff --git a/chrome/browser/ui/libgtkui/menu_util.cc b/chrome/browser/ui/libgtkui/menu_util.cc
index 5ce8aaf..16f3fc22 100644
--- a/chrome/browser/ui/libgtkui/menu_util.cc
+++ b/chrome/browser/ui/libgtkui/menu_util.cc
@@ -12,14 +12,19 @@
 #include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
 #include "ui/base/models/menu_model.h"
 
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-
 namespace libgtkui {
 
 GtkWidget* BuildMenuItemWithImage(const std::string& label, GtkWidget* image) {
+// GTK4 removed support for image menu items.
+#if GTK_CHECK_VERSION(3, 90, 0)
+  return gtk_menu_item_new_with_mnemonic(label.c_str());
+#else
+  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
   GtkWidget* menu_item = gtk_image_menu_item_new_with_mnemonic(label.c_str());
   gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
+  G_GNUC_END_IGNORE_DEPRECATIONS;
   return menu_item;
+#endif
 }
 
 GtkWidget* BuildMenuItemWithImage(const std::string& label,
@@ -140,10 +145,14 @@
           menu_item = BuildMenuItemWithImage(label, icon);
         else
           menu_item = BuildMenuItemWithLabel(label);
+#if !GTK_CHECK_VERSION(3, 90, 0)
+        G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
         if (GTK_IS_IMAGE_MENU_ITEM(menu_item)) {
           gtk_image_menu_item_set_always_show_image(
               GTK_IMAGE_MENU_ITEM(menu_item), TRUE);
         }
+        G_GNUC_END_IGNORE_DEPRECATIONS;
+#endif
         break;
       }
 
@@ -233,17 +242,21 @@
             base::UTF16ToUTF8(model->GetLabelAt(id)));
 
         gtk_menu_item_set_label(GTK_MENU_ITEM(widget), label.c_str());
+#if !GTK_CHECK_VERSION(3, 90, 0)
+        G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
         if (GTK_IS_IMAGE_MENU_ITEM(widget)) {
           gfx::Image icon;
           if (model->GetIconAt(id, &icon)) {
             GdkPixbuf* pixbuf = GdkPixbufFromSkBitmap(*icon.ToSkBitmap());
             gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget),
-                gtk_image_new_from_pixbuf(pixbuf));
+                                          gtk_image_new_from_pixbuf(pixbuf));
             g_object_unref(pixbuf);
           } else {
             gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), nullptr);
           }
         }
+        G_GNUC_END_IGNORE_DEPRECATIONS;
+#endif
       }
 
       gtk_widget_show(widget);
diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk.cc b/chrome/browser/ui/libgtkui/native_theme_gtk.cc
index 8b9bd47b..08621907 100644
--- a/chrome/browser/ui/libgtkui/native_theme_gtk.cc
+++ b/chrome/browser/ui/libgtkui/native_theme_gtk.cc
@@ -6,7 +6,6 @@
 
 #include <gtk/gtk.h>
 
-#include "chrome/browser/ui/libgtkui/chrome_gtk_frame.h"
 #include "chrome/browser/ui/libgtkui/chrome_gtk_menu_subclasses.h"
 #include "chrome/browser/ui/libgtkui/gtk_util.h"
 #include "chrome/browser/ui/libgtkui/skia_utils_gtk.h"
@@ -159,9 +158,9 @@
         return GetFgColor("GtkLabel.link:link:hover:active");
       FALLTHROUGH;
     case ui::NativeTheme::kColorId_LinkEnabled: {
-      if (GtkVersionCheck(3, 12)) {
+      if (GtkVersionCheck(3, 12))
         return GetFgColor("GtkLabel.link:link");
-      }
+#if !GTK_CHECK_VERSION(3, 90, 0)
       auto link_context = GetStyleContextFromCss("GtkLabel.view");
       GdkColor* color;
       gtk_style_context_get_style(link_context, "link-color", &color, nullptr);
@@ -175,6 +174,7 @@
         G_GNUC_END_IGNORE_DEPRECATIONS;
         return ret_color;
       }
+#endif
 
       // Default color comes from gtklinkbutton.c.
       return SkColorSetRGB(0x00, 0x00, 0xEE);
@@ -606,12 +606,19 @@
     auto context = GetStyleContextFromCss(
         "GtkMenu#menu GtkSeparator#separator.horizontal");
     GtkBorder margin, border, padding;
+    int min_height = 1;
+#if GTK_CHECK_VERSION(3, 90, 0)
+    gtk_style_context_get_margin(context, &margin);
+    gtk_style_context_get_border(context, &border);
+    gtk_style_context_get_padding(context, &padding);
+    gtk_style_context_get(context, "min-height", &min_height, nullptr);
+#else
     GtkStateFlags state = gtk_style_context_get_state(context);
     gtk_style_context_get_margin(context, state, &margin);
     gtk_style_context_get_border(context, state, &border);
     gtk_style_context_get_padding(context, state, &padding);
-    int min_height = 1;
     gtk_style_context_get(context, state, "min-height", &min_height, nullptr);
+#endif
     int w = rect.width() - margin.left - margin.right;
     int h = std::max(
         min_height + padding.top + padding.bottom + border.top + border.bottom,
@@ -620,6 +627,7 @@
     int y = separator_offset(h);
     PaintWidget(canvas, gfx::Rect(x, y, w, h), context, BG_RENDER_NORMAL, true);
   } else {
+#if !GTK_CHECK_VERSION(3, 90, 0)
     auto context = GetStyleContextFromCss(
         "GtkMenu#menu GtkMenuItem#menuitem.separator.horizontal");
     gboolean wide_separators = false;
@@ -644,6 +652,7 @@
       flags.setStrokeWidth(1);
       canvas->drawLine(x + 0.5f, y + 0.5f, x + w + 0.5f, y + 0.5f, flags);
     }
+#endif
   }
 }
 
diff --git a/chrome/browser/ui/libgtkui/nav_button_provider_gtk.cc b/chrome/browser/ui/libgtkui/nav_button_provider_gtk.cc
index 46d4694b..0491ef1 100644
--- a/chrome/browser/ui/libgtkui/nav_button_provider_gtk.cc
+++ b/chrome/browser/ui/libgtkui/nav_button_provider_gtk.cc
@@ -79,21 +79,33 @@
 gfx::Insets PaddingFromStyleContext(GtkStyleContext* context,
                                     GtkStateFlags state) {
   GtkBorder padding;
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_style_context_get_padding(context, &padding);
+#else
   gtk_style_context_get_padding(context, state, &padding);
+#endif
   return InsetsFromGtkBorder(padding);
 }
 
 gfx::Insets BorderFromStyleContext(GtkStyleContext* context,
                                    GtkStateFlags state) {
   GtkBorder border;
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_style_context_get_border(context, &border);
+#else
   gtk_style_context_get_border(context, state, &border);
+#endif
   return InsetsFromGtkBorder(border);
 }
 
 gfx::Insets MarginFromStyleContext(GtkStyleContext* context,
                                    GtkStateFlags state) {
   GtkBorder margin;
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_style_context_get_margin(context, &margin);
+#else
   gtk_style_context_get_margin(context, state, &margin);
+#endif
   return InsetsFromGtkBorder(margin);
 }
 
@@ -118,8 +130,13 @@
     widget_rect.Inset(-MarginFromStyleContext(content_context, state));
   if (GtkVersionCheck(3, 20)) {
     int min_width, min_height;
+#if GTK_CHECK_VERSION(3, 90, 0)
+    gtk_style_context_get(widget_context, "min-width", &min_width, "min-height",
+                          &min_height, nullptr);
+#else
     gtk_style_context_get(widget_context, state, "min-width", &min_width,
-                          "min-height", &min_height, NULL);
+                          "min-height", &min_height, nullptr);
+#endif
     widget_rect.set_width(std::max(widget_rect.width(), min_width));
     widget_rect.set_height(std::max(widget_rect.height(), min_height));
   }
@@ -220,9 +237,14 @@
     // "contain" if clipping would occur.
     cairo_pattern_t* cr_pattern = nullptr;
     cairo_surface_t* cr_surface = nullptr;
+#if GTK_CHECK_VERSION(3, 90, 0)
+    gtk_style_context_get(button_context, GTK_STYLE_PROPERTY_BACKGROUND_IMAGE,
+                          &cr_pattern, nullptr);
+#else
     gtk_style_context_get(button_context, button_state,
                           GTK_STYLE_PROPERTY_BACKGROUND_IMAGE, &cr_pattern,
                           nullptr);
+#endif
     if (cr_pattern &&
         cairo_pattern_get_surface(cr_pattern, &cr_surface) ==
             CAIRO_STATUS_SUCCESS &&
@@ -299,8 +321,12 @@
   auto header_context = CreateHeaderContext(maximized);
 
   GtkBorder header_padding;
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_style_context_get_padding(header_context, &header_padding);
+#else
   gtk_style_context_get_padding(header_context, GTK_STATE_FLAG_NORMAL,
                                 &header_padding);
+#endif
 
   double scale = 1.0f;
   std::map<chrome::FrameButtonDisplayType, gfx::Size> button_sizes;
@@ -418,8 +444,13 @@
   float content_height = content_size.height();
   if (GtkVersionCheck(3, 20)) {
     int min_width, min_height;
+#if GTK_CHECK_VERSION(3, 90, 0)
+    gtk_style_context_get(button_context, "min-width", &min_width, "min-height",
+                          &min_height, nullptr);
+#else
     gtk_style_context_get(button_context, GTK_STATE_FLAG_NORMAL, "min-width",
-                          &min_width, "min-height", &min_height, NULL);
+                          &min_width, "min-height", &min_height, nullptr);
+#endif
     content_width = std::max(content_width, static_cast<float>(min_width));
     content_height = std::max(content_height, static_cast<float>(min_height));
   }
diff --git a/chrome/browser/ui/libgtkui/print_dialog_gtk.cc b/chrome/browser/ui/libgtkui/print_dialog_gtk.cc
index a569325..0105f34 100644
--- a/chrome/browser/ui/libgtkui/print_dialog_gtk.cc
+++ b/chrome/browser/ui/libgtkui/print_dialog_gtk.cc
@@ -301,14 +301,8 @@
           gtk_page_setup_set_paper_size(page_setup_, custom_size);
           gtk_paper_size_free(custom_size);
         }
-#if GTK_CHECK_VERSION(2, 28, 0)
         g_list_free_full(gtk_paper_sizes,
                          reinterpret_cast<GDestroyNotify>(gtk_paper_size_free));
-#else
-        g_list_foreach(gtk_paper_sizes,
-                       reinterpret_cast<GFunc>(gtk_paper_size_free), nullptr);
-        g_list_free(gtk_paper_sizes);
-#endif
       }
     } else {
       VLOG(1) << "Using default paper size";
diff --git a/chrome/browser/ui/libgtkui/select_file_dialog_impl_gtk.cc b/chrome/browser/ui/libgtkui/select_file_dialog_impl_gtk.cc
index 031f314..9a587a8 100644
--- a/chrome/browser/ui/libgtkui/select_file_dialog_impl_gtk.cc
+++ b/chrome/browser/ui/libgtkui/select_file_dialog_impl_gtk.cc
@@ -34,6 +34,23 @@
 
 namespace {
 
+#if GTK_CHECK_VERSION(3, 90, 0)
+// GTK stock items have been deprecated.  The docs say to switch to using the
+// strings "_Open", etc.  However this breaks i18n.  We could supply our own
+// internationalized strings, but the "_" in these strings is significant: it's
+// the keyboard shortcut to select these actions.  TODO(thomasanderson): Provide
+// internationalized strings when GTK provides support for it.
+const char kCancelLabel[] = "_Cancel";
+const char kOpenLabel[] = "_Open";
+const char kSaveLabel[] = "_Save";
+#else
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+const char* kCancelLabel = GTK_STOCK_CANCEL;
+const char* kOpenLabel = GTK_STOCK_OPEN;
+const char* kSaveLabel = GTK_STOCK_SAVE;
+G_GNUC_END_IGNORE_DEPRECATIONS;
+#endif
+
 // Makes sure that .jpg also shows .JPG.
 gboolean FileFilterCaseInsensitive(const GtkFileFilterInfo* file_info,
                                    std::string* file_extension) {
@@ -188,7 +205,9 @@
     }
   }
 
+#if !GTK_CHECK_VERSION(3, 90, 0)
   gtk_widget_show_all(dialog);
+#endif
 
   // We need to call gtk_window_present after making the widgets visible to make
   // sure window gets correctly raised and gets focus.
@@ -296,11 +315,9 @@
     const std::string& title,
     const base::FilePath& default_path,
     gfx::NativeWindow parent) {
-  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
   GtkWidget* dialog = gtk_file_chooser_dialog_new(
-      title.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
-      GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, nullptr);
-  G_GNUC_END_IGNORE_DEPRECATIONS;
+      title.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_OPEN, kCancelLabel,
+      GTK_RESPONSE_CANCEL, kOpenLabel, GTK_RESPONSE_ACCEPT, nullptr);
   SetGtkTransientForAura(dialog, parent);
   AddFilters(GTK_FILE_CHOOSER(dialog));
 
@@ -333,18 +350,16 @@
             ? l10n_util::GetStringUTF8(IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE)
             : l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE);
   }
-  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
   std::string accept_button_label =
       (type == SELECT_UPLOAD_FOLDER)
           ? l10n_util::GetStringUTF8(
                 IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON)
-          : GTK_STOCK_OPEN;
+          : kOpenLabel;
 
   GtkWidget* dialog = gtk_file_chooser_dialog_new(
       title_string.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
-      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, accept_button_label.c_str(),
+      kCancelLabel, GTK_RESPONSE_CANCEL, accept_button_label.c_str(),
       GTK_RESPONSE_ACCEPT, nullptr);
-  G_GNUC_END_IGNORE_DEPRECATIONS;
   SetGtkTransientForAura(dialog, parent);
   GtkFileChooser* chooser = GTK_FILE_CHOOSER(dialog);
   if (type == SELECT_UPLOAD_FOLDER || type == SELECT_EXISTING_FOLDER)
@@ -407,12 +422,9 @@
       !title.empty() ? title
                      : l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE);
 
-  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
   GtkWidget* dialog = gtk_file_chooser_dialog_new(
-      title_string.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_SAVE,
-      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE,
-      GTK_RESPONSE_ACCEPT, nullptr);
-  G_GNUC_END_IGNORE_DEPRECATIONS;
+      title_string.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_SAVE, kCancelLabel,
+      GTK_RESPONSE_CANCEL, kSaveLabel, GTK_RESPONSE_ACCEPT, nullptr);
   SetGtkTransientForAura(dialog, parent);
 
   AddFilters(GTK_FILE_CHOOSER(dialog));
diff --git a/chrome/browser/ui/libgtkui/settings_provider_gtk.cc b/chrome/browser/ui/libgtkui/settings_provider_gtk.cc
index fc2754c5..28ba3fb8 100644
--- a/chrome/browser/ui/libgtkui/settings_provider_gtk.cc
+++ b/chrome/browser/ui/libgtkui/settings_provider_gtk.cc
@@ -26,6 +26,10 @@
 }
 
 std::string GetDecorationLayoutFromGtkWindow() {
+#if GTK_CHECK_VERSION(3, 90, 0)
+  NOTREACHED();
+  return kDefaultGtkLayout;
+#else
   static ScopedStyleContext context;
   if (!context) {
     context = GetStyleContextFromCss("");
@@ -39,6 +43,7 @@
   std::string layout(layout_c);
   g_free(layout_c);
   return layout;
+#endif
 }
 
 void ParseActionString(const std::string& value,
diff --git a/chrome/browser/ui/libgtkui/skia_utils_gtk.cc b/chrome/browser/ui/libgtkui/skia_utils_gtk.cc
index 150a41d..6daec57 100644
--- a/chrome/browser/ui/libgtkui/skia_utils_gtk.cc
+++ b/chrome/browser/ui/libgtkui/skia_utils_gtk.cc
@@ -17,6 +17,7 @@
 // To get back, we can just right shift by eight
 // (or, formulated differently, i == (i*257)/256 for all i < 256).
 
+#if !GTK_CHECK_VERSION(3, 90, 0)
 SkColor GdkColorToSkColor(GdkColor color) {
   return SkColorSetRGB(color.red >> 8, color.green >> 8, color.blue >> 8);
 }
@@ -28,6 +29,7 @@
       static_cast<guint16>(SkColorGetB(color) * kSkiaToGDKMultiplier)};
   return gdk_color;
 }
+#endif
 
 const SkBitmap GdkPixbufToImageSkia(GdkPixbuf* pixbuf) {
   // TODO(erg): What do we do in the case where the pixbuf fails these dchecks?
diff --git a/chrome/browser/ui/libgtkui/skia_utils_gtk.h b/chrome/browser/ui/libgtkui/skia_utils_gtk.h
index e05fbe9d..bd6ed68a 100644
--- a/chrome/browser/ui/libgtkui/skia_utils_gtk.h
+++ b/chrome/browser/ui/libgtkui/skia_utils_gtk.h
@@ -5,15 +5,19 @@
 #ifndef CHROME_BROWSER_UI_LIBGTKUI_SKIA_UTILS_GTK_H_
 #define CHROME_BROWSER_UI_LIBGTKUI_SKIA_UTILS_GTK_H_
 
+#include <gtk/gtk.h>
 #include <stdint.h>
 
 #include "third_party/skia/include/core/SkColor.h"
 
+#if !GTK_CHECK_VERSION(3, 90, 0)
 typedef struct _GdkColor GdkColor;
+#endif
 typedef struct _GdkPixbuf GdkPixbuf;
 
 class SkBitmap;
 
+#if !GTK_CHECK_VERSION(3, 90, 0)
 // Define a macro for creating GdkColors from RGB values.  This is a macro to
 // allow static construction of literals, etc.  Use this like:
 //   GdkColor white = GDK_COLOR_RGB(0xff, 0xff, 0xff);
@@ -24,9 +28,11 @@
     g * ::libgtkui::kSkiaToGDKMultiplier,    \
     b * ::libgtkui::kSkiaToGDKMultiplier,    \
   }
+#endif
 
 namespace libgtkui {
 
+#if !GTK_CHECK_VERSION(3, 90, 0)
 // Multiply uint8_t color components by this.
 const int kSkiaToGDKMultiplier = 257;
 
@@ -35,6 +41,7 @@
 
 // Converts ARGB to GdkColor.
 GdkColor SkColorToGdkColor(SkColor color);
+#endif
 
 const SkBitmap GdkPixbufToImageSkia(GdkPixbuf* pixbuf);
 
diff --git a/chrome/browser/ui/libgtkui/x11_input_method_context_impl_gtk.cc b/chrome/browser/ui/libgtkui/x11_input_method_context_impl_gtk.cc
index 66e2367..e27284a 100644
--- a/chrome/browser/ui/libgtkui/x11_input_method_context_impl_gtk.cc
+++ b/chrome/browser/ui/libgtkui/x11_input_method_context_impl_gtk.cc
@@ -71,19 +71,28 @@
   }
 
   if (event->key.window != gdk_last_set_client_window_) {
+#if GTK_CHECK_VERSION(3, 90, 0)
+    gtk_im_context_set_client_widget(gtk_context_,
+                                     GTK_WIDGET(event->key.window));
+#else
     gtk_im_context_set_client_window(gtk_context_, event->key.window);
+#endif
     gdk_last_set_client_window_ = event->key.window;
   }
 
   // Convert the last known caret bounds relative to the screen coordinates
   // to a GdkRectangle relative to the client window.
-  gint x = 0;
-  gint y = 0;
-  gdk_window_get_origin(event->key.window, &x, &y);
+  gint win_x = 0;
+  gint win_y = 0;
+  gdk_window_get_origin(event->key.window, &win_x, &win_y);
 
-  GdkRectangle gdk_rect = {
-      last_caret_bounds_.x() - x, last_caret_bounds_.y() - y,
-      last_caret_bounds_.width(), last_caret_bounds_.height()};
+  gint factor = gdk_window_get_scale_factor(event->key.window);
+  gint caret_x = last_caret_bounds_.x() / factor;
+  gint caret_y = last_caret_bounds_.y() / factor;
+  gint caret_w = last_caret_bounds_.width() / factor;
+  gint caret_h = last_caret_bounds_.height() / factor;
+
+  GdkRectangle gdk_rect = {caret_x - win_x, caret_y - win_y, caret_w, caret_h};
   gtk_im_context_set_cursor_location(gtk_context_, &gdk_rect);
 
   const bool handled =
@@ -209,19 +218,11 @@
   g_free(keyvals);
   keyvals = nullptr;
 // Get a GdkWindow.
-#if GTK_CHECK_VERSION(2, 24, 0)
   GdkWindow* window = gdk_x11_window_lookup_for_display(display, xkey.window);
-#else
-  GdkWindow* window = gdk_window_lookup_for_display(display, xkey.window);
-#endif
   if (window)
     g_object_ref(window);
   else
-#if GTK_CHECK_VERSION(2, 24, 0)
     window = gdk_x11_window_foreign_new_for_display(display, xkey.window);
-#else
-    window = gdk_window_foreign_new_for_display(display, xkey.window);
-#endif
   if (!window) {
     LOG(ERROR) << "Cannot get a GdkWindow for a key event.";
     return nullptr;
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
index 59f56d7..1aeae41 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
@@ -30,7 +30,7 @@
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
 #include "chrome/browser/ui/views/exclusive_access_bubble_views.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/mus/property_converter.h"
 #include "ui/aura/mus/window_tree_host_mus.h"
diff --git a/chrome/browser/ui/views/apps/shaped_app_window_targeter_unittest.cc b/chrome/browser/ui/views/apps/shaped_app_window_targeter_unittest.cc
index 10c06e0..5ba1bd0 100644
--- a/chrome/browser/ui/views/apps/shaped_app_window_targeter_unittest.cc
+++ b/chrome/browser/ui/views/apps/shaped_app_window_targeter_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/macros.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/aura_test_base.h"
 #include "ui/aura/window.h"
diff --git a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc
index 0f7ce9f..dcbb4b6 100644
--- a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc
+++ b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc
@@ -31,7 +31,7 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/runner/common/client_util.h"
 #include "services/ui/public/cpp/gpu/gpu.h"  // nogncheck
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/aura/env.h"
 #include "ui/display/screen.h"
 #include "ui/views/mus/mus_client.h"
diff --git a/chrome/browser/ui/views/frame/browser_frame_mash.cc b/chrome/browser/ui/views/frame/browser_frame_mash.cc
index 9bc01ee..e46b0a1be 100644
--- a/chrome/browser/ui/views/frame/browser_frame_mash.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_mash.cc
@@ -20,8 +20,8 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/mus/window_tree_host_mus_init_params.h"
 #include "ui/base/ui_base_features.h"
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index ffbc911..653313d4 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -8,14 +8,14 @@
 
 #include "ash/frame/caption_buttons/frame_back_button.h"  // mash-ok
 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"  // mash-ok
-#include "ash/frame/default_frame_header.h"      // mash-ok
-#include "ash/frame/frame_header_util.h"         // mash-ok
+#include "ash/frame/default_frame_header.h"  // mash-ok
+#include "ash/frame/frame_header_util.h"     // mash-ok
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/app_types.h"
 #include "ash/public/cpp/ash_constants.h"
 #include "ash/public/cpp/ash_layout_constants.h"
 #include "ash/public/cpp/ash_switches.h"
-#include "ash/public/cpp/frame_border_hit_test.h"
+#include "ash/public/cpp/frame_utils.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/public/interfaces/constants.mojom.h"
 #include "ash/public/interfaces/window_state_type.mojom.h"
@@ -55,6 +55,7 @@
 #include "ui/base/ui_base_features.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/scoped_canvas.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/mus/desktop_window_tree_host_mus.h"
@@ -397,7 +398,7 @@
       // clicks in the frame decoration.
       static_cast<aura::WindowTreeHostMus*>(
           reveal_widget->GetNativeWindow()->GetHost())
-          ->SetClientArea(client_area_insets, additional_client_area);
+          ->SetClientArea(client_area_insets, {});
     }
   } else {
     window_tree_host_mus->SetClientArea(gfx::Insets(), additional_client_area);
@@ -508,12 +509,24 @@
   if (!ShouldPaint())
     return;
 
+  ImmersiveModeController* immersive_mode_controller =
+      browser_view()->immersive_mode_controller();
+
   if (frame_header_) {
     DCHECK(!IsMash());
     const ash::FrameHeader::Mode header_mode =
         ShouldPaintAsActive() ? ash::FrameHeader::MODE_ACTIVE
                               : ash::FrameHeader::MODE_INACTIVE;
     frame_header_->PaintHeader(canvas, header_mode);
+  } else if (IsMash() && immersive_mode_controller->IsEnabled() &&
+             immersive_mode_controller->IsRevealed()) {
+    // TODO(estade): GetTopInset() ignores its parameter in Mash. Fix for this
+    // case, where we truly want to force |restored| to true.
+    ash::PaintThemedFrame(canvas, GetFrameImage(kActive),
+                          GetFrameOverlayImage(kActive), GetFrameColor(kActive),
+                          gfx::Rect(width(), GetTopInset(false)),
+                          GetThemeBackgroundXInset(),
+                          GetFrameHeaderImageYInset(), SK_AlphaOPAQUE);
   }
 
   if (browser_view()->IsToolbarVisible() &&
@@ -534,7 +547,7 @@
               ash::kCaptionButtonBoundsKey);
       if (inverted_caption_button_bounds) {
         hosted_app_button_container_->LayoutInContainer(
-            0, inverted_caption_button_bounds->x() + width(),
+            0, inverted_caption_button_bounds->x() + width(), 0,
             inverted_caption_button_bounds->height());
       }
     }
@@ -563,7 +576,7 @@
     LayoutIncognitoButton();
   if (hosted_app_button_container_) {
     hosted_app_button_container_->LayoutInContainer(
-        0, caption_button_container_->x(), painted_height);
+        0, caption_button_container_->x(), 0, painted_height);
   }
 
   BrowserNonClientFrameView::Layout();
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index 8fcfaa3..47011d34 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -72,7 +72,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "net/dns/mock_host_resolver.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/env_test_helper.h"
 #include "ui/base/class_property.h"
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
index cdd1f57..7ce1d48f7 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -600,7 +600,18 @@
 }
 
 int GlassBrowserFrameView::TitlebarMaximizedVisualHeight() const {
-  return display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYCAPTION);
+  int maximized_height =
+      display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYCAPTION);
+  if (hosted_app_button_container_) {
+    // Adding 2px of vertical padding puts at least 1 px of space on the top and
+    // bottom of the element.
+    constexpr int kVerticalPadding = 2;
+    maximized_height =
+        std::max(maximized_height,
+                 hosted_app_button_container_->GetPreferredSize().height() +
+                     kVerticalPadding);
+  }
+  return maximized_height;
 }
 
 int GlassBrowserFrameView::TitlebarHeight(bool restored) const {
@@ -710,11 +721,6 @@
   return GetFrameColor();
 }
 
-HostedAppButtonContainer*
-GlassBrowserFrameView::GetHostedAppButtonContainerForTesting() const {
-  return hosted_app_button_container_;
-}
-
 Windows10CaptionButton* GlassBrowserFrameView::CreateCaptionButton(
     ViewID button_type,
     int accessible_name_resource_id) {
@@ -916,16 +922,16 @@
   gfx::Rect window_icon_bounds;
   const int icon_size =
       display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYSMICON);
-  constexpr int kIconMaximizedLeftMargin = 2;
   const int titlebar_visual_height =
       IsMaximized() ? TitlebarMaximizedVisualHeight() : TitlebarHeight(false);
   // Don't include the area above the screen when maximized. However it only
   // looks centered if we start from y=0 when restored.
   const int window_top = IsMaximized() ? WindowTopY() : 0;
   int next_leading_x =
-      IsMaximized()
-          ? kIconMaximizedLeftMargin
-          : display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXSIZEFRAME);
+      display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXSIZEFRAME);
+  constexpr int kMaximizedLeftMargin = 2;
+  if (IsMaximized())
+    next_leading_x += kMaximizedLeftMargin;
   int next_trailing_x = MinimizeButtonX();
 
   const int y = window_top + (titlebar_visual_height - icon_size) / 2;
@@ -943,7 +949,7 @@
   if (hosted_app_button_container_) {
     DCHECK(!GetProfileSwitcherButton());
     next_trailing_x = hosted_app_button_container_->LayoutInContainer(
-        next_leading_x, next_trailing_x, titlebar_visual_height);
+        next_leading_x, next_trailing_x, window_top, titlebar_visual_height);
   }
 
   if (ShowCustomTitle()) {
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.h b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
index 1f6d24a..43c993b 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
@@ -74,7 +74,11 @@
 
   SkColor GetTitlebarColor() const;
 
-  HostedAppButtonContainer* GetHostedAppButtonContainerForTesting() const;
+  HostedAppButtonContainer* hosted_app_button_container_for_testing() {
+    return hosted_app_button_container_;
+  }
+
+  views::Label* window_title_for_testing() { return window_title_; }
 
  protected:
   // views::View:
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view_browsertest_win.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view_browsertest_win.cc
index 7a8d36c9..a22da2a 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view_browsertest_win.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view_browsertest_win.cc
@@ -61,7 +61,7 @@
     glass_frame_view_ = static_cast<GlassBrowserFrameView*>(frame_view);
 
     hosted_app_button_container_ =
-        glass_frame_view_->GetHostedAppButtonContainerForTesting();
+        glass_frame_view_->hosted_app_button_container_for_testing();
     DCHECK(hosted_app_button_container_);
     DCHECK(hosted_app_button_container_->visible());
     return true;
@@ -136,3 +136,15 @@
   EXPECT_EQ(page_action_icon_container->width(), 0);
   EXPECT_EQ(menu_button->width(), original_menu_button_width);
 }
+
+IN_PROC_BROWSER_TEST_F(HostedAppGlassBrowserFrameViewTest, MaximizedLayout) {
+  if (!InstallAndLaunchHostedApp())
+    return;
+
+  glass_frame_view_->frame()->Maximize();
+  static_cast<views::View*>(glass_frame_view_)->Layout();
+
+  DCHECK_GT(glass_frame_view_->window_title_for_testing()->x(), 0);
+  DCHECK_GT(glass_frame_view_->hosted_app_button_container_for_testing()->y(),
+            0);
+}
diff --git a/chrome/browser/ui/views/frame/hosted_app_button_container.cc b/chrome/browser/ui/views/frame/hosted_app_button_container.cc
index 4fe537e..3344f361 100644
--- a/chrome/browser/ui/views/frame/hosted_app_button_container.cc
+++ b/chrome/browser/ui/views/frame/hosted_app_button_container.cc
@@ -268,6 +268,7 @@
 
 int HostedAppButtonContainer::LayoutInContainer(int leading_x,
                                                 int trailing_x,
+                                                int y,
                                                 int available_height) {
   if (available_height == 0) {
     SetSize(gfx::Size());
@@ -279,7 +280,8 @@
       std::min(preferred_size.width(), std::max(0, trailing_x - leading_x));
   const int height = preferred_size.height();
   DCHECK_LE(height, available_height);
-  SetBounds(trailing_x - width, (available_height - height) / 2, width, height);
+  SetBounds(trailing_x - width, y + (available_height - height) / 2, width,
+            height);
   Layout();
   return bounds().x();
 }
diff --git a/chrome/browser/ui/views/frame/hosted_app_button_container.h b/chrome/browser/ui/views/frame/hosted_app_button_container.h
index c970d2a..37a8e48 100644
--- a/chrome/browser/ui/views/frame/hosted_app_button_container.h
+++ b/chrome/browser/ui/views/frame/hosted_app_button_container.h
@@ -59,7 +59,10 @@
   // Sets the container to paints its buttons the active/inactive color.
   void SetPaintAsActive(bool active);
 
-  int LayoutInContainer(int leading_x, int trailing_x, int available_height);
+  int LayoutInContainer(int leading_x,
+                        int trailing_x,
+                        int y,
+                        int available_height);
 
  private:
   friend class HostedAppNonClientFrameViewAshTest;
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
index d42057c..25810e4 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
@@ -18,26 +18,61 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_contents.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/mus/mus_types.h"
 #include "ui/aura/mus/property_converter.h"
 #include "ui/aura/mus/window_port_mus.h"
+#include "ui/aura/mus/window_tree_host_mus.h"
 #include "ui/aura/window.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/paint_context.h"
 #include "ui/compositor/paint_recorder.h"
+#include "ui/events/event_rewriter.h"
 #include "ui/views/background.h"
 #include "ui/views/mus/mus_client.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/native_widget_aura.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/non_client_view.h"
-#include "ui/wm/core/window_util.h"
 
 namespace {
 
+// This class rewrites located events to have no target so the target will be
+// found via local process hit testing instead of the window service, which is
+// unaware of the browser's top container that is on top of the web contents. An
+// instance is active whenever the Mash reveal widget is active.
+class LocatedEventRetargeter : public ui::EventRewriter {
+ public:
+  LocatedEventRetargeter() {}
+  ~LocatedEventRetargeter() override {}
+
+  ui::EventRewriteStatus RewriteEvent(
+      const ui::Event& event,
+      std::unique_ptr<ui::Event>* rewritten_event) override {
+    if (!event.IsLocatedEvent())
+      return ui::EVENT_REWRITE_CONTINUE;
+
+    // Before being sent to the rewriters, the event is already cloned which
+    // strips its EventTarget. The only goal of this EventRewriter is to null
+    // the target, so there's no need to do anything extra here.
+    DCHECK(!event.target());
+    *rewritten_event = ui::Event::Clone(event);
+
+    return ui::EVENT_REWRITE_REWRITTEN;
+  }
+
+  ui::EventRewriteStatus NextDispatchEvent(
+      const ui::Event& last_event,
+      std::unique_ptr<ui::Event>* new_event) override {
+    return ui::EVENT_REWRITE_CONTINUE;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LocatedEventRetargeter);
+};
+
 // Converts from ImmersiveModeController::AnimateReveal to
 // ash::ImmersiveFullscreenController::AnimateReveal.
 ash::ImmersiveFullscreenController::AnimateReveal
@@ -64,46 +99,12 @@
   DISALLOW_COPY_AND_ASSIGN(ImmersiveRevealedLockAsh);
 };
 
-// View responsible for mirroring the content of the TopContainer. This is done
-// by way of mirroring the actual layers.
-class TopContainerMirrorView : public views::View {
- public:
-  explicit TopContainerMirrorView(views::View* view) : view_(view) {
-    DCHECK(view_->layer());
-    SetPaintToLayer();
-    layer()->SetFillsBoundsOpaquely(false);
-    // At this point we have no size. Wait for the first resize before we
-    // create the mirrored layer.
-  }
-  ~TopContainerMirrorView() override {}
-
-  // views::View:
-  void OnBoundsChanged(const gfx::Rect& previous_bounds) override {
-    if (mirrored_layer_tree_owner_ &&
-        mirrored_layer_tree_owner_->root()->size() == size()) {
-      return;
-    }
-
-    mirrored_layer_tree_owner_.reset();
-    DCHECK(view_->layer());  // SetPaintToLayer() should have been called.
-    mirrored_layer_tree_owner_ = wm::MirrorLayers(view_, false);
-    mirrored_layer_tree_owner_->root()->SetBounds(gfx::Rect(size()));
-    layer()->Add(mirrored_layer_tree_owner_->root());
-  }
-
- private:
-  views::View* view_;
-
-  std::unique_ptr<ui::LayerTreeOwner> mirrored_layer_tree_owner_;
-
-  DISALLOW_COPY_AND_ASSIGN(TopContainerMirrorView);
-};
-
 }  // namespace
 
 ImmersiveModeControllerAsh::ImmersiveModeControllerAsh()
     : ImmersiveModeController(Type::ASH),
-      controller_(new ash::ImmersiveFullscreenController) {}
+      controller_(new ash::ImmersiveFullscreenController),
+      event_rewriter_(std::make_unique<LocatedEventRetargeter>()) {}
 
 ImmersiveModeControllerAsh::~ImmersiveModeControllerAsh() = default;
 
@@ -220,28 +221,34 @@
       [ui::mojom::WindowManager::kWindowIgnoredByShelf_InitProperty] =
       mojo::ConvertTo<std::vector<uint8_t>>(true);
   init_params.name = "ChromeImmersiveRevealWindow";
-  // We want events to fall through to the real views.
-  init_params.accept_events = false;
   init_params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   init_params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
   init_params.parent = browser_view_->GetNativeWindow()->GetRootWindow();
   // The widget needs to be translucent so the frame decorations drawn by the
   // window manager are visible.
   init_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
-  const gfx::Rect& top_container_bounds =
-      browser_view_->top_container()->bounds();
-  init_params.bounds =
-      gfx::Rect(0, -top_container_bounds.height(), top_container_bounds.width(),
-                top_container_bounds.height());
+  init_params.bounds = GetScreenBoundsForRevealWidget();
   mash_reveal_widget_->Init(init_params);
-  mash_reveal_widget_->SetContentsView(
-      new TopContainerMirrorView(browser_view_->top_container()));
   mash_reveal_widget_->StackAtTop();
   mash_reveal_widget_->Show();
+
+  browser_view_->GetWidget()
+      ->GetNativeWindow()
+      ->GetHost()
+      ->GetEventSource()
+      ->AddEventRewriter(event_rewriter_.get());
 }
 
 void ImmersiveModeControllerAsh::DestroyMashRevealWidget() {
-  mash_reveal_widget_.reset();
+  if (mash_reveal_widget_) {
+    browser_view_->GetWidget()
+        ->GetNativeWindow()
+        ->GetHost()
+        ->GetEventSource()
+        ->RemoveEventRewriter(event_rewriter_.get());
+
+    mash_reveal_widget_.reset();
+  }
 }
 
 void ImmersiveModeControllerAsh::OnImmersiveRevealStarted() {
@@ -283,11 +290,8 @@
   browser_view_->Layout();
   browser_view_->frame()->GetFrameView()->UpdateClientArea();
 
-  if (mash_reveal_widget_) {
-    gfx::Rect bounds = mash_reveal_widget_->GetNativeWindow()->bounds();
-    bounds.set_y(visible_fraction * bounds.height() - bounds.height());
-    mash_reveal_widget_->SetBounds(bounds);
-  }
+  if (mash_reveal_widget_)
+    mash_reveal_widget_->SetBounds(GetScreenBoundsForRevealWidget());
 }
 
 std::vector<gfx::Rect>
@@ -351,3 +355,17 @@
   observed_windows_.Remove(window);
   DCHECK(!observed_windows_.IsObservingSources());
 }
+
+gfx::Rect ImmersiveModeControllerAsh::GetScreenBoundsForRevealWidget() {
+  const gfx::Rect* inverted_caption_button_bounds =
+      browser_view_->GetWidget()
+          ->GetNativeWindow()
+          ->GetRootWindow()
+          ->GetProperty(ash::kCaptionButtonBoundsKey);
+  if (!inverted_caption_button_bounds)
+    return gfx::Rect();
+  gfx::Rect top_container_bounds =
+      browser_view_->top_container()->GetBoundsInScreen();
+  return *inverted_caption_button_bounds +
+         gfx::Vector2d(top_container_bounds.right(), top_container_bounds.y());
+}
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h
index 7cc5bd25..b1ccf46 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h
@@ -21,6 +21,10 @@
 class Window;
 }
 
+namespace ui {
+class EventRewriter;
+}
+
 // See ash/mus/frame/README.md for description of how immersive mode works in
 // mash. This code works with both classic ash, and mash.
 class ImmersiveModeControllerAsh
@@ -80,6 +84,8 @@
                                intptr_t old) override;
   void OnWindowDestroying(aura::Window* window) override;
 
+  gfx::Rect GetScreenBoundsForRevealWidget();
+
   std::unique_ptr<ash::ImmersiveFullscreenController> controller_;
 
   BrowserView* browser_view_ = nullptr;
@@ -92,10 +98,13 @@
   // the top-of-window views are not revealed.
   double visible_fraction_ = 1.0;
 
-  // When running in mash a widget is created to draw the top container. This
-  // widget does not actually contain the top container, it just renders it.
+  // When running in mash a widget is created to draw window controls on top of
+  // the browser's |top_container|.
   std::unique_ptr<views::Widget> mash_reveal_widget_;
 
+  // See comment above LocatedEventRetargeter.
+  std::unique_ptr<ui::EventRewriter> event_rewriter_;
+
   content::NotificationRegistrar registrar_;
 
   ScopedObserver<aura::Window, aura::WindowObserver> observed_windows_{this};
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc
index fe6bcd3..3cbfddf 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc
@@ -421,7 +421,7 @@
     if (hosted_app_button_container_) {
       available_space_trailing_x_ =
           hosted_app_button_container_->LayoutInContainer(
-              available_space_leading_x_, available_space_trailing_x_,
+              available_space_leading_x_, available_space_trailing_x_, y,
               available_height);
     }
   }
diff --git a/chrome/browser/ui/views/ime_driver/ime_driver_mus.cc b/chrome/browser/ui/views/ime_driver/ime_driver_mus.cc
index 4de2137..d76cb5b2 100644
--- a/chrome/browser/ui/views/ime_driver/ime_driver_mus.cc
+++ b/chrome/browser/ui/views/ime_driver/ime_driver_mus.cc
@@ -12,8 +12,8 @@
 #include "content/public/common/service_manager_connection.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 #include "ui/base/ime/ime_bridge.h"
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/views/ime_driver/ime_driver_mus.h b/chrome/browser/ui/views/ime_driver/ime_driver_mus.h
index 9ac2c0a..e7ecb6f3 100644
--- a/chrome/browser/ui/views/ime_driver/ime_driver_mus.h
+++ b/chrome/browser/ui/views/ime_driver/ime_driver_mus.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_UI_VIEWS_IME_DRIVER_IME_DRIVER_MUS_H_
 
 #include "base/macros.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 
 // Creates an InputMethodBridge when an IME session is started via mojo.
 class IMEDriver : public ui::mojom::IMEDriver {
diff --git a/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos.h b/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos.h
index 90610b6..5804b38 100644
--- a/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos.h
+++ b/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_UI_VIEWS_IME_DRIVER_INPUT_METHOD_BRIDGE_CHROMEOS_H_
 
 #include "chrome/browser/ui/views/ime_driver/remote_text_input_client.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 
 class AccessibilityInputMethodObserver;
 
diff --git a/chrome/browser/ui/views/ime_driver/remote_text_input_client.h b/chrome/browser/ui/views/ime_driver/remote_text_input_client.h
index 01d0a408..14dcec3 100644
--- a/chrome/browser/ui/views/ime_driver/remote_text_input_client.h
+++ b/chrome/browser/ui/views/ime_driver/remote_text_input_client.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_IME_DRIVER_REMOTE_TEXT_INPUT_CLIENT_H_
 #define CHROME_BROWSER_UI_VIEWS_IME_DRIVER_REMOTE_TEXT_INPUT_CLIENT_H_
 
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 #include "ui/base/ime/input_method_delegate.h"
 #include "ui/base/ime/text_input_client.h"
 
diff --git a/chrome/browser/ui/views/ime_driver/simple_input_method.h b/chrome/browser/ui/views/ime_driver/simple_input_method.h
index 4b9f33ec..c1660bb 100644
--- a/chrome/browser/ui/views/ime_driver/simple_input_method.h
+++ b/chrome/browser/ui/views/ime_driver/simple_input_method.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_IME_DRIVER_SIMPLE_INPUT_METHOD_H_
 #define CHROME_BROWSER_UI_VIEWS_IME_DRIVER_SIMPLE_INPUT_METHOD_H_
 
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 
 // This is to be used on platforms where a proper implementation of
 // ui::mojom::InputMethod is missing. It doesn't handle any events and calls
diff --git a/chrome/browser/ui/views/screen_capture_notification_ui_views.cc b/chrome/browser/ui/views/screen_capture_notification_ui_views.cc
index 2883db7b..514bce0 100644
--- a/chrome/browser/ui/views/screen_capture_notification_ui_views.cc
+++ b/chrome/browser/ui/views/screen_capture_notification_ui_views.cc
@@ -33,7 +33,7 @@
 #include "ash/shell.h"  // mash-ok
 #include "mojo/public/cpp/bindings/type_converter.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
diff --git a/chrome/browser/ui/views/status_bubble_views.cc b/chrome/browser/ui/views/status_bubble_views.cc
index 9e0c329d..ceae89a1 100644
--- a/chrome/browser/ui/views/status_bubble_views.cc
+++ b/chrome/browser/ui/views/status_bubble_views.cc
@@ -44,7 +44,7 @@
 #include "ash/shell.h"                                           // mash-ok
 #include "ash/wm/window_state.h"                                 // mash-ok
 #include "services/ui/public/cpp/property_type_converters.h"     // nogncheck
-#include "services/ui/public/interfaces/window_manager.mojom.h"  // nogncheck
+#include "services/ws/public/mojom/window_manager.mojom.h"       // nogncheck
 #endif
 
 namespace {
diff --git a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
index d20cb59..ba465d5 100644
--- a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <fstream>
 #include <memory>
 #include <utility>
 
@@ -17,6 +18,8 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/path_service.h"
+#include "base/strings/pattern.h"
+#include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
 #include "base/sys_info.h"
 #include "base/task/post_task.h"
@@ -52,6 +55,32 @@
 
 namespace {
 
+constexpr char kKey[] = "key";
+constexpr char kValue[] = "value";
+constexpr char kClass[] = "class";
+
+constexpr const char* const kLogLevelName[] = {"info", "warning", "error"};
+
+size_t SeverityToLogLevelNameIndex(logging::LogSeverity severity) {
+  if (severity <= logging::LOG_INFO)
+    return 0;
+  if (severity == logging::LOG_WARNING)
+    return 1;
+  return 2;
+}
+
+size_t LogMarkToLogLevelNameIndex(char mark) {
+  switch (mark) {
+    case 'I':
+    case 'V':
+      return 0;
+    case 'W':
+      return 1;
+    default:
+      return 2;
+  }
+}
+
 // Gets metadata of all files and directories in |root_path|
 // recursively. Stores the result as a list of dictionaries like:
 //
@@ -189,27 +218,73 @@
   return out;
 }
 
-std::string SeverityToString(logging::LogSeverity severity) {
-  switch (severity) {
-    case logging::LOG_INFO:
-      return "info";
-    case logging::LOG_WARNING:
-      return "warning";
-    case logging::LOG_ERROR:
-      return "error";
-    default:  // Treat all other higher severities as ERROR.
-      return "error";
-  }
+// Appends {'key': key, 'value': value, 'class': clazz} dictionary to the
+// |list|.
+void AppendKeyValue(base::ListValue* list,
+                    std::string key,
+                    std::string value,
+                    std::string clazz = std::string()) {
+  auto dict = std::make_unique<base::DictionaryValue>();
+  dict->SetPath({kKey}, base::Value(std::move(key)));
+  dict->SetPath({kValue}, base::Value(std::move(value)));
+  if (!clazz.empty())
+    dict->SetPath({kClass}, base::Value(std::move(clazz)));
+  list->GetList().push_back(std::move(*dict));
 }
 
-// Appends {'key': key, 'value': value} dictionary to the |list|.
-void AppendKeyValue(base::ListValue* list,
-                    const std::string& key,
-                    const std::string& value) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetString("key", key);
-  dict->SetString("value", value);
-  list->Append(std::move(dict));
+ino_t GetInodeValue(const base::FilePath& path) {
+  struct stat file_stats;
+  if (stat(path.value().c_str(), &file_stats) != 0)
+    return 0;
+  return file_stats.st_ino;
+}
+
+std::pair<ino_t, base::ListValue> GetServiceLogContents(
+    const base::FilePath& log_path,
+    ino_t inode,
+    int from_line_number) {
+  base::ListValue result;
+
+  std::ifstream log(log_path.value());
+  if (log.good()) {
+    ino_t new_inode = GetInodeValue(log_path);
+    if (new_inode != inode) {
+      // Apparently log was recreated. Re-read the log.
+      from_line_number = 0;
+      inode = new_inode;
+    }
+
+    base::Time time;
+    constexpr char kTimestampPattern[] = R"(????-??-??T??:??:??.???Z? )";
+    const size_t pattern_length = strlen(kTimestampPattern);
+
+    std::string line;
+    int line_number = 0;
+    while (log.good()) {
+      std::getline(log, line);
+      if (line.empty() || ++line_number <= from_line_number) {
+        continue;
+      }
+
+      base::StringPiece log_line = line;
+      size_t severity_index = 0;
+      if (base::MatchPattern(log_line.substr(0, pattern_length),
+                             kTimestampPattern) &&
+          google_apis::util::GetTimeFromString(
+              log_line.substr(0, pattern_length - 2), &time)) {
+        severity_index = LogMarkToLogLevelNameIndex(line[pattern_length - 2]);
+        line = line.substr(pattern_length);
+      }
+      const char* const severity = kLogLevelName[severity_index];
+
+      AppendKeyValue(&result,
+                     google_apis::util::FormatTimeAsStringLocaltime(time),
+                     base::StrCat({"[", severity, "] ", line}),
+                     base::StrCat({"log-", severity}));
+    }
+  }
+
+  return {inode, std::move(result)};
 }
 
 // Class to handle messages from chrome://drive-internals.
@@ -255,6 +330,7 @@
   void UpdateCacheContentsSection(
       drive::DebugInfoCollector* debug_info_collector);
   void UpdateEventLogSection();
+  void UpdateServiceLogSection();
   void UpdatePathConfigurationsSection();
 
   // Called when GetGCacheContents() is complete.
@@ -306,9 +382,21 @@
   // Called after file system reset for ResetDriveFileSystem is done.
   void ResetFinished(bool success);
 
+  // Called when service logs are read.
+  void OnServiceLogRead(std::pair<ino_t, base::ListValue>);
+
   // The last event sent to the JavaScript side.
   int last_sent_event_id_;
 
+  // The last line of service log sent to the JS side.
+  int last_sent_line_number_;
+
+  // The inode of the log file.
+  ino_t service_log_file_inode_;
+
+  // Service log file is being parsed.
+  bool service_log_file_is_processing_ = false;
+
   base::WeakPtrFactory<DriveInternalsWebUIHandler> weak_ptr_factory_;
   DISALLOW_COPY_AND_ASSIGN(DriveInternalsWebUIHandler);
 };
@@ -461,6 +549,9 @@
   // and resent whole the logs to the page.
   last_sent_event_id_ = -1;
   UpdateEventLogSection();
+  last_sent_line_number_ = 0;
+  service_log_file_inode_ = 0;
+  UpdateServiceLogSection();
 }
 
 void DriveInternalsWebUIHandler::UpdateDriveRelatedPreferencesSection() {
@@ -763,20 +854,54 @@
     if (log[i].id <= last_sent_event_id_)
       continue;
 
-    std::string severity = SeverityToString(log[i].severity);
-
-    auto dict = std::make_unique<base::DictionaryValue>();
-    dict->SetString("key",
-        google_apis::util::FormatTimeAsStringLocaltime(log[i].when));
-    dict->SetString("value", "[" + severity + "] " + log[i].what);
-    dict->SetString("class", "log-" + severity);
-    list.Append(std::move(dict));
+    const char* const severity =
+        kLogLevelName[SeverityToLogLevelNameIndex(log[i].severity)];
+    AppendKeyValue(&list,
+                   google_apis::util::FormatTimeAsStringLocaltime(log[i].when),
+                   base::StrCat({"[", severity, "] ", log[i].what}),
+                   base::StrCat({"log-", severity}));
     last_sent_event_id_ = log[i].id;
   }
   if (!list.empty())
     web_ui()->CallJavascriptFunctionUnsafe("updateEventLog", list);
 }
 
+void DriveInternalsWebUIHandler::UpdateServiceLogSection() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  if (service_log_file_is_processing_)
+    return;
+  service_log_file_is_processing_ = true;
+
+  drive::DriveIntegrationService* integration_service = GetIntegrationService();
+  if (!integration_service)
+    return;
+  base::FilePath log_path = integration_service->GetDriveFsLogPath();
+  if (log_path.empty())
+    return;
+
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+      base::BindOnce(&GetServiceLogContents, log_path, service_log_file_inode_,
+                     last_sent_line_number_),
+      base::BindOnce(&DriveInternalsWebUIHandler::OnServiceLogRead,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DriveInternalsWebUIHandler::OnServiceLogRead(
+    std::pair<ino_t, base::ListValue> response) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (service_log_file_inode_ != response.first) {
+    service_log_file_inode_ = response.first;
+    last_sent_line_number_ = 0;
+  }
+  if (!response.second.empty()) {
+    web_ui()->CallJavascriptFunctionUnsafe("updateServiceLog", response.second);
+    last_sent_line_number_ += response.second.GetList().size();
+  }
+  service_log_file_is_processing_ = false;
+}
+
 void DriveInternalsWebUIHandler::UpdatePathConfigurationsSection() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
@@ -901,6 +1026,7 @@
     return;
 
   UpdateEventLogSection();
+  UpdateServiceLogSection();
 
   drive::JobListInterface* job_list = integration_service->job_list();
   if (job_list) {
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 9386182..1643471 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -129,7 +129,7 @@
   HEADERS_AND_FOOTERS,
   CSS_BACKGROUND,
   SELECTION_ONLY,
-  EXTERNAL_PDF_PREVIEW,
+  EXTERNAL_PDF_PREVIEW_UNUSED,
   PAGE_RANGE,
   DEFAULT_MEDIA,
   NON_DEFAULT_MEDIA,
@@ -294,18 +294,30 @@
 }
 
 // Track the popularity of print settings and report the stats.
-void ReportPrintSettingsStats(const base::DictionaryValue& settings,
+void ReportPrintSettingsStats(const base::DictionaryValue& print_settings,
+                              const base::DictionaryValue& preview_settings,
                               bool is_pdf) {
   ReportPrintSettingHistogram(TOTAL);
 
+  // Print settings can be categorized into 2 groups: settings that are applied
+  // via preview generation (page range, selection, headers/footers, background
+  // graphics, scaling, layout, page size, pages per sheet, fit to page,
+  // margins, rasterize), and settings that are applied at the printer (color,
+  // duplex, copies, collate, dpi). The former should be captured from the most
+  // recent preview request, as some of them are set to dummy values in the
+  // print ticket. Similarly, settings applied at the printer should be pulled
+  // from the print ticket, as they may have dummy values in the preview
+  // request.
   const base::ListValue* page_range_array = NULL;
-  if (settings.GetList(printing::kSettingPageRange, &page_range_array) &&
+  if (preview_settings.GetList(printing::kSettingPageRange,
+                               &page_range_array) &&
       !page_range_array->empty()) {
     ReportPrintSettingHistogram(PAGE_RANGE);
   }
 
   const base::DictionaryValue* media_size_value = NULL;
-  if (settings.GetDictionary(printing::kSettingMediaSize, &media_size_value) &&
+  if (preview_settings.GetDictionary(printing::kSettingMediaSize,
+                                     &media_size_value) &&
       !media_size_value->empty()) {
     bool is_default = false;
     if (media_size_value->GetBoolean(printing::kSettingMediaSizeIsDefault,
@@ -318,89 +330,92 @@
   }
 
   bool landscape = false;
-  if (settings.GetBoolean(printing::kSettingLandscape, &landscape))
+  if (preview_settings.GetBoolean(printing::kSettingLandscape, &landscape))
     ReportPrintSettingHistogram(landscape ? LANDSCAPE : PORTRAIT);
 
   int copies = 1;
-  if (settings.GetInteger(printing::kSettingCopies, &copies) && copies > 1)
+  if (print_settings.GetInteger(printing::kSettingCopies, &copies) &&
+      copies > 1) {
     ReportPrintSettingHistogram(COPIES);
+  }
 
   int scaling = 100;
-  if (settings.GetInteger(printing::kSettingScaleFactor, &scaling) &&
+  if (preview_settings.GetInteger(printing::kSettingScaleFactor, &scaling) &&
       scaling != 100) {
     ReportPrintSettingHistogram(SCALING);
   }
 
   int pages_per_sheet = 1;
-  if (settings.GetInteger(printing::kSettingPagesPerSheet, &pages_per_sheet) &&
+  if (preview_settings.GetInteger(printing::kSettingPagesPerSheet,
+                                  &pages_per_sheet) &&
       pages_per_sheet != 1) {
     ReportPrintSettingHistogram(PAGES_PER_SHEET);
   }
 
   bool collate = false;
-  if (settings.GetBoolean(printing::kSettingCollate, &collate) && collate)
+  if (print_settings.GetBoolean(printing::kSettingCollate, &collate) && collate)
     ReportPrintSettingHistogram(COLLATE);
 
   int duplex_mode = 0;
-  if (settings.GetInteger(printing::kSettingDuplexMode, &duplex_mode))
+  if (print_settings.GetInteger(printing::kSettingDuplexMode, &duplex_mode))
     ReportPrintSettingHistogram(duplex_mode ? DUPLEX : SIMPLEX);
 
   int color_mode = 0;
-  if (settings.GetInteger(printing::kSettingColor, &color_mode)) {
+  if (print_settings.GetInteger(printing::kSettingColor, &color_mode)) {
     ReportPrintSettingHistogram(
         printing::IsColorModelSelected(color_mode) ? COLOR : BLACK_AND_WHITE);
   }
 
   int margins_type = 0;
-  if (settings.GetInteger(printing::kSettingMarginsType, &margins_type) &&
+  if (preview_settings.GetInteger(printing::kSettingMarginsType,
+                                  &margins_type) &&
       margins_type != 0) {
     ReportPrintSettingHistogram(NON_DEFAULT_MARGINS);
   }
 
   bool headers = false;
-  if (settings.GetBoolean(printing::kSettingHeaderFooterEnabled, &headers) &&
+  if (preview_settings.GetBoolean(printing::kSettingHeaderFooterEnabled,
+                                  &headers) &&
       headers) {
     ReportPrintSettingHistogram(HEADERS_AND_FOOTERS);
   }
 
   bool css_background = false;
-  if (settings.GetBoolean(printing::kSettingShouldPrintBackgrounds,
-                          &css_background) && css_background) {
+  if (preview_settings.GetBoolean(printing::kSettingShouldPrintBackgrounds,
+                                  &css_background) &&
+      css_background) {
     ReportPrintSettingHistogram(CSS_BACKGROUND);
   }
 
   bool selection_only = false;
-  if (settings.GetBoolean(printing::kSettingShouldPrintSelectionOnly,
-                          &selection_only) && selection_only) {
+  if (preview_settings.GetBoolean(printing::kSettingShouldPrintSelectionOnly,
+                                  &selection_only) &&
+      selection_only) {
     ReportPrintSettingHistogram(SELECTION_ONLY);
   }
 
-  bool external_preview = false;
-  if (settings.GetBoolean(printing::kSettingOpenPDFInPreview,
-                          &external_preview) && external_preview) {
-    ReportPrintSettingHistogram(EXTERNAL_PDF_PREVIEW);
-  }
-
   bool rasterize = false;
-  if (settings.GetBoolean(printing::kSettingRasterizePdf,
-                          &rasterize) && rasterize) {
+  if (preview_settings.GetBoolean(printing::kSettingRasterizePdf, &rasterize) &&
+      rasterize) {
     ReportPrintSettingHistogram(PRINT_AS_IMAGE);
   }
 
   bool fit_to_page = false;
   if (is_pdf &&
-      settings.GetBoolean(printing::kSettingFitToPageEnabled, &fit_to_page) &&
+      preview_settings.GetBoolean(printing::kSettingFitToPageEnabled,
+                                  &fit_to_page) &&
       fit_to_page) {
     ReportPrintSettingHistogram(FIT_TO_PAGE);
   }
 
   int dpi_horizontal = 0;
   int dpi_vertical = 0;
-  if (settings.GetInteger(printing::kSettingDpiHorizontal, &dpi_horizontal) &&
-      settings.GetInteger(printing::kSettingDpiVertical, &dpi_vertical) &&
+  if (print_settings.GetInteger(printing::kSettingDpiHorizontal,
+                                &dpi_horizontal) &&
+      print_settings.GetInteger(printing::kSettingDpiVertical, &dpi_vertical) &&
       dpi_horizontal > 0 && dpi_vertical > 0) {
     bool is_default = false;
-    if (settings.GetBoolean(printing::kSettingDpiDefault, &is_default))
+    if (print_settings.GetBoolean(printing::kSettingDpiDefault, &is_default))
       ReportPrintSettingHistogram(is_default ? DEFAULT_DPI : NON_DEFAULT_DPI);
   }
 }
@@ -801,6 +816,7 @@
   VLOG(1) << "Print preview request start";
 
   rfh->Send(new PrintMsg_PrintPreview(rfh->GetRoutingID(), *settings));
+  last_preview_settings_ = std::move(settings);
 }
 
 void PrintPreviewHandler::HandlePrint(const base::ListValue* args) {
@@ -864,7 +880,8 @@
 
   // After validating |settings|, record metrics.
   bool is_pdf = !print_preview_ui()->source_is_modifiable();
-  ReportPrintSettingsStats(*settings, is_pdf);
+  if (last_preview_settings_)
+    ReportPrintSettingsStats(*settings, *last_preview_settings_, is_pdf);
   {
     PrintDocumentTypeBuckets doc_type = is_pdf ? PDF_DOCUMENT : HTML_DOCUMENT;
     size_t average_page_size_in_kb = data->size() / page_count;
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
index 2a5806b8..c3f7918 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -326,6 +326,9 @@
   // Whether we have already logged the number of printers this session.
   bool has_logged_printers_count_;
 
+  // The settings used for the most recent preview request.
+  std::unique_ptr<base::DictionaryValue> last_preview_settings_;
+
   // Holds token service to get OAuth2 access tokens.
   std::unique_ptr<AccessTokenService> token_service_;
 
diff --git a/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc b/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc
index 06fca75..e215debd 100644
--- a/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc
+++ b/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc
@@ -28,6 +28,13 @@
 constexpr char kWebAppManifestUrl[] = "web_app_manifest_url";
 constexpr char kWebAppStartUrl[] = "web_app_start_url";
 
+#if defined(OS_CHROMEOS)
+// The sub-directory of the extensions directory in which to scan for external
+// web apps (as opposed to external extensions or external ARC apps).
+const base::FilePath::CharType kWebAppsSubDirectory[] =
+    FILE_PATH_LITERAL("web_apps");
+#endif
+
 std::vector<web_app::PendingAppManager::AppInfo> ScanDir(base::FilePath dir) {
   base::AssertBlockingAllowed();
   base::FilePath::StringType extension(FILE_PATH_LITERAL(".json"));
@@ -94,18 +101,14 @@
 
   if (chromeos::ProfileHelper::IsPrimaryProfile(profile)) {
     // For manual testing, you can change s/STANDALONE/USER/, as writing to
-    // "$HOME/.config/chromium/test-user/.config/chromium/External Extensions"
-    // does not require root ACLs, unlike "/usr/share/chromium/extensions".
-    //
-    // TODO(nigeltao): do we want to append a sub-directory name, analogous to
-    // the "arc" in "/usr/share/chromium/extensions/arc" as per
-    // chrome/browser/ui/app_list/arc/arc_default_app_list.cc? Or should we not
-    // sort "system apps" into directories based on their platform (e.g. ARC,
-    // PWA, etc.), and instead examine the JSON contents (e.g. an "activity"
-    // key means ARC, "web_app_start_url" key means PWA, etc.)?
+    // "$HOME/.config/chromium/test-user/.config/chromium/External
+    // Extensions/web_apps" does not require root ACLs, unlike
+    // "/usr/share/chromium/extensions/web_apps".
     if (!base::PathService::Get(chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS,
                                 &dir)) {
       LOG(ERROR) << "ScanForExternalWebApps: base::PathService::Get failed";
+    } else {
+      dir = dir.Append(kWebAppsSubDirectory);
     }
   }
 
@@ -128,22 +131,22 @@
   base::FilePath dir = DetermineScanDir(profile);
   if (dir.empty()) {
     std::move(callback).Run(std::vector<web_app::PendingAppManager::AppInfo>());
-  } else {
-    // Do a two-part callback dance, across different TaskRunners.
-    //
-    // 1. Schedule ScanDir to happen on a background thread, so that we don't
-    // block the UI thread. When that's done,
-    // base::PostTaskWithTraitsAndReplyWithResult will bounce us back to the
-    // originating thread (the UI thread).
-    //
-    // 2. In |callback|, forward the vector of AppInfo's on to the
-    // pending_app_manager_, which can only be called on the UI thread.
-    base::PostTaskWithTraitsAndReplyWithResult(
-        FROM_HERE,
-        {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
-        base::BindOnce(&ScanDir, dir), std::move(callback));
+    return;
   }
+  // Do a two-part callback dance, across different TaskRunners.
+  //
+  // 1. Schedule ScanDir to happen on a background thread, so that we don't
+  // block the UI thread. When that's done,
+  // base::PostTaskWithTraitsAndReplyWithResult will bounce us back to the
+  // originating thread (the UI thread).
+  //
+  // 2. In |callback|, forward the vector of AppInfo's on to the
+  // pending_app_manager_, which can only be called on the UI thread.
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+      base::BindOnce(&ScanDir, dir), std::move(callback));
 }
 
 }  //  namespace web_app
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
index 5e88dda5..97caa30 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
@@ -63,13 +63,15 @@
   void InstallWebAppOrShortcutFromWebContents(
       content::WebContents* web_contents,
       BookmarkAppInstallationTask::ResultCallback callback) override {
+    BookmarkAppInstallationTask::ResultCode result_code =
+        BookmarkAppInstallationTask::ResultCode::kInstallationFailed;
+    std::string app_id;
+    if (succeeds_) {
+      result_code = BookmarkAppInstallationTask::ResultCode::kSuccess;
+      app_id = "fake_app_id_for:" + app_info().url.spec();
+    }
     std::move(callback).Run(
-        succeeds_
-            ? BookmarkAppInstallationTask::Result(
-                  BookmarkAppInstallationTask::ResultCode::kSuccess, "12345")
-            : BookmarkAppInstallationTask::Result(
-                  BookmarkAppInstallationTask::ResultCode::kInstallationFailed,
-                  std::string()));
+        BookmarkAppInstallationTask::Result(result_code, app_id));
   }
 
  private:
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index 8850cf63..dfafcd72 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -63,17 +63,6 @@
       switches::kWebAuthenticationUI);
 }
 
-bool ShouldDispatchRequestImmediately(
-    const device::FidoAuthenticator& authenticator) {
-  // TODO(hongjunchoi): Change this so that requests for BLE authenticators are
-  // not dispatched immediately if WebAuthN UI is enabled.
-  if (!IsWebAuthnUiEnabled())
-    return true;
-
-  return authenticator.AuthenticatorTransport() !=
-         device::FidoTransportProtocol::kInternal;
-}
-
 }  // namespace
 
 #if defined(OS_MACOSX)
@@ -267,12 +256,19 @@
 #endif
 }
 
-void ChromeAuthenticatorRequestDelegate::FidoAuthenticatorAdded(
-    const device::FidoAuthenticator& authenticator,
-    bool* hold_off_request) {
-  if (!ShouldDispatchRequestImmediately(authenticator))
-    *hold_off_request = true;
+bool ChromeAuthenticatorRequestDelegate::EmbedderControlsAuthenticatorDispatch(
+    const device::FidoAuthenticator& authenticator) {
+  // TODO(hongjunchoi): Change this so that requests for BLE authenticators are
+  // not dispatched immediately if WebAuthN UI is enabled.
+  if (!IsWebAuthnUiEnabled())
+    return false;
 
+  return authenticator.AuthenticatorTransport() ==
+         device::FidoTransportProtocol::kInternal;
+}
+
+void ChromeAuthenticatorRequestDelegate::FidoAuthenticatorAdded(
+    const device::FidoAuthenticator& authenticator) {
   if (!IsWebAuthnUiEnabled())
     return;
 
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
index 83bff00..e8f91268 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
@@ -79,8 +79,10 @@
   // device::FidoRequestHandlerBase::TransportAvailabilityObserver:
   void OnTransportAvailabilityEnumerated(
       device::FidoRequestHandlerBase::TransportAvailabilityInfo data) override;
-  void FidoAuthenticatorAdded(const device::FidoAuthenticator& authenticator,
-                              bool* hold_off_request) override;
+  bool EmbedderControlsAuthenticatorDispatch(
+      const device::FidoAuthenticator& authenticator) override;
+  void FidoAuthenticatorAdded(
+      const device::FidoAuthenticator& authenticator) override;
   void FidoAuthenticatorRemoved(base::StringPiece authenticator_id) override;
 
   // AuthenticatorRequestDialogModel::Observer:
diff --git a/chrome/common/extensions/docs/templates/articles/inline_faq.html b/chrome/common/extensions/docs/templates/articles/inline_faq.html
index 0bd535f0..c7b65e0 100644
--- a/chrome/common/extensions/docs/templates/articles/inline_faq.html
+++ b/chrome/common/extensions/docs/templates/articles/inline_faq.html
@@ -53,7 +53,7 @@
 
 <h2 id="after_disabled">After inline-installation is disabled</h2>
 
-<h3 id="future_flow">What will the installation flow look like?</h2>
+<h3 id="future_flow">What will the installation flow look like?</h3>
 
 <p>
   When your site calls <code>chrome.webstore.install()</code>,
diff --git a/chrome/services/printing/pdf_nup_converter.cc b/chrome/services/printing/pdf_nup_converter.cc
index 4d306c87..cbea1b17 100644
--- a/chrome/services/printing/pdf_nup_converter.cc
+++ b/chrome/services/printing/pdf_nup_converter.cc
@@ -9,8 +9,9 @@
 
 #include "base/containers/span.h"
 #include "base/logging.h"
+#include "base/memory/free_deleter.h"
 #include "components/crash/core/common/crash_key.h"
-#include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.h"
+#include "mojo/public/cpp/base/shared_memory_utils.h"
 #include "pdf/pdf.h"
 
 namespace printing {
@@ -36,6 +37,25 @@
   return pdf_page_span;
 }
 
+using ScopedMallocMemory = std::unique_ptr<void, base::FreeDeleter>;
+
+template <class Callback>
+void RunCallbackWithConversionResult(Callback callback,
+                                     ScopedMallocMemory buffer,
+                                     size_t size) {
+  base::MappedReadOnlyRegion region_mapping =
+      mojo::CreateReadOnlySharedMemoryRegion(size);
+  if (!region_mapping.IsValid()) {
+    std::move(callback).Run(mojom::PdfNupConverter::Status::HANDLE_MAP_ERROR,
+                            std::move(region_mapping.region));
+    return;
+  }
+
+  memcpy(region_mapping.mapping.memory(), buffer.get(), size);
+  std::move(callback).Run(mojom::PdfNupConverter::Status::SUCCESS,
+                          std::move(region_mapping.region));
+}
+
 }  // namespace
 
 PdfNupConverter::PdfNupConverter(
@@ -55,21 +75,17 @@
 
   void* output_pdf_buffer = nullptr;
   size_t output_pdf_buffer_size = 0;
-  base::MappedReadOnlyRegion region_mapping;
   if (!chrome_pdf::ConvertPdfPagesToNupPdf(
           std::move(input_pdf_buffers), pages_per_sheet, page_size.width(),
           page_size.height(), &output_pdf_buffer, &output_pdf_buffer_size)) {
     std::move(callback).Run(mojom::PdfNupConverter::Status::CONVERSION_FAILURE,
-                            std::move(region_mapping.region));
+                            base::ReadOnlySharedMemoryRegion());
     return;
   }
 
-  region_mapping = CreateReadOnlySharedMemoryRegion(output_pdf_buffer_size);
-  memcpy(region_mapping.mapping.memory(), output_pdf_buffer,
-         output_pdf_buffer_size);
-  free(output_pdf_buffer);
-  std::move(callback).Run(mojom::PdfNupConverter::Status::SUCCESS,
-                          std::move(region_mapping.region));
+  RunCallbackWithConversionResult(std::move(callback),
+                                  ScopedMallocMemory(output_pdf_buffer),
+                                  output_pdf_buffer_size);
 }
 
 void PdfNupConverter::NupDocumentConvert(
@@ -83,21 +99,17 @@
       pdf_document_mapping.size());
   void* output_pdf_buffer = nullptr;
   size_t output_pdf_buffer_size = 0;
-  base::MappedReadOnlyRegion region_mapping;
   if (!chrome_pdf::ConvertPdfDocumentToNupPdf(
           input_pdf_buffer, pages_per_sheet, page_size.width(),
           page_size.height(), &output_pdf_buffer, &output_pdf_buffer_size)) {
     std::move(callback).Run(mojom::PdfNupConverter::Status::CONVERSION_FAILURE,
-                            std::move(region_mapping.region));
+                            base::ReadOnlySharedMemoryRegion());
     return;
   }
 
-  region_mapping = CreateReadOnlySharedMemoryRegion(output_pdf_buffer_size);
-  memcpy(region_mapping.mapping.memory(), output_pdf_buffer,
-         output_pdf_buffer_size);
-  free(output_pdf_buffer);
-  std::move(callback).Run(mojom::PdfNupConverter::Status::SUCCESS,
-                          std::move(region_mapping.region));
+  RunCallbackWithConversionResult(std::move(callback),
+                                  ScopedMallocMemory(output_pdf_buffer),
+                                  output_pdf_buffer_size);
 }
 
 void PdfNupConverter::SetWebContentsURL(const GURL& url) {
diff --git a/chrome/services/printing/pdf_to_emf_converter.cc b/chrome/services/printing/pdf_to_emf_converter.cc
index 3500b9d..dbf0e8bf 100644
--- a/chrome/services/printing/pdf_to_emf_converter.cc
+++ b/chrome/services/printing/pdf_to_emf_converter.cc
@@ -11,7 +11,7 @@
 #include "base/containers/span.h"
 #include "base/lazy_instance.h"
 #include "base/stl_util.h"
-#include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.h"
+#include "mojo/public/cpp/base/shared_memory_utils.h"
 #include "pdf/pdf.h"
 #include "printing/emf_win.h"
 #include "ui/gfx/gdi_util.h"
@@ -112,8 +112,8 @@
     return;
 
   int page_count = 0;
-  auto pdf_span = base::make_span(
-      reinterpret_cast<const uint8_t*>(pdf_mapping_.memory()), size);
+  auto pdf_span =
+      base::make_span(static_cast<const uint8_t*>(pdf_mapping_.memory()), size);
   chrome_pdf::GetPDFDocInfo(pdf_span, &page_count, nullptr);
   total_page_count_ = page_count;
 }
@@ -152,9 +152,8 @@
   int offset_y = postscript ? pdf_render_settings_.offsets.y() : 0;
 
   base::ReadOnlySharedMemoryRegion invalid_emf_region;
-  auto pdf_span =
-      base::make_span(reinterpret_cast<const uint8_t*>(pdf_mapping_.memory()),
-                      pdf_mapping_.size());
+  auto pdf_span = base::make_span(
+      static_cast<const uint8_t*>(pdf_mapping_.memory()), pdf_mapping_.size());
   if (!chrome_pdf::RenderPDFPageToDC(
           pdf_span, page_number, metafile.context(),
           pdf_render_settings_.dpi.width(), pdf_render_settings_.dpi.height(),
@@ -170,7 +169,7 @@
 
   const uint32_t size = metafile.GetDataSize();
   base::MappedReadOnlyRegion region_mapping =
-      CreateReadOnlySharedMemoryRegion(size);
+      mojo::CreateReadOnlySharedMemoryRegion(size);
   if (!region_mapping.IsValid())
     return invalid_emf_region;
 
diff --git a/chrome/services/printing/pdf_to_pwg_raster_converter.cc b/chrome/services/printing/pdf_to_pwg_raster_converter.cc
index 7d06ca0..9e80c09 100644
--- a/chrome/services/printing/pdf_to_pwg_raster_converter.cc
+++ b/chrome/services/printing/pdf_to_pwg_raster_converter.cc
@@ -11,7 +11,7 @@
 #include "base/containers/span.h"
 #include "components/pwg_encoder/bitmap_image.h"
 #include "components/pwg_encoder/pwg_encoder.h"
-#include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.h"
+#include "mojo/public/cpp/base/shared_memory_utils.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "pdf/pdf.h"
 #include "printing/pdf_render_settings.h"
@@ -30,9 +30,8 @@
   if (!pdf_mapping.IsValid())
     return invalid_pwg_region;
 
-  auto pdf_data =
-      base::make_span(reinterpret_cast<const uint8_t*>(pdf_mapping.memory()),
-                      pdf_mapping.size());
+  auto pdf_data = base::make_span(
+      static_cast<const uint8_t*>(pdf_mapping.memory()), pdf_mapping.size());
 
   // Get the page count and reserve 64 KB per page in |pwg_data| below.
   static constexpr size_t kEstimatedSizePerPage = 64 * 1024;
@@ -101,7 +100,7 @@
   }
 
   base::MappedReadOnlyRegion region_mapping =
-      CreateReadOnlySharedMemoryRegion(pwg_data.size());
+      mojo::CreateReadOnlySharedMemoryRegion(pwg_data.size());
   if (!region_mapping.IsValid())
     return invalid_pwg_region;
 
diff --git a/chrome/services/printing/public/mojom/pdf_nup_converter.mojom b/chrome/services/printing/public/mojom/pdf_nup_converter.mojom
index 8c616b5..bf7f2e3 100644
--- a/chrome/services/printing/public/mojom/pdf_nup_converter.mojom
+++ b/chrome/services/printing/public/mojom/pdf_nup_converter.mojom
@@ -26,6 +26,7 @@
   enum Status {
     SUCCESS = 0,
     CONVERSION_FAILURE = 1,
+    HANDLE_MAP_ERROR = 2,
   };
 
   // Convert a list of PDF pages to a N-up PDF.
diff --git a/chrome/test/data/webui/print_preview/preview_generation_test.js b/chrome/test/data/webui/print_preview/preview_generation_test.js
index b79b50e..5542203 100644
--- a/chrome/test/data/webui/print_preview/preview_generation_test.js
+++ b/chrome/test/data/webui/print_preview/preview_generation_test.js
@@ -28,9 +28,6 @@
     /** @type {?print_preview.NativeLayer} */
     let nativeLayer = null;
 
-    /** @type {?print_preview.DocumentInfo} */
-    let documentInfo = null;
-
     /** @type {!print_preview.NativeInitialSettings} */
     const initialSettings =
         print_preview_test_utils.getDefaultInitialSettings();
@@ -43,9 +40,8 @@
     });
 
     /**
-     * Initializes the UI with a default local destination. |documentInfo| is
-     * initialized to a 3 page HTML document with no selection if it has not
-     * been set yet.
+     * Initializes the UI with a default local destination and a 3 page document
+     * length.
      * @return {!Promise} Promise that resolves when initialization is done,
      *     destination is set, and initial preview request is complete.
      */
@@ -68,28 +64,14 @@
             nativeLayer.whenCalled('getPrinterCapabilities'),
           ])
           .then(function() {
-            if (!documentInfo)
-              initDocumentInfo(false, false);
-            page.set('documentInfo_', documentInfo);
-            page.notifyPath('documentInfo_.isModifiable');
+            if (!page.documentInfo_.isModifiable)
+              page.documentInfo_.updateFitToPageScaling(98);
+            page.documentInfo_.updatePageCount(3);
             return nativeLayer.whenCalled('getPreview');
           });
     }
 
     /**
-     * Initializes |documentInfo| with a 3 page document.
-     * @param {boolean} isPdf Whether the document should be a PDF.
-     * @param {boolean} hasSelection Whether the document has a selection.
-     */
-    function initDocumentInfo(isPdf, hasSelection) {
-      documentInfo = new print_preview.DocumentInfo();
-      documentInfo.init(!isPdf, 'title', hasSelection);
-      if (isPdf)
-        documentInfo.updateFitToPageScaling(98);
-      documentInfo.updatePageCount(3);
-    }
-
-    /**
      * @param {string} settingName The name of the setting to test.
      * @param {boolean | string} initialSettingValue The default setting value.
      * @param {boolean | string} updatedSettingValue The setting value to update
@@ -143,7 +125,7 @@
     /** Validate changing the fit to page setting updates the preview. */
     test(assert(TestNames.FitToPage), function() {
       // Set PDF document so setting is available.
-      initDocumentInfo(true, false);
+      initialSettings.previewModifiable = false;
       return testSimpleSetting(
           'fitToPage', false, true, 'fitToPageEnabled', false, true);
     });
@@ -239,7 +221,7 @@
     /** Validate changing the selection only setting updates the preview. */
     test(assert(TestNames.SelectionOnly), function() {
       // Set has selection to true so that the setting is available.
-      initDocumentInfo(false, true);
+      initialSettings.hasSelection = true;
       return testSimpleSetting(
           'selectionOnly', false, true, 'shouldPrintSelectionOnly', false,
           true);
@@ -261,7 +243,7 @@
      */
     test(assert(TestNames.Rasterize), function() {
       // Set PDF document so setting is available.
-      initDocumentInfo(true, false);
+      initialSettings.previewModifiable = false;
       return testSimpleSetting(
           'rasterize', false, true, 'rasterizePDF', false, true);
     });
diff --git a/chrome/utility/BUILD.gn b/chrome/utility/BUILD.gn
index 3a19af54..9d646c2 100644
--- a/chrome/utility/BUILD.gn
+++ b/chrome/utility/BUILD.gn
@@ -149,7 +149,6 @@
     ]
     deps += [
       "//ash",
-      "//ash/components/autoclick:lib",
       "//ash/components/quick_launch:lib",
       "//ash/components/quick_launch/public/mojom",
       "//ash/components/shortcut_viewer:lib",
@@ -157,7 +156,7 @@
       "//ash/components/tap_visualizer:lib",
       "//ash/components/tap_visualizer/public/mojom",
       "//chrome/services/file_util:lib",
-      "//services/ui/public/interfaces",
+      "//services/ws/public/mojom",
     ]
   }
 
diff --git a/chrome/utility/DEPS b/chrome/utility/DEPS
index 49c4ed6..bb5147f 100644
--- a/chrome/utility/DEPS
+++ b/chrome/utility/DEPS
@@ -43,6 +43,7 @@
   "+services/proxy_resolver",
   "+services/ui/public",
   "+services/ui/service.h",
+  "+services/ws/public",
   "+skia/ext",
   "+third_party/libxml",
   "+third_party/zlib/google",
@@ -51,7 +52,6 @@
 specific_include_rules = {
   "mash_service_factory.cc": [
     "+ash/ash_service.h",
-    "+ash/components/autoclick/autoclick_application.h",
     "+ash/components/quick_launch/public",
     "+ash/components/quick_launch/quick_launch_application.h",
     "+ash/components/shortcut_viewer/public",
@@ -62,5 +62,6 @@
     "+ash/window_manager_service.h",
     "+services/ui/public",
     "+services/ui/service.h",
+    "+services/ws/public",
   ],
 }
diff --git a/chrome/utility/mash_service_factory.cc b/chrome/utility/mash_service_factory.cc
index 62095128..6d32dc7 100644
--- a/chrome/utility/mash_service_factory.cc
+++ b/chrome/utility/mash_service_factory.cc
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include "ash/ash_service.h"
-#include "ash/components/autoclick/autoclick_application.h"
 #include "ash/components/quick_launch/public/mojom/constants.mojom.h"
 #include "ash/components/quick_launch/quick_launch_application.h"
 #include "ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom.h"
@@ -26,7 +25,7 @@
 // numeric values should never be reused.
 enum class MashService {
   kAsh = 0,
-  kAutoclick = 1,
+  kAutoclickDeprecated = 1,  // Deleted Aug 2018, https://crbug.com/876115
   kQuickLaunch = 2,
   kShortcutViewer = 3,
   kTapVisualizer = 4,
@@ -58,13 +57,6 @@
   return std::make_unique<ash::AshService>();
 }
 
-std::unique_ptr<service_manager::Service> CreateAutoclickApp() {
-  RecordMashServiceLaunch(MashService::kAutoclick);
-  // Use an abbreviation of the service name to keep log lines shorter.
-  logging::SetLogPrefix("autoclick");
-  return std::make_unique<autoclick::AutoclickApplication>();
-}
-
 std::unique_ptr<service_manager::Service> CreateQuickLaunchApp() {
   RecordMashServiceLaunch(MashService::kQuickLaunch);
   logging::SetLogPrefix("quick");
@@ -95,7 +87,6 @@
   RegisterMashService(services, quick_launch::mojom::kServiceName,
                       &CreateQuickLaunchApp);
   RegisterMashService(services, ash::mojom::kServiceName, &CreateAshService);
-  RegisterMashService(services, "autoclick_app", &CreateAutoclickApp);
   RegisterMashService(services, shortcut_viewer::mojom::kServiceName,
                       &CreateShortcutViewerApp);
   RegisterMashService(services, tap_visualizer::mojom::kServiceName,
diff --git a/chromeos/components/drivefs/drivefs_host.cc b/chromeos/components/drivefs/drivefs_host.cc
index 22f58eb..aaa232371 100644
--- a/chromeos/components/drivefs/drivefs_host.cc
+++ b/chromeos/components/drivefs/drivefs_host.cc
@@ -79,10 +79,8 @@
         pending_token_(base::UnguessableToken::Create()),
         binding_(this) {
     source_path_ = base::StrCat({kMountScheme, pending_token_.ToString()});
-    std::string datadir_option = base::StrCat(
-        {"datadir=", host_->profile_path_.Append(kDataPath)
-                         .Append(host_->delegate_->GetObfuscatedAccountId())
-                         .value()});
+    std::string datadir_option =
+        base::StrCat({"datadir=", host_->GetDataPath().value()});
     auto bootstrap =
         mojo::MakeProxy(mojo_connection_delegate_->InitializeMojoConnection());
     mojom::DriveFsDelegatePtr delegate;
@@ -365,6 +363,11 @@
   return mount_state_->mount_path();
 }
 
+base::FilePath DriveFsHost::GetDataPath() const {
+  return profile_path_.Append(kDataPath).Append(
+      delegate_->GetObfuscatedAccountId());
+}
+
 mojom::DriveFs* DriveFsHost::GetDriveFsInterface() const {
   if (!mount_state_ || !mount_state_->mounted()) {
     return nullptr;
diff --git a/chromeos/components/drivefs/drivefs_host.h b/chromeos/components/drivefs/drivefs_host.h
index 60942b7b..f530f48 100644
--- a/chromeos/components/drivefs/drivefs_host.h
+++ b/chromeos/components/drivefs/drivefs_host.h
@@ -100,6 +100,9 @@
   // |IsMounted()| returns true.
   const base::FilePath& GetMountPath() const;
 
+  // Returns the path where DriveFS keeps its data and caches.
+  base::FilePath GetDataPath() const;
+
   mojom::DriveFs* GetDriveFsInterface() const;
 
  private:
diff --git a/chromeos/components/drivefs/drivefs_host_unittest.cc b/chromeos/components/drivefs/drivefs_host_unittest.cc
index ac2ce017..99930890 100644
--- a/chromeos/components/drivefs/drivefs_host_unittest.cc
+++ b/chromeos/components/drivefs/drivefs_host_unittest.cc
@@ -372,6 +372,9 @@
 TEST_F(DriveFsHostTest, Basic) {
   EXPECT_FALSE(host_->IsMounted());
 
+  EXPECT_EQ(base::FilePath("/path/to/profile/GCache/v2/salt-g-ID"),
+            host_->GetDataPath());
+
   ASSERT_NO_FATAL_FAILURE(DoMount());
 
   EXPECT_EQ(base::FilePath("/media/drivefsroot/salt-g-ID"),
diff --git a/chromeos/services/assistant/audio_decoder/BUILD.gn b/chromeos/services/assistant/audio_decoder/BUILD.gn
new file mode 100644
index 0000000..0dd692d4
--- /dev/null
+++ b/chromeos/services/assistant/audio_decoder/BUILD.gn
@@ -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.
+
+import("//chromeos/assistant/assistant.gni")
+
+import("//services/service_manager/public/service_manifest.gni")
+
+assert(enable_cros_libassistant)
+
+source_set("lib") {
+  sources = [
+    "assistant_audio_decoder.cc",
+    "assistant_audio_decoder.h",
+    "assistant_audio_decoder_factory.cc",
+    "assistant_audio_decoder_factory.h",
+    "assistant_audio_decoder_service.cc",
+    "assistant_audio_decoder_service.h",
+  ]
+
+  deps = [
+    "//base",
+    "//chromeos/services/assistant/public/mojom",
+  ]
+
+  public_deps = [
+    "//media",
+    "//mojo/public/cpp/bindings:bindings",
+    "//services/service_manager/public/cpp:cpp",
+  ]
+}
+
+service_manifest("manifest") {
+  name = "assistant_audio_decoder"
+  source = "manifest.json"
+}
diff --git a/chromeos/services/assistant/audio_decoder/DEPS b/chromeos/services/assistant/audio_decoder/DEPS
new file mode 100644
index 0000000..386e0fef
--- /dev/null
+++ b/chromeos/services/assistant/audio_decoder/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+media/filters",
+  "+media/formats",
+  "+media/mojo",
+]
diff --git a/ash/components/autoclick/OWNERS b/chromeos/services/assistant/audio_decoder/OWNERS
similarity index 100%
rename from ash/components/autoclick/OWNERS
rename to chromeos/services/assistant/audio_decoder/OWNERS
diff --git a/chromeos/services/assistant/audio_decoder/assistant_audio_decoder.cc b/chromeos/services/assistant/audio_decoder/assistant_audio_decoder.cc
new file mode 100644
index 0000000..30d6b9c
--- /dev/null
+++ b/chromeos/services/assistant/audio_decoder/assistant_audio_decoder.cc
@@ -0,0 +1,119 @@
+// 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/audio_decoder/assistant_audio_decoder.h"
+
+#include "base/bind_helpers.h"
+#include "media/base/audio_buffer.h"
+#include "media/base/media_log.h"
+#include "media/base/media_tracks.h"
+#include "media/base/stream_parser_buffer.h"
+#include "media/base/text_track_config.h"
+#include "media/filters/ffmpeg_audio_decoder.h"
+#include "media/formats/mpeg/mpeg1_audio_stream_parser.h"
+#include "media/mojo/common/media_type_converters.h"
+#include "media/mojo/interfaces/media_types.mojom.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+
+namespace chromeos {
+namespace assistant {
+
+AssistantAudioDecoder::AssistantAudioDecoder(
+    std::unique_ptr<service_manager::ServiceContextRef> service_ref,
+    mojom::AssistantAudioDecoderClientPtr client)
+    : service_ref_(std::move(service_ref)),
+      client_(std::move(client)),
+      task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      media_log_(std::make_unique<media::MediaLog>()),
+      parser_(std::make_unique<media::MPEG1AudioStreamParser>()),
+      decoder_(std::make_unique<media::FFmpegAudioDecoder>(task_runner_,
+                                                           media_log_.get())) {
+  parser_->Init(
+      /*init_cb=*/base::DoNothing(),
+      base::BindRepeating(&AssistantAudioDecoder::OnNewConfigs,
+                          base::Unretained(this)),
+      base::BindRepeating(&AssistantAudioDecoder::OnNewBuffers,
+                          base::Unretained(this)),
+      /*ignore_text_tracks=*/true,
+      /*encrypted_media_init_data_cb=*/base::DoNothing(),
+      /*new_segment_cb=*/base::DoNothing(),
+      /*end_of_segment_cb=*/base::DoNothing(), media_log_.get());
+}
+
+AssistantAudioDecoder::~AssistantAudioDecoder() = default;
+
+void AssistantAudioDecoder::Decode(const std::vector<uint8_t>& data) {
+  if (!parser_->Parse(data.data(), data.size()))
+    client_->OnDecoderError();
+}
+
+bool AssistantAudioDecoder::OnNewConfigs(
+    std::unique_ptr<media::MediaTracks> tracks,
+    const media::StreamParser::TextTrackConfigMap& text_configs) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  const media::StreamParser::TrackId track_id =
+      tracks->tracks().back()->bytestream_track_id();
+
+  // Initialize decoder.
+  const media::AudioDecoderConfig& config = tracks->getAudioConfig(track_id);
+  decoder_->Initialize(
+      config, /*cdm_context=*/nullptr,
+      base::BindRepeating(&AssistantAudioDecoder::OnDecoderInitialized,
+                          base::Unretained(this), config),
+      base::BindRepeating(&AssistantAudioDecoder::OnDecodeOutput,
+                          base::Unretained(this)),
+      /*waiting_for_decryption_key_cb=*/base::NullCallback());
+  return true;
+}
+
+bool AssistantAudioDecoder::OnNewBuffers(
+    const media::StreamParser::BufferQueueMap& buffer_queue_map) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  int new_buffer_size = 0;
+  for (const auto& queue_map : buffer_queue_map) {
+    new_buffer_size += queue_map.second.size();
+    for (const auto& buffer : queue_map.second)
+      parsed_buffers_.push_back(buffer);
+  }
+  client_->OnNewBuffers(new_buffer_size);
+  DecodeNow();
+  return true;
+}
+
+void AssistantAudioDecoder::OnDecoderInitialized(
+    media::AudioDecoderConfig config,
+    bool success) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  client_->OnDecoderInitialized(success, config.samples_per_second(),
+                                config.channels());
+}
+
+void AssistantAudioDecoder::OnDecodeOutput(
+    const scoped_refptr<media::AudioBuffer>& decoded) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  client_->OnBufferDecoded(media::mojom::AudioBuffer::From(decoded));
+  DecodeNow();
+}
+
+void AssistantAudioDecoder::OnDecodeStatus(media::DecodeStatus status) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  if (status != media::DecodeStatus::OK)
+    client_->OnDecoderError();
+}
+
+void AssistantAudioDecoder::DecodeNow() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  if (!parsed_buffers_.empty()) {
+    const auto& data = parsed_buffers_.front();
+    DCHECK_NE(data->timestamp(), media::kNoTimestamp);
+
+    decoder_->Decode(data,
+                     base::BindRepeating(&AssistantAudioDecoder::OnDecodeStatus,
+                                         base::Unretained(this)));
+    parsed_buffers_.pop_front();
+  }
+}
+
+}  // namespace assistant
+}  // namespace chromeos
diff --git a/chromeos/services/assistant/audio_decoder/assistant_audio_decoder.h b/chromeos/services/assistant/audio_decoder/assistant_audio_decoder.h
new file mode 100644
index 0000000..e875a8c3
--- /dev/null
+++ b/chromeos/services/assistant/audio_decoder/assistant_audio_decoder.h
@@ -0,0 +1,72 @@
+// 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 CHROMEOS_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_H_
+#define CHROMEOS_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_H_
+
+#include <memory>
+
+#include "base/containers/circular_deque.h"
+#include "base/macros.h"
+#include "chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom.h"
+#include "media/base/stream_parser.h"
+
+namespace media {
+class AudioBuffer;
+class MediaLog;
+class MediaTracks;
+class MPEG1AudioStreamParser;
+class FFmpegAudioDecoder;
+class StreamParserBuffer;
+}  // namespace media
+
+namespace service_manager {
+class ServiceContextRef;
+}  // namespace service_manager
+
+namespace chromeos {
+namespace assistant {
+
+class AssistantAudioDecoder : public mojom::AssistantAudioDecoder {
+ public:
+  AssistantAudioDecoder(
+      std::unique_ptr<service_manager::ServiceContextRef> service_ref,
+      mojom::AssistantAudioDecoderClientPtr client);
+  ~AssistantAudioDecoder() override;
+
+  // Called by |client_| on main thread.
+  void Decode(const std::vector<uint8_t>& data) override;
+
+ private:
+  // Called by |parser_| on main thread.
+  bool OnNewConfigs(
+      std::unique_ptr<media::MediaTracks> tracks,
+      const media::StreamParser::TextTrackConfigMap& text_configs);
+  bool OnNewBuffers(
+      const media::StreamParser::BufferQueueMap& buffer_queue_map);
+
+  // Called by |decoder_| on main thread.
+  void OnDecoderInitialized(media::AudioDecoderConfig config, bool success);
+  void OnDecodeOutput(const scoped_refptr<media::AudioBuffer>& decoded);
+  void OnDecodeStatus(media::DecodeStatus status);
+
+  // Called by |OnNewBuffers()| and |OnDecodeOutput()| on main thread.
+  void DecodeNow();
+
+  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
+  mojom::AssistantAudioDecoderClientPtr client_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  std::unique_ptr<media::MediaLog> media_log_;
+  std::unique_ptr<media::MPEG1AudioStreamParser> parser_;
+  std::unique_ptr<media::FFmpegAudioDecoder> decoder_;
+  base::circular_deque<scoped_refptr<media::StreamParserBuffer>>
+      parsed_buffers_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssistantAudioDecoder);
+};
+
+}  // namespace assistant
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_H_
diff --git a/chromeos/services/assistant/audio_decoder/assistant_audio_decoder_factory.cc b/chromeos/services/assistant/audio_decoder/assistant_audio_decoder_factory.cc
new file mode 100644
index 0000000..f6c624c
--- /dev/null
+++ b/chromeos/services/assistant/audio_decoder/assistant_audio_decoder_factory.cc
@@ -0,0 +1,28 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/assistant/audio_decoder/assistant_audio_decoder_factory.h"
+
+#include "chromeos/services/assistant/audio_decoder/assistant_audio_decoder.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace chromeos {
+namespace assistant {
+
+AssistantAudioDecoderFactory::AssistantAudioDecoderFactory(
+    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
+    : service_ref_(std::move(service_ref)) {}
+
+AssistantAudioDecoderFactory::~AssistantAudioDecoderFactory() = default;
+
+void AssistantAudioDecoderFactory::CreateAssistantAudioDecoder(
+    mojom::AssistantAudioDecoderRequest request,
+    mojom::AssistantAudioDecoderClientPtr client) {
+  mojo::MakeStrongBinding(std::make_unique<AssistantAudioDecoder>(
+                              service_ref_->Clone(), std::move(client)),
+                          std::move(request));
+}
+
+}  // namespace assistant
+}  // namespace chromeos
diff --git a/chromeos/services/assistant/audio_decoder/assistant_audio_decoder_factory.h b/chromeos/services/assistant/audio_decoder/assistant_audio_decoder_factory.h
new file mode 100644
index 0000000..3871766
--- /dev/null
+++ b/chromeos/services/assistant/audio_decoder/assistant_audio_decoder_factory.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 CHROMEOS_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_FACTORY_H_
+#define CHROMEOS_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_FACTORY_H_
+
+#include <memory>
+
+#include "chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+
+namespace chromeos {
+namespace assistant {
+
+class AssistantAudioDecoderFactory
+    : public mojom::AssistantAudioDecoderFactory {
+ public:
+  explicit AssistantAudioDecoderFactory(
+      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+  ~AssistantAudioDecoderFactory() override;
+
+ private:
+  // mojom::AssistantAudioDecoderFactory:
+  void CreateAssistantAudioDecoder(
+      mojom::AssistantAudioDecoderRequest request,
+      mojom::AssistantAudioDecoderClientPtr client) override;
+
+  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssistantAudioDecoderFactory);
+};
+
+}  // namespace assistant
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_FACTORY_H_
diff --git a/chromeos/services/assistant/audio_decoder/assistant_audio_decoder_service.cc b/chromeos/services/assistant/audio_decoder/assistant_audio_decoder_service.cc
new file mode 100644
index 0000000..7f82229b
--- /dev/null
+++ b/chromeos/services/assistant/audio_decoder/assistant_audio_decoder_service.cc
@@ -0,0 +1,50 @@
+// 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/audio_decoder/assistant_audio_decoder_service.h"
+
+#include "chromeos/services/assistant/audio_decoder/assistant_audio_decoder_factory.h"
+#include "chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace chromeos {
+namespace assistant {
+
+namespace {
+
+void OnAudioDecoderFactoryRequest(
+    service_manager::ServiceContextRefFactory* ref_factory,
+    mojom::AssistantAudioDecoderFactoryRequest request) {
+  mojo::MakeStrongBinding(
+      std::make_unique<AssistantAudioDecoderFactory>(ref_factory->CreateRef()),
+      std::move(request));
+}
+
+}  // namespace
+
+AssistantAudioDecoderService::AssistantAudioDecoderService() = default;
+
+AssistantAudioDecoderService::~AssistantAudioDecoderService() = default;
+
+std::unique_ptr<service_manager::Service>
+AssistantAudioDecoderService::CreateService() {
+  return std::make_unique<AssistantAudioDecoderService>();
+}
+
+void AssistantAudioDecoderService::OnStart() {
+  ref_factory_ = std::make_unique<service_manager::ServiceContextRefFactory>(
+      context()->CreateQuitClosure());
+  registry_.AddInterface(
+      base::BindRepeating(&OnAudioDecoderFactoryRequest, ref_factory_.get()));
+}
+
+void AssistantAudioDecoderService::OnBindInterface(
+    const service_manager::BindSourceInfo& source_info,
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe) {
+  registry_.BindInterface(interface_name, std::move(interface_pipe));
+}
+
+}  // namespace assistant
+}  // namespace chromeos
diff --git a/chromeos/services/assistant/audio_decoder/assistant_audio_decoder_service.h b/chromeos/services/assistant/audio_decoder/assistant_audio_decoder_service.h
new file mode 100644
index 0000000..4b93f2f
--- /dev/null
+++ b/chromeos/services/assistant/audio_decoder/assistant_audio_decoder_service.h
@@ -0,0 +1,42 @@
+// 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 CHROMEOS_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_SERVICE_H_
+#define CHROMEOS_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_SERVICE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/cpp/service_context.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+
+namespace chromeos {
+namespace assistant {
+
+class AssistantAudioDecoderService : public service_manager::Service {
+ public:
+  AssistantAudioDecoderService();
+  ~AssistantAudioDecoderService() override;
+
+  // Factory method for creating the service.
+  static std::unique_ptr<service_manager::Service> CreateService();
+
+ private:
+  // service_manager::Service overrides:
+  void OnStart() override;
+  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
+                       const std::string& interface_name,
+                       mojo::ScopedMessagePipeHandle interface_pipe) override;
+
+  std::unique_ptr<service_manager::ServiceContextRefFactory> ref_factory_;
+  service_manager::BinderRegistry registry_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssistantAudioDecoderService);
+};
+
+}  // namespace assistant
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_SERVICE_H_
diff --git a/chromeos/services/assistant/audio_decoder/manifest.json b/chromeos/services/assistant/audio_decoder/manifest.json
new file mode 100644
index 0000000..00a4858b
--- /dev/null
+++ b/chromeos/services/assistant/audio_decoder/manifest.json
@@ -0,0 +1,15 @@
+{
+  "name": "assistant_audio_decoder",
+  "display_name": "Assistant Audio Decoder Service",
+  "sandbox_type": "utility",
+  "interface_provider_specs": {
+    "service_manager:connector": {
+      "provides": {
+        "assistant:audio_decoder": [ "chromeos.assistant.mojom.AssistantAudioDecoderFactory" ]
+      },
+      "requires": {
+        "service_manager": [ "service_manager:all_users" ]
+      }
+    }
+  }
+}
diff --git a/chromeos/services/assistant/public/mojom/BUILD.gn b/chromeos/services/assistant/public/mojom/BUILD.gn
index 2da375ce..baf378c3 100644
--- a/chromeos/services/assistant/public/mojom/BUILD.gn
+++ b/chromeos/services/assistant/public/mojom/BUILD.gn
@@ -7,11 +7,13 @@
 mojom("mojom") {
   sources = [
     "assistant.mojom",
+    "assistant_audio_decoder.mojom",
     "constants.mojom",
     "settings.mojom",
   ]
 
   public_deps = [
+    "//media/mojo/interfaces",
     "//mojo/public/mojom/base",
     "//services/identity/public/mojom",
     "//ui/accessibility/mojom",
diff --git a/chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom b/chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom
new file mode 100644
index 0000000..5c09f56a
--- /dev/null
+++ b/chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom
@@ -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.
+
+module chromeos.assistant.mojom;
+
+import "media/mojo/interfaces/media_types.mojom";
+
+// A factory for creating an assistant audio decoder.
+interface AssistantAudioDecoderFactory {
+  // Creates an AssistantAudioDecoder. |client|'s methods will be called when
+  // certain events happen.
+  CreateAssistantAudioDecoder(AssistantAudioDecoder& audio_decoder,
+                              AssistantAudioDecoderClient client);
+};
+
+// Interface to communicate with assistant audio decoder service.
+interface AssistantAudioDecoder {
+  // Starts to parse and decode |data|.
+  Decode(array<uint8> data);
+};
+
+// Interface for assistant audio decoder service to call into client.
+interface AssistantAudioDecoderClient {
+  // Called when the decoder has been initialized.
+  OnDecoderInitialized(bool success,
+                       int32 samples_per_second,
+                       int32 channels);
+
+  // Called when new buffers have been parsed.
+  // |total_frames| is the total number of new buffers.
+  OnNewBuffers(int32 total_frames);
+
+  // Called when an audio buffer has been decoded.
+  OnBufferDecoded(media.mojom.AudioBuffer buffer);
+
+  // Called when there is error.
+  OnDecoderError();
+};
diff --git a/chromeos/services/assistant/public/mojom/constants.mojom b/chromeos/services/assistant/public/mojom/constants.mojom
index 01cdfe7..a284351 100644
--- a/chromeos/services/assistant/public/mojom/constants.mojom
+++ b/chromeos/services/assistant/public/mojom/constants.mojom
@@ -5,3 +5,7 @@
 module chromeos.assistant.mojom;
 
 const string kServiceName = "assistant";
+
+// The service name hosting the audio parser and decoder service for Chrome OS
+// native assistant.
+const string kAudioDecoderServiceName = "assistant_audio_decoder";
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 1b2d6387..8fcc581 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -114,6 +114,7 @@
     "//components/language/core/common:unit_tests",
     "//components/language_usage_metrics:unit_tests",
     "//components/leveldb_proto:unit_tests",
+    "//components/live_tab_count_metrics:unit_tests",
     "//components/login:unit_tests",
     "//components/metrics:unit_tests",
     "//components/navigation_metrics:unit_tests",
diff --git a/components/arc/common/BUILD.gn b/components/arc/common/BUILD.gn
index 4ac40443..edaf0349 100644
--- a/components/arc/common/BUILD.gn
+++ b/components/arc/common/BUILD.gn
@@ -81,9 +81,8 @@
   }
 }
 
-# Media related mojo interfaces. There are used by
-# //services/ui/public/interfaces. We have this separate mojom target to avoid
-# pulling in unnecessary interfaces,
+# Media related mojo interfaces. There are used by //services/ws/public/mojom.
+# We have this separate mojom target to avoid pulling in unnecessary interfaces.
 mojom("media") {
   sources = [
     "gfx.mojom",
diff --git a/components/drive/BUILD.gn b/components/drive/BUILD.gn
index 2d88976a..2431ae0 100644
--- a/components/drive/BUILD.gn
+++ b/components/drive/BUILD.gn
@@ -114,6 +114,7 @@
       "chromeos/drive_change_list_loader.h",
       "chromeos/drive_file_util.cc",
       "chromeos/drive_file_util.h",
+      "chromeos/drive_operation_queue.h",
       "chromeos/file_cache.cc",
       "chromeos/file_cache.h",
       "chromeos/file_system.cc",
diff --git a/components/drive/chromeos/drive_operation_queue.h b/components/drive/chromeos/drive_operation_queue.h
new file mode 100644
index 0000000..8a0b3cb
--- /dev/null
+++ b/components/drive/chromeos/drive_operation_queue.h
@@ -0,0 +1,179 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DRIVE_CHROMEOS_DRIVE_OPERATION_QUEUE_H_
+#define COMPONENTS_DRIVE_CHROMEOS_DRIVE_OPERATION_QUEUE_H_
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/containers/queue.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/drive/file_errors.h"
+
+namespace drive {
+namespace internal {
+
+// A rate limited queue for making calls to the drive backend. This is a basic
+// token based queue that will loosely rate limit requests to the qps specified
+// on construction. When enquing operations to an empty queue, the queue will
+// operate in burst mode and rapidly schedule up to |desired_qps| operations.
+template <typename T>
+class DriveBackgroundOperationQueue {
+ public:
+  // |tick_clock| can be injected for testing.
+  explicit DriveBackgroundOperationQueue(
+      int desired_qps,
+      const base::TickClock* tick_clock = base::DefaultTickClock::GetInstance())
+      : tick_clock_(tick_clock),
+        token_count_(desired_qps),
+        per_token_time_delta_(
+            base::TimeDelta::FromMilliseconds(1000 / desired_qps)),
+        desired_qps_(desired_qps),
+        weak_ptr_factory_(this) {}
+
+  ~DriveBackgroundOperationQueue() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  }
+
+  // Add a new operation to the queue. When the operation is scheduled to be
+  // executed start_callback will be called. When the operation completes
+  // finish_callback will be with the result.
+  void AddOperation(
+      base::WeakPtr<T> target,
+      base::OnceCallback<void(const FileOperationCallback&)> start_callback,
+      FileOperationCallback finish_callback) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK(start_callback);
+    DCHECK(finish_callback);
+
+    drive_operation_queue_.emplace(std::move(target), std::move(start_callback),
+                                   std::move(finish_callback));
+
+    CheckAndMaybeStartTokenRefillTimer();
+    CheckAndMaybeStartDriveOperation();
+  }
+
+  // Effectively disables the rate limiting for test purposes.
+  void DisableQueueForTesting() {
+    desired_qps_ = INT_MAX;
+    token_count_ = INT_MAX;
+  }
+
+ private:
+  struct DriveOperation;
+
+  using OperationQueue = base::queue<DriveOperation>;
+  using StartOperationCallback =
+      base::OnceCallback<void(const FileOperationCallback&)>;
+  using EndOperationCallback = FileOperationCallback;
+
+  struct DriveOperation {
+    DriveOperation(base::WeakPtr<T> target,
+                   StartOperationCallback start_callback,
+                   EndOperationCallback finish)
+        : target(std::move(target)),
+          start_callback(std::move(start_callback)),
+          finish_callback(std::move(finish)) {}
+
+    base::WeakPtr<T> target;
+    StartOperationCallback start_callback;
+    EndOperationCallback finish_callback;
+  };
+
+  void CheckAndMaybeStartDriveOperation() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    while (token_count_ > 0 && !drive_operation_queue_.empty()) {
+      auto next = std::move(drive_operation_queue_.front());
+      drive_operation_queue_.pop();
+
+      if (!next.target) {
+        continue;
+      }
+      base::SequencedTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE,
+          base::BindOnce(
+              std::move(next.start_callback),
+              base::BindRepeating(
+                  &DriveBackgroundOperationQueue<T>::OnDriveOperationComplete,
+                  weak_ptr_factory_.GetWeakPtr(),
+                  std::move(next.finish_callback))));
+      --token_count_;
+    }
+  }
+
+  void OnDriveOperationComplete(FileOperationCallback callback,
+                                FileError error) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    callback.Run(error);
+  }
+
+  void CheckAndMaybeStartTokenRefillTimer() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    if (token_refill_timer_.IsRunning()) {
+      return;
+    }
+
+    base::TimeDelta elapsed_since_stopped =
+        tick_clock_->NowTicks() - last_timer_stop_ticks_;
+    int additional_tokens = elapsed_since_stopped / per_token_time_delta_;
+
+    // Do not overflow when adding additional tokens.
+    if (desired_qps_ - token_count_ < additional_tokens) {
+      token_count_ = desired_qps_;
+    } else {
+      token_count_ += additional_tokens;
+    }
+
+    token_refill_timer_.Start(
+        FROM_HERE, per_token_time_delta_,
+        base::BindRepeating(&DriveBackgroundOperationQueue<T>::RefillTokens,
+                            weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  void RefillTokens() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    if (token_count_ < desired_qps_) {
+      ++token_count_;
+    }
+    CheckAndMaybeStartDriveOperation();
+
+    if (drive_operation_queue_.empty()) {
+      token_refill_timer_.Stop();
+      last_timer_stop_ticks_ = tick_clock_->NowTicks();
+    }
+  }
+
+  OperationQueue drive_operation_queue_;
+
+  // Token Bucket timer, to refill tokens if required.
+  const base::TickClock* tick_clock_;  // Not owned.
+  base::RepeatingTimer token_refill_timer_;
+  int token_count_;
+  const base::TimeDelta per_token_time_delta_;
+  int desired_qps_;
+  base::TimeTicks last_timer_stop_ticks_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<DriveBackgroundOperationQueue> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DriveBackgroundOperationQueue<T>);
+};
+
+}  // namespace internal
+}  // namespace drive
+
+#endif  // COMPONENTS_DRIVE_CHROMEOS_DRIVE_OPERATION_QUEUE_H_
diff --git a/components/drive/chromeos/file_system.cc b/components/drive/chromeos/file_system.cc
index e922cfac..e9c840e1 100644
--- a/components/drive/chromeos/file_system.cc
+++ b/components/drive/chromeos/file_system.cc
@@ -48,6 +48,9 @@
 namespace drive {
 namespace {
 
+// Desired QPS for team drive background operations (update checking etc)
+constexpr int kTeamDriveBackgroundOperationQPS = 10;
+
 // Gets a ResourceEntry from the metadata, and overwrites its file info when the
 // cached file is dirty.
 FileError GetLocallyStoredResourceEntry(
@@ -344,6 +347,11 @@
       blocking_task_runner_.get(), delegate, scheduler_, resource_metadata_,
       cache_, loader_controller_.get(), temporary_file_directory_);
 
+  team_drive_operation_queue_ =
+      std::make_unique<internal::DriveBackgroundOperationQueue<
+          internal::TeamDriveChangeListLoader>>(
+          kTeamDriveBackgroundOperationQPS);
+
   copy_operation_ = std::make_unique<file_system::CopyOperation>(
       blocking_task_runner_.get(), delegate, scheduler_, resource_metadata_,
       cache_);
@@ -392,7 +400,10 @@
                                     weak_ptr_factory_.GetWeakPtr()));
 
   for (auto& team_drive : team_drive_change_list_loaders_) {
-    team_drive.second->CheckForUpdates(
+    team_drive_operation_queue_->AddOperation(
+        team_drive.second->GetWeakPtr(),
+        base::BindOnce(&internal::TeamDriveChangeListLoader::CheckForUpdates,
+                       team_drive.second->GetWeakPtr()),
         base::Bind(&FileSystem::OnUpdateChecked, weak_ptr_factory_.GetWeakPtr(),
                    team_drive.first, closure));
   }
@@ -896,7 +907,11 @@
             blocking_task_runner_.get(), resource_metadata_, scheduler_,
             loader_controller_.get());
         loader->AddChangeListLoaderObserver(this);
-        loader->LoadIfNeeded(base::DoNothing());
+        team_drive_operation_queue_->AddOperation(
+            loader->GetWeakPtr(),
+            base::BindOnce(&internal::TeamDriveChangeListLoader::LoadIfNeeded,
+                           loader->GetWeakPtr()),
+            base::DoNothing());
         team_drive_change_list_loaders_.emplace(change.team_drive_id(),
                                                 std::move(loader));
       }
@@ -934,7 +949,11 @@
         blocking_task_runner_.get(), resource_metadata_, scheduler_,
         loader_controller_.get());
     loader->AddChangeListLoaderObserver(this);
-    loader->LoadIfNeeded(base::DoNothing());
+    team_drive_operation_queue_->AddOperation(
+        loader->GetWeakPtr(),
+        base::BindOnce(&internal::TeamDriveChangeListLoader::LoadIfNeeded,
+                       loader->GetWeakPtr()),
+        base::DoNothing());
     team_drive_change_list_loaders_.emplace(team_drive.team_drive_id(),
                                             std::move(loader));
   }
diff --git a/components/drive/chromeos/file_system.h b/components/drive/chromeos/file_system.h
index 5d7c63c..7a88875 100644
--- a/components/drive/chromeos/file_system.h
+++ b/components/drive/chromeos/file_system.h
@@ -17,6 +17,7 @@
 #include "base/observer_list.h"
 #include "base/threading/thread_checker.h"
 #include "components/drive/chromeos/change_list_loader_observer.h"
+#include "components/drive/chromeos/drive_operation_queue.h"
 #include "components/drive/chromeos/file_system/operation_delegate.h"
 #include "components/drive/chromeos/file_system_interface.h"
 #include "components/drive/chromeos/team_drive_change_list_loader.h"
@@ -199,6 +200,11 @@
   }
   internal::SyncClient* sync_client_for_testing() { return sync_client_.get(); }
 
+  internal::DriveBackgroundOperationQueue<internal::TeamDriveChangeListLoader>*
+  team_drive_operation_queue_for_testing() {
+    return team_drive_operation_queue_.get();
+  }
+
  private:
   struct CreateDirectoryParams;
 
@@ -289,6 +295,10 @@
   std::unique_ptr<internal::DriveChangeListLoader>
       default_corpus_change_list_loader_;
 
+  std::unique_ptr<internal::DriveBackgroundOperationQueue<
+      internal::TeamDriveChangeListLoader>>
+      team_drive_operation_queue_;
+
   // Used to retrieve changelists for team drives. The key for the map is the
   // team_drive_id.
   std::map<std::string, std::unique_ptr<internal::TeamDriveChangeListLoader>>
diff --git a/components/drive/chromeos/team_drive_change_list_loader.cc b/components/drive/chromeos/team_drive_change_list_loader.cc
index 042fc01..ec39f83 100644
--- a/components/drive/chromeos/team_drive_change_list_loader.cc
+++ b/components/drive/chromeos/team_drive_change_list_loader.cc
@@ -41,7 +41,9 @@
     ResourceMetadata* resource_metadata,
     JobScheduler* scheduler,
     LoaderController* apply_task_controller)
-    : team_drive_id_(team_drive_id), root_entry_path_(root_entry_path) {
+    : team_drive_id_(team_drive_id),
+      root_entry_path_(root_entry_path),
+      weak_ptr_factory_(this) {
   root_folder_id_loader_ =
       std::make_unique<ConstantRootFolderIdLoader>(team_drive_id_);
 
@@ -63,6 +65,11 @@
 
 TeamDriveChangeListLoader::~TeamDriveChangeListLoader() = default;
 
+base::WeakPtr<TeamDriveChangeListLoader>
+TeamDriveChangeListLoader::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
 // DriveChangeListLoader overrides
 void TeamDriveChangeListLoader::AddChangeListLoaderObserver(
     ChangeListLoaderObserver* observer) {
diff --git a/components/drive/chromeos/team_drive_change_list_loader.h b/components/drive/chromeos/team_drive_change_list_loader.h
index c01ab6f..5888932 100644
--- a/components/drive/chromeos/team_drive_change_list_loader.h
+++ b/components/drive/chromeos/team_drive_change_list_loader.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "components/drive/chromeos/change_list_loader_observer.h"
 #include "components/drive/chromeos/drive_change_list_loader.h"
@@ -44,6 +45,7 @@
   ~TeamDriveChangeListLoader() override;
 
   const base::FilePath& root_entry_path() const { return root_entry_path_; }
+  base::WeakPtr<TeamDriveChangeListLoader> GetWeakPtr();
 
   // DriveChangeListLoader overrides
   void AddChangeListLoaderObserver(ChangeListLoaderObserver* observer) override;
@@ -77,6 +79,8 @@
 
   THREAD_CHECKER(thread_checker_);
 
+  base::WeakPtrFactory<TeamDriveChangeListLoader> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(TeamDriveChangeListLoader);
 };
 
diff --git a/components/drive/drive_operation_queue_unittest.cc b/components/drive/drive_operation_queue_unittest.cc
new file mode 100644
index 0000000..3d70b0a8
--- /dev/null
+++ b/components/drive/drive_operation_queue_unittest.cc
@@ -0,0 +1,299 @@
+// 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 "components/drive/chromeos/drive_operation_queue.h"
+#include "base/memory/weak_ptr.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "components/drive/file_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace drive {
+namespace internal {
+
+namespace {
+
+constexpr int kDesiredQPS = 5;
+constexpr base::TimeDelta kTokenRefreshRate =
+    base::TimeDelta::FromSeconds(1) / kDesiredQPS;
+
+class FakeDriveOperation {
+ public:
+  FakeDriveOperation() : weak_ptr_factory_(this) {}
+
+  base::WeakPtr<FakeDriveOperation> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+  void FakeOperation(const FileOperationCallback& callback) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    CHECK(!task_has_executed);
+
+    task_has_executed = true;
+    callback.Run(FILE_ERROR_OK);
+  }
+
+  bool HasTaskExecuted() const { return task_has_executed; }
+
+ private:
+  bool task_has_executed = false;
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<FakeDriveOperation> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeDriveOperation);
+};
+
+}  // namespace
+
+class DriveOperationQueueTest : public testing::Test {
+ protected:
+  using OperationQueue = DriveBackgroundOperationQueue<FakeDriveOperation>;
+
+  void SetUp() override {
+    task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
+        base::TestMockTimeTaskRunner::Type::kBoundToThread);
+    operation_queue_ =
+        std::make_unique<DriveBackgroundOperationQueue<FakeDriveOperation>>(
+            kDesiredQPS, task_runner_->GetMockTickClock());
+  }
+
+  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+  std::unique_ptr<OperationQueue> operation_queue_;
+};
+
+// Put up to |kDesiredQPS| entries in queue, all should run without the timer
+// needing to advance.
+TEST_F(DriveOperationQueueTest, BurstMode) {
+  std::vector<std::unique_ptr<FakeDriveOperation>> operations;
+
+  for (int i = 0; i < kDesiredQPS; ++i) {
+    operations.emplace_back(std::make_unique<FakeDriveOperation>());
+  }
+
+  int callbacks_run = 0;
+
+  for (auto& operation : operations) {
+    operation_queue_->AddOperation(
+        operation->GetWeakPtr(),
+        base::BindOnce(&FakeDriveOperation::FakeOperation,
+                       operation->GetWeakPtr()),
+        base::BindLambdaForTesting(
+            [&callbacks_run](drive::FileError error) { ++callbacks_run; }));
+  }
+
+  // Execute all pending tasks without advancing virtual time.
+  task_runner_->RunUntilIdle();
+
+  ASSERT_EQ(callbacks_run, kDesiredQPS);
+
+  for (auto& operation : operations) {
+    ASSERT_TRUE(operation->HasTaskExecuted());
+  }
+}
+
+// Ensure that tokens are refilled as the timer advances.
+TEST_F(DriveOperationQueueTest, RefillTokens) {
+  constexpr int kAdditionalTasks = 5;
+  constexpr int kTasksToQueue = kDesiredQPS + kAdditionalTasks;
+
+  std::vector<std::unique_ptr<FakeDriveOperation>> operations;
+
+  for (int i = 0; i < kTasksToQueue; ++i) {
+    operations.emplace_back(std::make_unique<FakeDriveOperation>());
+  }
+
+  int callbacks_run = 0;
+
+  for (int i = 0; i < kTasksToQueue; ++i) {
+    operation_queue_->AddOperation(
+        operations[i]->GetWeakPtr(),
+        base::BindOnce(&FakeDriveOperation::FakeOperation,
+                       operations[i]->GetWeakPtr()),
+        base::BindLambdaForTesting(
+            [&callbacks_run](drive::FileError) { ++callbacks_run; }));
+  }
+
+  // Execute all pending tasks without advancing virtual time.
+  task_runner_->RunUntilIdle();
+
+  // We should only have advanced through the burst mode tasks.
+  ASSERT_EQ(callbacks_run, kDesiredQPS);
+
+  // Move time forward, should cause a token to be refreshed and an operation to
+  // run.
+  int last_callbacks_run = callbacks_run;
+  for (int i = kDesiredQPS; i < kTasksToQueue; ++i) {
+    ASSERT_FALSE(operations[i]->HasTaskExecuted());
+
+    task_runner_->FastForwardBy(kTokenRefreshRate);
+
+    ASSERT_EQ(callbacks_run, last_callbacks_run + 1);
+    ASSERT_TRUE(operations[i]->HasTaskExecuted());
+    last_callbacks_run = callbacks_run;
+  }
+
+  ASSERT_EQ(kTasksToQueue, callbacks_run);
+}
+
+// Ensure that invalidated tasks are not run, but also do not block the queue.
+TEST_F(DriveOperationQueueTest, InvalidateTasks) {
+  constexpr int kTasksToInvalidate = kDesiredQPS * 2;
+  constexpr int kAdditionalTasks = 5;
+  constexpr int kTasksToQueue =
+      kDesiredQPS + kAdditionalTasks + kTasksToInvalidate;
+
+  std::vector<std::unique_ptr<FakeDriveOperation>> operations;
+
+  for (int i = 0; i < kTasksToQueue; ++i) {
+    operations.emplace_back(std::make_unique<FakeDriveOperation>());
+  }
+
+  int callbacks_run = 0;
+
+  for (int i = 0; i < kTasksToQueue; ++i) {
+    operation_queue_->AddOperation(
+        operations[i]->GetWeakPtr(),
+        base::BindOnce(&FakeDriveOperation::FakeOperation,
+                       operations[i]->GetWeakPtr()),
+        base::BindLambdaForTesting(
+            [&callbacks_run](drive::FileError) { ++callbacks_run; }));
+  }
+
+  // Execute all pending tasks without advancing virtual time.
+  task_runner_->RunUntilIdle();
+
+  // We should only have advanced through the burst mode tasks.
+  ASSERT_EQ(callbacks_run, kDesiredQPS);
+
+  // Erase tasks from the queue, which will invalidate their weak ptrs and
+  // prevent callbacks from being run.
+  operations.erase(operations.begin(),
+                   operations.begin() + callbacks_run + kTasksToInvalidate);
+
+  // Fast forward until all tasks have been executed.
+  int last_callbacks_run = callbacks_run;
+  task_runner_->FastForwardUntilNoTasksRemain();
+
+  // All expected tasks should now have been run.
+  ASSERT_EQ(last_callbacks_run + kAdditionalTasks, callbacks_run);
+}
+
+// Ensure that tasks added after burst mode has finished are only executed
+// at the desired QPS.
+TEST_F(DriveOperationQueueTest, DoNotRepeatBurstMode) {
+  constexpr int kTasksToCreate = kDesiredQPS * 2;
+
+  std::vector<std::unique_ptr<FakeDriveOperation>> operations;
+
+  for (int i = 0; i < kTasksToCreate; ++i) {
+    operations.emplace_back(std::make_unique<FakeDriveOperation>());
+  }
+
+  int callbacks_run = 0;
+
+  // Only run the burst mode tasks.
+  for (int i = 0; i < kDesiredQPS; ++i) {
+    operation_queue_->AddOperation(
+        operations[i]->GetWeakPtr(),
+        base::BindOnce(&FakeDriveOperation::FakeOperation,
+                       operations[i]->GetWeakPtr()),
+        base::BindLambdaForTesting(
+            [&callbacks_run](drive::FileError error) { ++callbacks_run; }));
+  }
+
+  // Execute all pending tasks without advancing virtual time.
+  task_runner_->FastForwardBy(kTokenRefreshRate);
+
+  ASSERT_EQ(callbacks_run, kDesiredQPS);
+
+  // Add the remaining tasks to the queue, only the first should run as we've
+  // only advanced 1 refresh period.
+  for (int i = kDesiredQPS; i < kTasksToCreate; ++i) {
+    operation_queue_->AddOperation(
+        operations[i]->GetWeakPtr(),
+        base::BindOnce(&FakeDriveOperation::FakeOperation,
+                       operations[i]->GetWeakPtr()),
+        base::BindLambdaForTesting(
+            [&callbacks_run](drive::FileError error) { ++callbacks_run; }));
+  }
+
+  int last_callbacks_run = callbacks_run;
+  // Just run the tasks that are scheduled now.
+  task_runner_->RunUntilIdle();
+  ASSERT_EQ(last_callbacks_run + 1, callbacks_run);
+
+  // Run the rest of the tasks.
+  task_runner_->FastForwardUntilNoTasksRemain();
+  ASSERT_EQ(kTasksToCreate, callbacks_run);
+}
+
+// Ensure that burst mode is re-enabled if more than 1 second has advanced since
+// the last task finished.
+TEST_F(DriveOperationQueueTest, RepeatBurstMode) {
+  constexpr int kTasksToCreate = kDesiredQPS * 3;
+
+  std::vector<std::unique_ptr<FakeDriveOperation>> operations;
+
+  for (int i = 0; i < kTasksToCreate; ++i) {
+    operations.emplace_back(std::make_unique<FakeDriveOperation>());
+  }
+
+  int callbacks_run = 0;
+
+  // Only run the burst mode tasks.
+  for (int i = 0; i < kDesiredQPS; ++i) {
+    operation_queue_->AddOperation(
+        operations[i]->GetWeakPtr(),
+        base::BindOnce(&FakeDriveOperation::FakeOperation,
+                       operations[i]->GetWeakPtr()),
+        base::BindLambdaForTesting(
+            [&callbacks_run](drive::FileError error) { ++callbacks_run; }));
+  }
+
+  // Execute all pending tasks.
+  task_runner_->FastForwardUntilNoTasksRemain();
+  ASSERT_EQ(kDesiredQPS, callbacks_run);
+
+  // Move forward two seconds, so that we should reset burst mode.
+  task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
+
+  // Add the remaining tasks to the queue, all should run in burst mode.
+  for (int i = kDesiredQPS; i < kTasksToCreate; ++i) {
+    operation_queue_->AddOperation(
+        operations[i]->GetWeakPtr(),
+        base::BindOnce(&FakeDriveOperation::FakeOperation,
+                       operations[i]->GetWeakPtr()),
+        base::BindLambdaForTesting(
+            [&callbacks_run](drive::FileError error) { ++callbacks_run; }));
+  }
+
+  callbacks_run = 0;
+
+  // Execute all pending tasks without advancing virtual time.
+  task_runner_->RunUntilIdle();
+
+  // We should only have advanced through the burst mode tasks.
+  ASSERT_EQ(kDesiredQPS, callbacks_run);
+
+  // The last tasks should not have been run yet.
+  for (int i = 2 * kDesiredQPS; i < kTasksToCreate; ++i) {
+    ASSERT_FALSE(operations[i]->HasTaskExecuted());
+  }
+
+  // Run the remaining tasks, which should happen at desired qps
+  base::TimeTicks start_ticks = task_runner_->NowTicks();
+  task_runner_->FastForwardUntilNoTasksRemain();
+
+  ASSERT_LE(base::TimeDelta::FromSeconds(1),
+            task_runner_->NowTicks() - start_ticks);
+
+  // All tasks have been run.
+  for (auto& operation : operations) {
+    ASSERT_TRUE(operation->HasTaskExecuted());
+  }
+}
+
+}  // namespace internal
+}  // namespace drive
diff --git a/components/drive/file_system_unittest.cc b/components/drive/file_system_unittest.cc
index f617406..1c60cc0 100644
--- a/components/drive/file_system_unittest.cc
+++ b/components/drive/file_system_unittest.cc
@@ -153,6 +153,9 @@
     // Disable delaying so that the sync starts immediately.
     file_system_->sync_client_for_testing()->set_delay_for_testing(
         base::TimeDelta::FromSeconds(0));
+
+    file_system_->team_drive_operation_queue_for_testing()
+        ->DisableQueueForTesting();
   }
 
   // Loads the full resource list via FakeDriveService.
@@ -873,6 +876,7 @@
 
   // Notify the update to the file system.
   file_system_->CheckForUpdates();
+  base::RunLoop().RunUntilIdle();
 
   std::unique_ptr<ResourceEntryVector> entries(ReadDirectorySync(
       base::FilePath::FromUTF8Unsafe("drive/team_drives/team_drive_1")));
@@ -902,6 +906,7 @@
 
   // Notify the update to the file system, which will add the team drive
   file_system_->CheckForUpdates();
+  base::RunLoop().RunUntilIdle();
 
   std::unique_ptr<ResourceEntryVector> entries(
       ReadDirectorySync(base::FilePath::FromUTF8Unsafe("drive/team_drives/")));
@@ -929,6 +934,7 @@
 
   // Notify the update to the file system.
   file_system_->CheckForUpdates();
+  base::RunLoop().RunUntilIdle();
 
   entries = ReadDirectorySync(
       base::FilePath::FromUTF8Unsafe("drive/team_drives/team_drive_3"));
diff --git a/components/exo/BUILD.gn b/components/exo/BUILD.gn
index 755fe0e..d2e6fbe 100644
--- a/components/exo/BUILD.gn
+++ b/components/exo/BUILD.gn
@@ -80,7 +80,7 @@
     "//device/gamepad/public/cpp:shared_with_blink",
     "//gpu",
     "//gpu/command_buffer/client:gles2_interface",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//ui/aura",
     "//ui/base/ime",
diff --git a/components/exo/DEPS b/components/exo/DEPS
index 2fc1da6..0ef6c813 100644
--- a/components/exo/DEPS
+++ b/components/exo/DEPS
@@ -7,7 +7,7 @@
   "+device/gamepad",
   "+gpu",
   "+mojo/public/cpp",
-  "+services/ui/public/interfaces",
+  "+services/ws/public/mojom",
   "+third_party/khronos",
   "+third_party/skia",
   "+ui",
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc
index e3d4678d..d8d42cd 100644
--- a/components/exo/client_controlled_shell_surface.cc
+++ b/components/exo/client_controlled_shell_surface.cc
@@ -34,7 +34,7 @@
 #include "base/trace_event/trace_event_argument.h"
 #include "components/exo/surface.h"
 #include "components/exo/wm_helper.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index 7000261..6d020b6 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -28,7 +28,7 @@
 #include "cc/trees/layer_tree_frame_sink.h"
 #include "components/exo/surface.h"
 #include "components/exo/wm_helper.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/cursor_client.h"
diff --git a/components/exo/surface.cc b/components/exo/surface.cc
index 7d462deb..d979ea07 100644
--- a/components/exo/surface.cc
+++ b/components/exo/surface.cc
@@ -25,7 +25,7 @@
 #include "components/viz/common/resources/single_release_callback.h"
 #include "components/viz/service/surfaces/surface.h"
 #include "components/viz/service/surfaces/surface_manager.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/drag_drop_delegate.h"
diff --git a/components/exo/surface_tree_host.cc b/components/exo/surface_tree_host.cc
index bcbc1dcf..0495fb0d 100644
--- a/components/exo/surface_tree_host.cc
+++ b/components/exo/surface_tree_host.cc
@@ -13,7 +13,7 @@
 #include "components/exo/wm_helper.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
diff --git a/components/feed/core/BUILD.gn b/components/feed/core/BUILD.gn
index 3122e9c7..3b2175cf 100644
--- a/components/feed/core/BUILD.gn
+++ b/components/feed/core/BUILD.gn
@@ -30,8 +30,6 @@
     "feed_networking_host.h",
     "feed_scheduler_host.cc",
     "feed_scheduler_host.h",
-    "feed_storage_database.cc",
-    "feed_storage_database.h",
     "pref_names.cc",
     "pref_names.h",
     "refresh_throttler.cc",
@@ -85,7 +83,6 @@
     "feed_journal_mutation_unittest.cc",
     "feed_networking_host_unittest.cc",
     "feed_scheduler_host_unittest.cc",
-    "feed_storage_database_unittest.cc",
     "refresh_throttler_unittest.cc",
     "user_classifier_unittest.cc",
   ]
diff --git a/components/feed/core/feed_host_service.cc b/components/feed/core/feed_host_service.cc
index 65fc019..3525d2d 100644
--- a/components/feed/core/feed_host_service.cc
+++ b/components/feed/core/feed_host_service.cc
@@ -12,13 +12,11 @@
     std::unique_ptr<FeedImageManager> image_manager,
     std::unique_ptr<FeedNetworkingHost> networking_host,
     std::unique_ptr<FeedSchedulerHost> scheduler_host,
-    std::unique_ptr<FeedStorageDatabase> storage_database,
     std::unique_ptr<FeedContentDatabase> content_database,
     std::unique_ptr<FeedJournalDatabase> journal_database)
     : image_manager_(std::move(image_manager)),
       networking_host_(std::move(networking_host)),
       scheduler_host_(std::move(scheduler_host)),
-      storage_database_(std::move(storage_database)),
       content_database_(std::move(content_database)),
       journal_database_(std::move(journal_database)) {}
 
@@ -36,10 +34,6 @@
   return scheduler_host_.get();
 }
 
-FeedStorageDatabase* FeedHostService::GetStorageDatabase() {
-  return storage_database_.get();
-}
-
 FeedContentDatabase* FeedHostService::GetContentDatabase() {
   return content_database_.get();
 }
diff --git a/components/feed/core/feed_host_service.h b/components/feed/core/feed_host_service.h
index cb8723c9..7ae0eaa 100644
--- a/components/feed/core/feed_host_service.h
+++ b/components/feed/core/feed_host_service.h
@@ -13,7 +13,6 @@
 #include "components/feed/core/feed_journal_database.h"
 #include "components/feed/core/feed_networking_host.h"
 #include "components/feed/core/feed_scheduler_host.h"
-#include "components/feed/core/feed_storage_database.h"
 #include "components/keyed_service/core/keyed_service.h"
 
 namespace feed {
@@ -28,7 +27,6 @@
   FeedHostService(std::unique_ptr<FeedImageManager> image_manager,
                   std::unique_ptr<FeedNetworkingHost> networking_host,
                   std::unique_ptr<FeedSchedulerHost> scheduler_host,
-                  std::unique_ptr<FeedStorageDatabase> storage_database,
                   std::unique_ptr<FeedContentDatabase> content_database,
                   std::unique_ptr<FeedJournalDatabase> journal_database);
   ~FeedHostService() override;
@@ -36,7 +34,6 @@
   FeedImageManager* GetImageManager();
   FeedNetworkingHost* GetNetworkingHost();
   FeedSchedulerHost* GetSchedulerHost();
-  FeedStorageDatabase* GetStorageDatabase();
   FeedContentDatabase* GetContentDatabase();
   FeedJournalDatabase* GetJournalDatabase();
 
@@ -44,7 +41,6 @@
   std::unique_ptr<FeedImageManager> image_manager_;
   std::unique_ptr<FeedNetworkingHost> networking_host_;
   std::unique_ptr<FeedSchedulerHost> scheduler_host_;
-  std::unique_ptr<FeedStorageDatabase> storage_database_;
   std::unique_ptr<FeedContentDatabase> content_database_;
   std::unique_ptr<FeedJournalDatabase> journal_database_;
 
diff --git a/components/feed/core/feed_storage_database.cc b/components/feed/core/feed_storage_database.cc
deleted file mode 100644
index 4ea3da4..0000000
--- a/components/feed/core/feed_storage_database.cc
+++ /dev/null
@@ -1,467 +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 "components/feed/core/feed_storage_database.h"
-
-#include <unordered_set>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/strings/string_util.h"
-#include "base/sys_info.h"
-#include "base/task/post_task.h"
-#include "components/feed/core/proto/feed_storage.pb.h"
-#include "components/leveldb_proto/proto_database_impl.h"
-
-namespace feed {
-
-namespace {
-using StorageEntryVector =
-    leveldb_proto::ProtoDatabase<FeedStorageProto>::KeyEntryVector;
-
-// Statistics are logged to UMA with this string as part of histogram name. They
-// can all be found under LevelDB.*.FeedStorageDatabase. Changing this needs to
-// synchronize with histograms.xml, AND will also become incompatible with older
-// browsers still reporting the previous values.
-const char kStorageDatabaseUMAClientName[] = "FeedStorageDatabase";
-
-const char kStorageDatabaseFolder[] = "storage";
-
-const size_t kDatabaseWriteBufferSizeBytes = 512 * 1024;
-const size_t kDatabaseWriteBufferSizeBytesForLowEndDevice = 128 * 1024;
-
-// Key prefixes for content's storage key and journal's storage key. Because we
-// put both content data and journal data into one storage, we need to add
-// prefixes to their keys to distinguish between content keys and journal keys.
-const char kContentStoragePrefix[] = "cs-";
-const char kJournalStoragePrefix[] = "js-";
-
-// Formats content key to storage key by adding a prefix.
-std::string FormatContentKeyToStorageKey(const std::string& content_key) {
-  return kContentStoragePrefix + content_key;
-}
-
-// Formats journal key to storage key by adding a prefix.
-std::string FormatJournalKeyToStorageKey(const std::string& journal_key) {
-  return kJournalStoragePrefix + journal_key;
-}
-
-// Check if the |storage_key| is with |prefix|.
-bool IsKeywithPrefix(const std::string& storage_key, const char prefix[]) {
-  return base::StartsWith(storage_key, prefix, base::CompareCase::SENSITIVE);
-}
-
-// Check if the |storage_key| is for content data.
-bool IsValidContentKey(const std::string& storage_key) {
-  return IsKeywithPrefix(storage_key, kContentStoragePrefix);
-}
-
-// Check if the |storage_key| is for journal data.
-bool IsValidJournalKey(const std::string& storage_key) {
-  return IsKeywithPrefix(storage_key, kJournalStoragePrefix);
-}
-
-// Help function |ParseContentKey| and |ParseJournalKey| to parse storage key by
-// remove |prefix| from |storage_key|.
-std::string ParseKeyHelper(const std::string& storage_key,
-                           const char prefix[]) {
-  if (!IsKeywithPrefix(storage_key, prefix)) {
-    return std::string();
-  }
-
-  return storage_key.substr(strlen(prefix));
-}
-
-// Parse content key from storage key. Return an empty string if |storage_key|
-// is not recognized as content key. ex. journal's storage key.
-std::string ParseContentKey(const std::string& storage_key) {
-  return ParseKeyHelper(storage_key, kContentStoragePrefix);
-}
-
-// Parse journal key from storage key. Return an empty string if |storage_key|
-// is not recognized as journal key. ex. content's storage key.
-std::string ParseJournalKey(const std::string& storage_key) {
-  return ParseKeyHelper(storage_key, kJournalStoragePrefix);
-}
-
-bool DatabaseKeyFilter(const std::unordered_set<std::string>& key_set,
-                       const std::string& key) {
-  return key_set.find(key) != key_set.end();
-}
-
-bool DatabasePrefixFilter(const std::string& key_prefix,
-                          const std::string& key) {
-  return base::StartsWith(key, key_prefix, base::CompareCase::SENSITIVE);
-}
-
-}  // namespace
-
-FeedStorageDatabase::FeedStorageDatabase(const base::FilePath& database_folder)
-    : FeedStorageDatabase(
-          database_folder,
-          std::make_unique<leveldb_proto::ProtoDatabaseImpl<FeedStorageProto>>(
-              base::CreateSequencedTaskRunnerWithTraits(
-                  {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-                   base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))) {}
-
-FeedStorageDatabase::FeedStorageDatabase(
-    const base::FilePath& database_folder,
-    std::unique_ptr<leveldb_proto::ProtoDatabase<FeedStorageProto>>
-        storage_database)
-    : database_status_(UNINITIALIZED),
-      storage_database_(std::move(storage_database)),
-      weak_ptr_factory_(this) {
-  leveldb_env::Options options = leveldb_proto::CreateSimpleOptions();
-  if (base::SysInfo::IsLowEndDevice()) {
-    options.write_buffer_size = kDatabaseWriteBufferSizeBytesForLowEndDevice;
-  } else {
-    options.write_buffer_size = kDatabaseWriteBufferSizeBytes;
-  }
-
-  base::FilePath storage_folder =
-      database_folder.AppendASCII(kStorageDatabaseFolder);
-  storage_database_->Init(
-      kStorageDatabaseUMAClientName, storage_folder, options,
-      base::BindOnce(&FeedStorageDatabase::OnDatabaseInitialized,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-FeedStorageDatabase::~FeedStorageDatabase() = default;
-
-bool FeedStorageDatabase::IsInitialized() const {
-  return INITIALIZED == database_status_;
-}
-
-void FeedStorageDatabase::LoadContent(const std::vector<std::string>& keys,
-                                      ContentLoadCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  std::unordered_set<std::string> key_set;
-  for (const auto& key : keys) {
-    key_set.insert(FormatContentKeyToStorageKey(key));
-  }
-
-  storage_database_->LoadEntriesWithFilter(
-      base::BindRepeating(&DatabaseKeyFilter, std::move(key_set)),
-      base::BindOnce(&FeedStorageDatabase::OnLoadEntriesForLoadContent,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void FeedStorageDatabase::LoadContentByPrefix(const std::string& prefix,
-                                              ContentLoadCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  std::string key_prefix = FormatContentKeyToStorageKey(prefix);
-
-  storage_database_->LoadEntriesWithFilter(
-      base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)),
-      base::BindOnce(&FeedStorageDatabase::OnLoadEntriesForLoadContent,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void FeedStorageDatabase::LoadAllContentKeys(ContentKeyCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  storage_database_->LoadKeys(
-      base::BindOnce(&FeedStorageDatabase::OnLoadKeysForLoadAllContentKeys,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void FeedStorageDatabase::SaveContent(std::vector<KeyAndData> pairs,
-                                      ConfirmationCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  auto contents_to_save = std::make_unique<StorageEntryVector>();
-  for (auto entry : pairs) {
-    FeedStorageProto proto;
-    proto.set_key(std::move(entry.first));
-    proto.set_content_data(std::move(entry.second));
-    contents_to_save->emplace_back(FormatContentKeyToStorageKey(proto.key()),
-                                   std::move(proto));
-  }
-
-  storage_database_->UpdateEntries(
-      std::move(contents_to_save), std::make_unique<std::vector<std::string>>(),
-      base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void FeedStorageDatabase::DeleteContent(
-    const std::vector<std::string>& keys_to_delete,
-    ConfirmationCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  auto content_to_delete = std::make_unique<std::vector<std::string>>();
-  for (const auto& key : keys_to_delete) {
-    content_to_delete->emplace_back(FormatContentKeyToStorageKey(key));
-  }
-  storage_database_->UpdateEntries(
-      std::make_unique<StorageEntryVector>(), std::move(content_to_delete),
-      base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void FeedStorageDatabase::DeleteContentByPrefix(
-    const std::string& prefix_to_delete,
-    ConfirmationCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  std::string key_prefix = FormatContentKeyToStorageKey(prefix_to_delete);
-  storage_database_->UpdateEntriesWithRemoveFilter(
-      std::make_unique<StorageEntryVector>(),
-      base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)),
-      base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void FeedStorageDatabase::DeleteAllContent(ConfirmationCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  std::string key_prefix = FormatContentKeyToStorageKey(std::string());
-  storage_database_->UpdateEntriesWithRemoveFilter(
-      std::make_unique<StorageEntryVector>(),
-      base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)),
-      base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void FeedStorageDatabase::LoadJournal(const std::string& key,
-                                      JournalLoadCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  storage_database_->GetEntry(
-      FormatJournalKeyToStorageKey(key),
-      base::BindOnce(&FeedStorageDatabase::OnGetEntryForLoadJournal,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void FeedStorageDatabase::DoesJournalExist(const std::string& key,
-                                           ConfirmationCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  storage_database_->GetEntry(
-      FormatJournalKeyToStorageKey(key),
-      base::BindOnce(&FeedStorageDatabase::OnGetEntryForDoesJournalExist,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void FeedStorageDatabase::LoadAllJournalKeys(JournalLoadCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  storage_database_->LoadKeys(
-      base::BindOnce(&FeedStorageDatabase::OnLoadKeysForLoadAllJournalKeys,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void FeedStorageDatabase::AppendToJournal(const std::string& key,
-                                          std::vector<std::string> entries,
-                                          ConfirmationCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  storage_database_->GetEntry(
-      FormatJournalKeyToStorageKey(key),
-      base::BindOnce(&FeedStorageDatabase::OnGetEntryAppendToJournal,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback), key,
-                     std::move(entries)));
-}
-
-void FeedStorageDatabase::CopyJournal(const std::string& from_key,
-                                      const std::string& to_key,
-                                      ConfirmationCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  storage_database_->GetEntry(
-      FormatJournalKeyToStorageKey(from_key),
-      base::BindOnce(&FeedStorageDatabase::OnGetEntryForCopyJournal,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
-                     to_key));
-}
-
-void FeedStorageDatabase::DeleteJournal(const std::string& key,
-                                        ConfirmationCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  auto journals_to_delete = std::make_unique<std::vector<std::string>>();
-  journals_to_delete->push_back(FormatJournalKeyToStorageKey(key));
-
-  storage_database_->UpdateEntries(
-      std::make_unique<StorageEntryVector>(), std::move(journals_to_delete),
-      base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void FeedStorageDatabase::DeleteAllJournals(ConfirmationCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  std::string key_prefix = FormatJournalKeyToStorageKey(std::string());
-  storage_database_->UpdateEntriesWithRemoveFilter(
-      std::make_unique<StorageEntryVector>(),
-      base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)),
-      base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void FeedStorageDatabase::OnDatabaseInitialized(bool success) {
-  DCHECK_EQ(database_status_, UNINITIALIZED);
-
-  if (success) {
-    database_status_ = INITIALIZED;
-  } else {
-    database_status_ = INIT_FAILURE;
-    DVLOG(1) << "FeedStorageDatabase init failed.";
-  }
-}
-
-void FeedStorageDatabase::OnLoadEntriesForLoadContent(
-    ContentLoadCallback callback,
-    bool success,
-    std::unique_ptr<std::vector<FeedStorageProto>> content) {
-  std::vector<KeyAndData> results;
-
-  if (!success || !content) {
-    DVLOG_IF(1, !success) << "FeedStorageDatabase load content failed.";
-    std::move(callback).Run(std::move(results));
-    return;
-  }
-
-  for (const auto& proto : *content) {
-    DCHECK(proto.has_key());
-    DCHECK(proto.has_content_data());
-
-    results.emplace_back(proto.key(), proto.content_data());
-  }
-
-  std::move(callback).Run(std::move(results));
-}
-
-void FeedStorageDatabase::OnLoadKeysForLoadAllContentKeys(
-    ContentKeyCallback callback,
-    bool success,
-    std::unique_ptr<std::vector<std::string>> keys) {
-  std::vector<std::string> results;
-
-  if (!success || !keys) {
-    DVLOG_IF(1, !success) << "FeedStorageDatabase load content keys failed.";
-    std::move(callback).Run(std::move(results));
-    return;
-  }
-
-  // Filter out journal keys, only keep content keys.
-  for (const std::string& key : *keys) {
-    if (IsValidContentKey(key))
-      results.emplace_back(ParseContentKey(key));
-  }
-
-  std::move(callback).Run(std::move(results));
-}
-
-void FeedStorageDatabase::OnGetEntryForLoadJournal(
-    JournalLoadCallback callback,
-    bool success,
-    std::unique_ptr<FeedStorageProto> journal) {
-  std::vector<std::string> results;
-
-  if (!success || !journal) {
-    DVLOG_IF(1, !success) << "FeedStorageDatabase load journal failed.";
-    std::move(callback).Run(std::move(results));
-    return;
-  }
-
-  for (int i = 0; i < journal->journal_data_size(); ++i) {
-    results.emplace_back(journal->journal_data(i));
-  }
-
-  std::move(callback).Run(std::move(results));
-}
-
-void FeedStorageDatabase::OnGetEntryForDoesJournalExist(
-    ConfirmationCallback callback,
-    bool success,
-    std::unique_ptr<FeedStorageProto> journal) {
-  DVLOG_IF(1, !success) << "FeedStorageDatabase load journal failed.";
-  std::move(callback).Run(journal != nullptr);
-}
-
-void FeedStorageDatabase::OnGetEntryAppendToJournal(
-    ConfirmationCallback callback,
-    const std::string& key,
-    std::vector<std::string> entries,
-    bool success,
-    std::unique_ptr<FeedStorageProto> journal) {
-  if (!success) {
-    DVLOG(1) << "FeedStorageDatabase load journal failed.";
-    std::move(callback).Run(success);
-    return;
-  }
-
-  if (journal == nullptr) {
-    // The journal does not exist, create a new one.
-    journal = std::make_unique<FeedStorageProto>();
-    journal->set_key(key);
-  }
-  DCHECK_EQ(journal->key(), key);
-
-  for (const std::string& entry : entries) {
-    journal->add_journal_data(entry);
-  }
-  auto journals_to_save = std::make_unique<StorageEntryVector>();
-  journals_to_save->emplace_back(FormatJournalKeyToStorageKey(key),
-                                 std::move(*journal));
-
-  storage_database_->UpdateEntries(
-      std::move(journals_to_save), std::make_unique<std::vector<std::string>>(),
-      base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void FeedStorageDatabase::OnGetEntryForCopyJournal(
-    ConfirmationCallback callback,
-    const std::string& to_key,
-    bool success,
-    std::unique_ptr<FeedStorageProto> journal) {
-  if (!success || !journal) {
-    DVLOG_IF(1, !success) << "FeedStorageDatabase load journal failed.";
-    std::move(callback).Run(success);
-    return;
-  }
-
-  journal->set_key(to_key);
-  auto journal_to_save = std::make_unique<StorageEntryVector>();
-  journal_to_save->emplace_back(FormatJournalKeyToStorageKey(to_key),
-                                std::move(*journal));
-
-  storage_database_->UpdateEntries(
-      std::move(journal_to_save), std::make_unique<std::vector<std::string>>(),
-      base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void FeedStorageDatabase::OnLoadKeysForLoadAllJournalKeys(
-    JournalLoadCallback callback,
-    bool success,
-    std::unique_ptr<std::vector<std::string>> keys) {
-  std::vector<std::string> results;
-
-  if (!success || !keys) {
-    DVLOG_IF(1, !success) << "FeedStorageDatabase load journal keys failed.";
-    std::move(callback).Run(std::move(results));
-    return;
-  }
-
-  // Filter out content keys, only keep journal keys.
-  for (const std::string& key : *keys) {
-    if (IsValidJournalKey(key))
-      results.emplace_back(ParseJournalKey(key));
-  }
-
-  std::move(callback).Run(std::move(results));
-}
-
-void FeedStorageDatabase::OnStorageCommitted(ConfirmationCallback callback,
-                                             bool success) {
-  DVLOG_IF(1, !success) << "FeedStorageDatabase committed failed.";
-  std::move(callback).Run(success);
-}
-
-}  // namespace feed
diff --git a/components/feed/core/feed_storage_database.h b/components/feed/core/feed_storage_database.h
deleted file mode 100644
index 9c080be..0000000
--- a/components/feed/core/feed_storage_database.h
+++ /dev/null
@@ -1,177 +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 COMPONENTS_FEED_CORE_FEED_STORAGE_DATABASE_H_
-#define COMPONENTS_FEED_CORE_FEED_STORAGE_DATABASE_H_
-
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/memory/weak_ptr.h"
-#include "base/sequenced_task_runner.h"
-#include "components/leveldb_proto/proto_database.h"
-
-namespace feed {
-
-class FeedStorageProto;
-
-// FeedStorageDatabase is leveldb backed store for feed's content storage data
-// and jounal storage data.
-class FeedStorageDatabase {
- public:
-  enum State {
-    UNINITIALIZED,
-    INITIALIZED,
-    INIT_FAILURE,
-  };
-
-  using KeyAndData = std::pair<std::string, std::string>;
-
-  // Returns the storage data as a vector of key-value pairs when calling
-  // loading data.
-  using ContentLoadCallback = base::OnceCallback<void(std::vector<KeyAndData>)>;
-
-  // Returns the content keys as a vector when calling loading all content keys.
-  using ContentKeyCallback = base::OnceCallback<void(std::vector<std::string>)>;
-
-  // Returns the journal data as a vector of strings when calling loading data
-  // or keys.
-  using JournalLoadCallback =
-      base::OnceCallback<void(std::vector<std::string>)>;
-
-  // Returns whether the commit operation succeeded when calling for database
-  // operations, or return whether the entry exists when calling for checking
-  // the entry's existence.
-  using ConfirmationCallback = base::OnceCallback<void(bool)>;
-
-  // Initializes the database with |database_folder|.
-  explicit FeedStorageDatabase(const base::FilePath& database_folder);
-
-  // Initializes the database with |database_folder|. Creates storage using the
-  // given |storage_database| for local storage. Useful for testing.
-  FeedStorageDatabase(
-      const base::FilePath& database_folder,
-      std::unique_ptr<leveldb_proto::ProtoDatabase<FeedStorageProto>>
-          storage_database);
-
-  ~FeedStorageDatabase();
-
-  // Returns true if initialization has finished successfully, else false.
-  // While this is false, initialization may already started, or initialization
-  // failed.
-  bool IsInitialized() const;
-
-  // Loads the content data for the |keys| and passes them to |callback|.
-  void LoadContent(const std::vector<std::string>& keys,
-                   ContentLoadCallback callback);
-
-  // Loads the content data whose key matches |prefix|, and passes them to
-  // |callback|.
-  void LoadContentByPrefix(const std::string& prefix,
-                           ContentLoadCallback callback);
-
-  // Loads all content keys in the storage, and passes them to |callback|.
-  void LoadAllContentKeys(ContentKeyCallback callback);
-
-  // Inserts or updates the content data |pairs|, |callback| will be called when
-  // the data are saved or if there is an error. The fields in |pairs| will be
-  // std::move.
-  void SaveContent(std::vector<KeyAndData> pairs,
-                   ConfirmationCallback callback);
-
-  // Deletes the content data for |keys_to_delete|, |callback| will be called
-  // when the data are deleted or if there is an error.
-  void DeleteContent(const std::vector<std::string>& keys_to_delete,
-                     ConfirmationCallback callback);
-
-  // Deletes the content data whose key matches |prefix_to_delete|, |callback|
-  // will be called when the content are deleted or if there is an error.
-  void DeleteContentByPrefix(const std::string& prefix_to_delete,
-                             ConfirmationCallback callback);
-
-  // Delete all content, |callback| will be called when all content is deleted
-  // or if there is an error.
-  void DeleteAllContent(ConfirmationCallback callback);
-
-  // Loads the journal data for the |key| and passes it to |callback|.
-  void LoadJournal(const std::string& key, JournalLoadCallback callback);
-
-  // Checks if the journal for the |key| exists, and return the result to
-  // |callback|.
-  void DoesJournalExist(const std::string& key, ConfirmationCallback callback);
-
-  // Loads all journal keys in the storage, and passes them to |callback|.
-  void LoadAllJournalKeys(JournalLoadCallback callback);
-
-  // Appends |entries| to a journal whose key is |key|, if there the journal do
-  // not exist, create one. |callback| will be called when the data are saved or
-  // if there is an error.
-  void AppendToJournal(const std::string& key,
-                       std::vector<std::string> entries,
-                       ConfirmationCallback callback);
-
-  // Creates a new journal with name |to_key|, and copys all data from the
-  // journal with |from_key| to it. |callback| will be called when the data are
-  // saved or if there is an error.
-  void CopyJournal(const std::string& from_key,
-                   const std::string& to_key,
-                   ConfirmationCallback callback);
-
-  // Deletes the journal with |key|, |callback| will be called when the journal
-  // is deleted or if there is an error.
-  void DeleteJournal(const std::string& key, ConfirmationCallback callback);
-
-  // Delete all journals, |callback| will be called when all journals are
-  // deleted or if there is an error.
-  void DeleteAllJournals(ConfirmationCallback callback);
-
- private:
-  // Callback methods given to |storage_database_| for async responses.
-  void OnDatabaseInitialized(bool success);
-  void OnLoadEntriesForLoadContent(
-      ContentLoadCallback callback,
-      bool success,
-      std::unique_ptr<std::vector<FeedStorageProto>> content);
-  void OnLoadKeysForLoadAllContentKeys(
-      ContentKeyCallback callback,
-      bool success,
-      std::unique_ptr<std::vector<std::string>> keys);
-  void OnGetEntryForLoadJournal(JournalLoadCallback callback,
-                                bool success,
-                                std::unique_ptr<FeedStorageProto> journal);
-  void OnGetEntryForDoesJournalExist(ConfirmationCallback callback,
-                                     bool success,
-                                     std::unique_ptr<FeedStorageProto> journal);
-  void OnGetEntryAppendToJournal(ConfirmationCallback callback,
-                                 const std::string& key,
-                                 std::vector<std::string> entries,
-                                 bool success,
-                                 std::unique_ptr<FeedStorageProto> journal);
-  void OnGetEntryForCopyJournal(ConfirmationCallback callback,
-                                const std::string& to_key,
-                                bool success,
-                                std::unique_ptr<FeedStorageProto> journal);
-  void OnLoadKeysForLoadAllJournalKeys(
-      JournalLoadCallback callback,
-      bool success,
-      std::unique_ptr<std::vector<std::string>> keys);
-  void OnStorageCommitted(ConfirmationCallback callback, bool success);
-
-  State database_status_;
-
-  std::unique_ptr<leveldb_proto::ProtoDatabase<FeedStorageProto>>
-      storage_database_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  base::WeakPtrFactory<FeedStorageDatabase> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(FeedStorageDatabase);
-};
-
-}  // namespace feed
-
-#endif  // COMPONENTS_FEED_CORE_FEED_STORAGE_DATABASE_H_
diff --git a/components/feed/core/feed_storage_database_unittest.cc b/components/feed/core/feed_storage_database_unittest.cc
deleted file mode 100644
index a1d71f26..0000000
--- a/components/feed/core/feed_storage_database_unittest.cc
+++ /dev/null
@@ -1,625 +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 "components/feed/core/feed_storage_database.h"
-
-#include <map>
-
-#include "base/test/scoped_task_environment.h"
-#include "components/feed/core/proto/feed_storage.pb.h"
-#include "components/feed/core/time_serialization.h"
-#include "components/leveldb_proto/testing/fake_db.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using leveldb_proto::test::FakeDB;
-using testing::Mock;
-using testing::NotNull;
-using testing::_;
-
-namespace feed {
-
-namespace {
-const std::string kContentKeyPrefix = "ContentKey";
-const std::string kContentKey1 = "ContentKey1";
-const std::string kContentKey2 = "ContentKey2";
-const std::string kContentKey3 = "ContentKey3";
-const std::string kContentData1 = "Content Data1";
-const std::string kContentData2 = "Content Data2";
-const std::string kContentData3 = "Content Data3";
-const std::string kJournalKey1 = "JournalKey1";
-const std::string kJournalKey2 = "JournalKey2";
-const std::string kJournalKey3 = "JournalKey3";
-const std::string kJournalData1 = "Journal Data1";
-const std::string kJournalData2 = "Journal Data2";
-const std::string kJournalData3 = "Journal Data3";
-const std::string kJournalData4 = "Journal Data4";
-const std::string kJournalData5 = "Journal Data5";
-const std::string kJournalData6 = "Journal Data6";
-}  // namespace
-
-class FeedStorageDatabaseTest : public testing::Test {
- public:
-  FeedStorageDatabaseTest() : storage_db_(nullptr) {}
-
-  void CreateDatabase(bool init_database) {
-    // The FakeDBs are owned by |feed_db_|, so clear our pointers before
-    // resetting |feed_db_| itself.
-    storage_db_ = nullptr;
-    // Explicitly destroy any existing database before creating a new one.
-    feed_db_.reset();
-
-    auto storage_db =
-        std::make_unique<FakeDB<FeedStorageProto>>(&storage_db_storage_);
-
-    storage_db_ = storage_db.get();
-    feed_db_ = std::make_unique<FeedStorageDatabase>(base::FilePath(),
-                                                     std::move(storage_db));
-    if (init_database) {
-      storage_db_->InitCallback(true);
-      ASSERT_TRUE(db()->IsInitialized());
-    }
-  }
-
-  void InjectContentStorageProto(const std::string& key,
-                                 const std::string& data) {
-    FeedStorageProto storage_proto;
-    storage_proto.set_key(key);
-    storage_proto.set_content_data(data);
-    storage_db_storage_["cs-" + key] = storage_proto;
-  }
-
-  void InjectJournalStorageProto(const std::string& key,
-                                 const std::vector<std::string>& entries) {
-    FeedStorageProto storage_proto;
-    storage_proto.set_key(key);
-    for (const std::string& entry : entries) {
-      storage_proto.add_journal_data(entry);
-    }
-    storage_db_storage_["js-" + key] = storage_proto;
-  }
-
-  void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
-
-  FakeDB<FeedStorageProto>* storage_db() { return storage_db_; }
-
-  FeedStorageDatabase* db() { return feed_db_.get(); }
-
-  MOCK_METHOD1(OnContentEntriesReceived,
-               void(std::vector<std::pair<std::string, std::string>>));
-  MOCK_METHOD1(OnContentKeyReceived, void(std::vector<std::string>));
-  MOCK_METHOD1(OnJournalEntryReceived, void(std::vector<std::string>));
-  MOCK_METHOD1(JournalExist, void(bool));
-  MOCK_METHOD1(OnStorageCommitted, void(bool));
-
- private:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-
-  std::map<std::string, FeedStorageProto> storage_db_storage_;
-
-  // Owned by |feed_db_|.
-  FakeDB<FeedStorageProto>* storage_db_;
-
-  std::unique_ptr<FeedStorageDatabase> feed_db_;
-
-  DISALLOW_COPY_AND_ASSIGN(FeedStorageDatabaseTest);
-};
-
-TEST_F(FeedStorageDatabaseTest, Init) {
-  ASSERT_FALSE(db());
-
-  CreateDatabase(/*init_database=*/false);
-
-  storage_db()->InitCallback(true);
-  EXPECT_TRUE(db()->IsInitialized());
-}
-
-TEST_F(FeedStorageDatabaseTest, LoadContentAfterInitSuccess) {
-  CreateDatabase(/*init_database=*/true);
-
-  EXPECT_CALL(*this, OnContentEntriesReceived(_));
-  db()->LoadContent(
-      {kContentKey1},
-      base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
-                     base::Unretained(this)));
-  storage_db()->LoadCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, LoadContentsEntries) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Store |kContentKey1| and |kContentKey2|.
-  InjectContentStorageProto(kContentKey1, kContentData1);
-  InjectContentStorageProto(kContentKey2, kContentData2);
-  InjectJournalStorageProto(kJournalKey1,
-                            {kJournalData1, kJournalData2, kJournalData3});
-  InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
-  InjectJournalStorageProto(kJournalKey3, {kJournalData6});
-
-  // Try to Load |kContentKey2| and |kContentKey3|, only |kContentKey2| should
-  // return.
-  EXPECT_CALL(*this, OnContentEntriesReceived(_))
-      .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
-        ASSERT_EQ(results.size(), 1U);
-        EXPECT_EQ(results[0].first, kContentKey2);
-        EXPECT_EQ(results[0].second, kContentData2);
-      });
-  db()->LoadContent(
-      {kContentKey2, kContentKey3},
-      base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
-                     base::Unretained(this)));
-  storage_db()->LoadCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, LoadContentsEntriesByPrefix) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Store |kContentKey1|, |kContentKey2|, |kJournalKey1|, |kJournalKey2|,
-  // |kJournalKey3|.
-  InjectContentStorageProto(kContentKey1, kContentData1);
-  InjectContentStorageProto(kContentKey2, kContentData2);
-  InjectJournalStorageProto(kJournalKey1,
-                            {kJournalData1, kJournalData2, kJournalData3});
-  InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
-  InjectJournalStorageProto(kJournalKey3, {kJournalData6});
-
-  // Try to Load "ContentKey", both |kContentKey1| and |kContentKey2| should
-  // return.
-  EXPECT_CALL(*this, OnContentEntriesReceived(_))
-      .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
-        ASSERT_EQ(results.size(), 2U);
-        EXPECT_EQ(results[0].first, kContentKey1);
-        EXPECT_EQ(results[0].second, kContentData1);
-        EXPECT_EQ(results[1].first, kContentKey2);
-        EXPECT_EQ(results[1].second, kContentData2);
-      });
-  db()->LoadContentByPrefix(
-      kContentKeyPrefix,
-      base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
-                     base::Unretained(this)));
-  storage_db()->LoadCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, LoadAllContentKeys) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Store |kContentKey1|, |kContentKey2|, |kJournalKey1|, |kJournalKey2|,
-  // |kJournalKey3|.
-  InjectContentStorageProto(kContentKey1, kContentData1);
-  InjectContentStorageProto(kContentKey2, kContentData2);
-  InjectJournalStorageProto(kJournalKey1,
-                            {kJournalData1, kJournalData2, kJournalData3});
-  InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
-  InjectJournalStorageProto(kJournalKey3, {kJournalData6});
-
-  EXPECT_CALL(*this, OnContentKeyReceived(_))
-      .WillOnce([](std::vector<std::string> results) {
-        ASSERT_EQ(results.size(), 2U);
-        EXPECT_EQ(results[0], kContentKey1);
-        EXPECT_EQ(results[1], kContentKey2);
-      });
-  db()->LoadAllContentKeys(base::BindOnce(
-      &FeedStorageDatabaseTest::OnContentKeyReceived, base::Unretained(this)));
-  storage_db()->LoadKeysCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, SaveContent) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Store |kContentKey1|, |kContentKey2|, |kJournalKey1|, |kJournalKey2|,
-  // |kJournalKey3|.
-  std::vector<std::pair<std::string, std::string>> entries;
-  entries.push_back(std::make_pair(kContentKey1, kContentData1));
-  entries.push_back(std::make_pair(kContentKey2, kContentData2));
-  EXPECT_CALL(*this, OnStorageCommitted(true));
-  db()->SaveContent(std::move(entries),
-                    base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
-                                   base::Unretained(this)));
-  storage_db()->UpdateCallback(true);
-
-  // Make sure they're there.
-  EXPECT_CALL(*this, OnContentEntriesReceived(_))
-      .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
-        ASSERT_EQ(results.size(), 2U);
-        EXPECT_EQ(results[0].first, kContentKey1);
-        EXPECT_EQ(results[0].second, kContentData1);
-        EXPECT_EQ(results[1].first, kContentKey2);
-        EXPECT_EQ(results[1].second, kContentData2);
-      });
-  db()->LoadContent(
-      {kContentKey1, kContentKey2},
-      base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
-                     base::Unretained(this)));
-  storage_db()->LoadCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, DeleteContent) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Store |kContentKey1| and |kContentKey2|.
-  InjectContentStorageProto(kContentKey1, kContentData1);
-  InjectContentStorageProto(kContentKey2, kContentData2);
-
-  // Delete |kContentKey2| and |kContentKey3|
-  std::vector<std::string> keys;
-  keys.push_back(kContentKey2);
-  keys.push_back(kContentKey3);
-  EXPECT_CALL(*this, OnStorageCommitted(true));
-  db()->DeleteContent(
-      std::move(keys),
-      base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
-                     base::Unretained(this)));
-  storage_db()->UpdateCallback(true);
-
-  // Make sure only |kContentKey2| got deleted.
-  EXPECT_CALL(*this, OnContentEntriesReceived(_))
-      .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
-        EXPECT_EQ(results.size(), 1U);
-        EXPECT_EQ(results[0].first, kContentKey1);
-        EXPECT_EQ(results[0].second, kContentData1);
-      });
-  db()->LoadContent(
-      {kContentKey1, kContentKey2},
-      base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
-                     base::Unretained(this)));
-  storage_db()->LoadCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, DeleteContentByPrefix) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Store |kContentKey1| and |kContentKey2|.
-  InjectContentStorageProto(kContentKey1, kContentData1);
-  InjectContentStorageProto(kContentKey2, kContentData2);
-
-  // Delete |kContentKey1| and |kContentKey2|
-  EXPECT_CALL(*this, OnStorageCommitted(true));
-  db()->DeleteContentByPrefix(
-      kContentKeyPrefix,
-      base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
-                     base::Unretained(this)));
-  storage_db()->UpdateCallback(true);
-
-  // Make sure |kContentKey1| and |kContentKey2| got deleted.
-  EXPECT_CALL(*this, OnContentEntriesReceived(_))
-      .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
-        EXPECT_EQ(results.size(), 0U);
-      });
-  db()->LoadContent(
-      {kContentKey1, kContentKey2},
-      base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
-                     base::Unretained(this)));
-  storage_db()->LoadCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, DeleteAllContent) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Store |kContentKey1| and |kContentKey2|.
-  InjectContentStorageProto(kContentKey1, kContentData1);
-  InjectContentStorageProto(kContentKey2, kContentData2);
-
-  // Store |kJournalKey1|, |kJournalKey2|, |kJournalKey3|.
-  InjectJournalStorageProto(kJournalKey1,
-                            {kJournalData1, kJournalData2, kJournalData3});
-  InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
-  InjectJournalStorageProto(kJournalKey3, {kJournalData6});
-
-  // Delete all content, meaning |kContentKey1| and |kContentKey2| are expected
-  // to be deleted.
-  EXPECT_CALL(*this, OnStorageCommitted(true));
-  db()->DeleteAllContent(base::BindOnce(
-      &FeedStorageDatabaseTest::OnStorageCommitted, base::Unretained(this)));
-  storage_db()->UpdateCallback(true);
-
-  // Make sure |kContentKey1| and |kContentKey2| got deleted.
-  EXPECT_CALL(*this, OnContentEntriesReceived(_))
-      .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
-        EXPECT_EQ(results.size(), 0U);
-      });
-  db()->LoadContent(
-      {kContentKey1, kContentKey2},
-      base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
-                     base::Unretained(this)));
-  storage_db()->LoadCallback(true);
-
-  // Make sure all journals are there.
-  EXPECT_CALL(*this, OnJournalEntryReceived(_))
-      .WillOnce([](std::vector<std::string> results) {
-        ASSERT_EQ(results.size(), 3U);
-      });
-  db()->LoadAllJournalKeys(
-      base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
-                     base::Unretained(this)));
-  storage_db()->LoadKeysCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, LoadJournalEntry) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Store |kJournalKey1|.
-  InjectJournalStorageProto(kJournalKey1,
-                            {kJournalData1, kJournalData2, kJournalData3});
-
-  // Try to Load |kJournalKey1|.
-  EXPECT_CALL(*this, OnJournalEntryReceived(_))
-      .WillOnce([](std::vector<std::string> results) {
-        ASSERT_EQ(results.size(), 3U);
-        EXPECT_EQ(results[0], kJournalData1);
-        EXPECT_EQ(results[1], kJournalData2);
-        EXPECT_EQ(results[2], kJournalData3);
-      });
-  db()->LoadJournal(
-      kJournalKey1,
-      base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
-                     base::Unretained(this)));
-  storage_db()->GetCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, LoadNonExistingJournalEntry) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Try to Load |kJournalKey1|.
-  EXPECT_CALL(*this, OnJournalEntryReceived(_))
-      .WillOnce([](std::vector<std::string> results) {
-        ASSERT_EQ(results.size(), 0U);
-      });
-  db()->LoadJournal(
-      kJournalKey1,
-      base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
-                     base::Unretained(this)));
-  storage_db()->GetCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, DoesJournalExist) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Store |kJournalKey1|.
-  InjectJournalStorageProto(kJournalKey1,
-                            {kJournalData1, kJournalData2, kJournalData3});
-
-  // Check if |kJournalKey1| exists.
-  EXPECT_CALL(*this, JournalExist(true));
-  db()->DoesJournalExist(kJournalKey1,
-                         base::BindOnce(&FeedStorageDatabaseTest::JournalExist,
-                                        base::Unretained(this)));
-  storage_db()->GetCallback(true);
-
-  Mock::VerifyAndClearExpectations(this);
-
-  // Check if |kJournalKey2| exists.
-  EXPECT_CALL(*this, JournalExist(false));
-  db()->DoesJournalExist(kJournalKey2,
-                         base::BindOnce(&FeedStorageDatabaseTest::JournalExist,
-                                        base::Unretained(this)));
-  storage_db()->GetCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, LoadAllJournalKeys) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Store |kContentKey1|, |kContentKey2|, |kJournalKey1|, |kJournalKey2|,
-  // |kJournalKey3|.
-  InjectContentStorageProto(kContentKey1, kContentData1);
-  InjectContentStorageProto(kContentKey2, kContentData2);
-  InjectJournalStorageProto(kJournalKey1,
-                            {kJournalData1, kJournalData2, kJournalData3});
-  InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
-  InjectJournalStorageProto(kJournalKey3, {kJournalData6});
-
-  EXPECT_CALL(*this, OnJournalEntryReceived(_))
-      .WillOnce([](std::vector<std::string> results) {
-        ASSERT_EQ(results.size(), 3U);
-        EXPECT_EQ(results[0], kJournalKey1);
-        EXPECT_EQ(results[1], kJournalKey2);
-        EXPECT_EQ(results[2], kJournalKey3);
-      });
-  db()->LoadAllJournalKeys(
-      base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
-                     base::Unretained(this)));
-  storage_db()->LoadKeysCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, AppendToJournal_WhenJournalExists) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Save |kContentKey1|
-  EXPECT_CALL(*this, OnStorageCommitted(true));
-  db()->AppendToJournal(
-      kJournalKey1, {kJournalData1, kJournalData2},
-      base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
-                     base::Unretained(this)));
-  storage_db()->GetCallback(true);
-  storage_db()->UpdateCallback(true);
-
-  // Make sure they're there.
-  EXPECT_CALL(*this, OnJournalEntryReceived(_))
-      .WillOnce([](std::vector<std::string> results) {
-        ASSERT_EQ(results.size(), 2U);
-        EXPECT_EQ(results[0], kJournalData1);
-        EXPECT_EQ(results[1], kJournalData2);
-      });
-  db()->LoadJournal(
-      kJournalKey1,
-      base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
-                     base::Unretained(this)));
-  storage_db()->GetCallback(true);
-
-  Mock::VerifyAndClearExpectations(this);
-
-  // Append more for |kContentKey1|
-  EXPECT_CALL(*this, OnStorageCommitted(true));
-  db()->AppendToJournal(
-      kJournalKey1, {kJournalData3, kJournalData4, kJournalData5},
-      base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
-                     base::Unretained(this)));
-  storage_db()->GetCallback(true);
-  storage_db()->UpdateCallback(true);
-
-  // Check new instances are there.
-  EXPECT_CALL(*this, OnJournalEntryReceived(_))
-      .WillOnce([](std::vector<std::string> results) {
-        ASSERT_EQ(results.size(), 5U);
-        EXPECT_EQ(results[0], kJournalData1);
-        EXPECT_EQ(results[1], kJournalData2);
-        EXPECT_EQ(results[2], kJournalData3);
-        EXPECT_EQ(results[3], kJournalData4);
-        EXPECT_EQ(results[4], kJournalData5);
-      });
-  db()->LoadJournal(
-      kJournalKey1,
-      base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
-                     base::Unretained(this)));
-  storage_db()->GetCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, AppendToJournal_WhenJournalMissing) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Append data for |kContentKey1|
-  EXPECT_CALL(*this, OnStorageCommitted(true));
-  db()->AppendToJournal(
-      kJournalKey1, {kJournalData1, kJournalData2, kJournalData3},
-      base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
-                     base::Unretained(this)));
-  storage_db()->GetCallback(true);
-  storage_db()->UpdateCallback(true);
-
-  // Check new data are there.
-  EXPECT_CALL(*this, OnJournalEntryReceived(_))
-      .WillOnce([](std::vector<std::string> results) {
-        ASSERT_EQ(results.size(), 3U);
-        EXPECT_EQ(results[0], kJournalData1);
-        EXPECT_EQ(results[1], kJournalData2);
-        EXPECT_EQ(results[2], kJournalData3);
-      });
-  db()->LoadJournal(
-      kJournalKey1,
-      base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
-                     base::Unretained(this)));
-  storage_db()->GetCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, CopyJournal) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Save |kContentKey1|.
-  InjectJournalStorageProto(kJournalKey1,
-                            {kJournalData1, kJournalData2, kJournalData3});
-
-  // Copy |kContentKey1| to |kContentKey2|.
-  EXPECT_CALL(*this, OnStorageCommitted(true));
-  db()->CopyJournal(kJournalKey1, kJournalKey2,
-                    base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
-                                   base::Unretained(this)));
-  storage_db()->GetCallback(true);
-  storage_db()->UpdateCallback(true);
-
-  // Check new journal is there.
-  EXPECT_CALL(*this, OnJournalEntryReceived(_))
-      .WillOnce([](std::vector<std::string> results) {
-        ASSERT_EQ(results.size(), 3U);
-        EXPECT_EQ(results[0], kJournalData1);
-        EXPECT_EQ(results[1], kJournalData2);
-        EXPECT_EQ(results[2], kJournalData3);
-      });
-  db()->LoadJournal(
-      kJournalKey2,
-      base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
-                     base::Unretained(this)));
-  storage_db()->GetCallback(true);
-
-  Mock::VerifyAndClearExpectations(this);
-
-  // Check first journal is still there.
-  EXPECT_CALL(*this, OnJournalEntryReceived(_))
-      .WillOnce([](std::vector<std::string> results) {
-        ASSERT_EQ(results.size(), 3U);
-        EXPECT_EQ(results[0], kJournalData1);
-        EXPECT_EQ(results[1], kJournalData2);
-        EXPECT_EQ(results[2], kJournalData3);
-      });
-  db()->LoadJournal(
-      kJournalKey1,
-      base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
-                     base::Unretained(this)));
-  storage_db()->GetCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, DeleteJournal) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Store |kJournalKey1|, |kJournalKey2|, |kJournalKey3|.
-  InjectJournalStorageProto(kJournalKey1,
-                            {kJournalData1, kJournalData2, kJournalData3});
-  InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
-  InjectJournalStorageProto(kJournalKey3, {kJournalData6});
-
-  // Delete |kJournalKey2|.
-  EXPECT_CALL(*this, OnStorageCommitted(true));
-  db()->DeleteJournal(
-      kJournalKey2, base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
-                                   base::Unretained(this)));
-  storage_db()->UpdateCallback(true);
-
-  // Make sure |kJournalKey2| got deleted.
-  EXPECT_CALL(*this, OnJournalEntryReceived(_))
-      .WillOnce([](std::vector<std::string> results) {
-        ASSERT_EQ(results.size(), 2U);
-        EXPECT_EQ(results[0], kJournalKey1);
-        EXPECT_EQ(results[1], kJournalKey3);
-      });
-  db()->LoadAllJournalKeys(
-      base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
-                     base::Unretained(this)));
-  storage_db()->LoadKeysCallback(true);
-}
-
-TEST_F(FeedStorageDatabaseTest, DeleteAllJournals) {
-  CreateDatabase(/*init_database=*/true);
-
-  // Store |kContentKey1| and |kContentKey2|.
-  InjectContentStorageProto(kContentKey1, kContentData1);
-  InjectContentStorageProto(kContentKey2, kContentData2);
-
-  // Store |kJournalKey1|, |kJournalKey2|, |kJournalKey3|.
-  InjectJournalStorageProto(kJournalKey1,
-                            {kJournalData1, kJournalData2, kJournalData3});
-  InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
-  InjectJournalStorageProto(kJournalKey3, {kJournalData6});
-
-  // Delete all journals, meaning |kJournalKey1|, |kJournalKey2| and
-  // |kJournalKey3| are expected to be deleted.
-  EXPECT_CALL(*this, OnStorageCommitted(true));
-  db()->DeleteAllJournals(base::BindOnce(
-      &FeedStorageDatabaseTest::OnStorageCommitted, base::Unretained(this)));
-  storage_db()->UpdateCallback(true);
-
-  // Make sure all journals got deleted.
-  EXPECT_CALL(*this, OnJournalEntryReceived(_))
-      .WillOnce([](std::vector<std::string> results) {
-        ASSERT_EQ(results.size(), 0U);
-      });
-  db()->LoadAllJournalKeys(
-      base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
-                     base::Unretained(this)));
-  storage_db()->LoadKeysCallback(true);
-
-  // Make sure all content are still there.
-  EXPECT_CALL(*this, OnContentEntriesReceived(_))
-      .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
-        ASSERT_EQ(results.size(), 2U);
-        EXPECT_EQ(results[0].first, kContentKey1);
-        EXPECT_EQ(results[0].second, kContentData1);
-        EXPECT_EQ(results[1].first, kContentKey2);
-        EXPECT_EQ(results[1].second, kContentData2);
-      });
-  db()->LoadContent(
-      {kContentKey1, kContentKey2},
-      base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
-                     base::Unretained(this)));
-  storage_db()->LoadCallback(true);
-}
-
-}  // namespace feed
diff --git a/components/live_tab_count_metrics/BUILD.gn b/components/live_tab_count_metrics/BUILD.gn
new file mode 100644
index 0000000..5868db2
--- /dev/null
+++ b/components/live_tab_count_metrics/BUILD.gn
@@ -0,0 +1,29 @@
+# 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.
+
+component("live_tab_count_metrics") {
+  sources = [
+    "live_tab_count_metrics.cc",
+    "live_tab_count_metrics.h",
+  ]
+
+  defines = [ "IS_LIVE_TAB_COUNT_METRICS_IMPL" ]
+
+  public_deps = [
+    "//base",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "live_tab_count_metrics_unittest.cc",
+  ]
+
+  deps = [
+    ":live_tab_count_metrics",
+    "//base/test:test_support",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/live_tab_count_metrics/OWNERS b/components/live_tab_count_metrics/OWNERS
new file mode 100644
index 0000000..9795904
--- /dev/null
+++ b/components/live_tab_count_metrics/OWNERS
@@ -0,0 +1 @@
+file://base/metrics/OWNERS
diff --git a/components/live_tab_count_metrics/README b/components/live_tab_count_metrics/README
new file mode 100644
index 0000000..aa1dc62
--- /dev/null
+++ b/components/live_tab_count_metrics/README
@@ -0,0 +1,21 @@
+This directory contains process-independent code for recording metrics bucketed
+by the number of live tabs.
+
+We consider a tab to be alive (i.e. a live tab) if it is a UI tab (e.g. in a
+tabstrip, as opposed to a prerenderer), and it has not been discarded and has
+not crashed. Tabs can be discarded on desktop by TabManager to conserve
+resources, and tabs crash when the corresponding renderer process is killed,
+e.g. due to limited resources (OOM).
+
+Clients of this component must be able to count live tabs. The interface this
+component exposes provides a way to help create metrics bucketed by live tab
+count in a consistent manner, but this is dependent on the client knowing the
+count. The code in this directory is meant to be shared between processes, and
+so we do not count live tabs here. The live tab count is a browser concept, and
+it can be computed there. To record metrics bucketed by live tab counts in
+processes other than the browser, the live tab count would need to be plumbed
+out of the browser. In some cases this may be more efficient than plumbing
+metrics data out of the process to the browser, which is an alternative.
+
+This component should not have any dependencies other than //base as it
+should be able to be used from any other place.
diff --git a/components/live_tab_count_metrics/live_tab_count_metrics.cc b/components/live_tab_count_metrics/live_tab_count_metrics.cc
new file mode 100644
index 0000000..f37a7c2
--- /dev/null
+++ b/components/live_tab_count_metrics/live_tab_count_metrics.cc
@@ -0,0 +1,85 @@
+// 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 "components/live_tab_count_metrics/live_tab_count_metrics.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+namespace live_tab_count_metrics {
+
+// These values represent the lower bound for each bucket, and define the live
+// tab count buckets. The final bucket has no upper bound, and each other
+// bucket, i, is bounded above by the lower bound of bucket i + 1.
+//
+// The buckets were determined from the Tabs.MaxTabsInADay histogram,
+// approximating the 25th, 50th, 75th, 95th, and 99th percentiles, but with the
+// single and zero tab cases separated.
+//
+// If adding or removing a bucket, update |kNumLiveTabCountBuckets|,
+// |kLiveTabCountBucketMins|, and |kLiveTabCountBucketNames|. If adding,
+// removing, or changing bucket ranges, the existing metrics that use these
+// functions for emitting histograms should be marked as obsolete, and new
+// metrics should be created. This can be accomplished by versioning
+// |kLiveTabCountBucketNames|, e.g.  ".ByLiveTabCount2.0Tabs", etc., and
+// updating the histogram suffixes section of histograms.xml, creating a new
+// entry for the new suffixes and marking the old suffixes obsolete.
+constexpr size_t kLiveTabCountBucketMins[] = {0, 1, 2, 3, 5, 8, 20, 40};
+
+// Text for the live tab count portion of metric names. These need to be kept
+// in sync with |kLiveTabCountBucketMins|.
+constexpr const char* kLiveTabCountBucketNames[]{
+    ".ByLiveTabCount.0Tabs",      ".ByLiveTabCount.1Tab",
+    ".ByLiveTabCount.2Tabs",      ".ByLiveTabCount.3To4Tabs",
+    ".ByLiveTabCount.5To7Tabs",   ".ByLiveTabCount.8To19Tabs",
+    ".ByLiveTabCount.20To39Tabs", ".ByLiveTabCount.40OrMoreTabs"};
+
+std::string HistogramName(const std::string prefix, size_t bucket) {
+  static_assert(
+      base::size(kLiveTabCountBucketMins) == kNumLiveTabCountBuckets,
+      "kLiveTabCountBucketMins must have kNumLiveTabCountBuckets elements.");
+  static_assert(
+      base::size(kLiveTabCountBucketNames) == kNumLiveTabCountBuckets,
+      "kLiveTabCountBucketNames must have kNumLiveTabCountBuckets elements.");
+  DCHECK_LT(bucket, kNumLiveTabCountBuckets);
+  DCHECK(prefix.length());
+  return prefix + kLiveTabCountBucketNames[bucket];
+}
+
+size_t BucketForLiveTabCount(size_t num_live_tabs) {
+  for (size_t bucket = 0; bucket < kNumLiveTabCountBuckets; bucket++) {
+    if (internal::IsInBucket(num_live_tabs, bucket))
+      return bucket;
+  }
+  // There should be a bucket for any number of tabs >= 0.
+  NOTREACHED();
+  return kNumLiveTabCountBuckets;
+}
+
+namespace internal {
+
+size_t BucketMin(size_t bucket) {
+  DCHECK_LT(bucket, kNumLiveTabCountBuckets);
+  return kLiveTabCountBucketMins[bucket];
+}
+
+size_t BucketMax(size_t bucket) {
+  DCHECK_LT(bucket, kNumLiveTabCountBuckets);
+  // The last bucket includes everything after the min bucket value.
+  if (bucket == kNumLiveTabCountBuckets - 1)
+    return std::numeric_limits<size_t>::max();
+  return kLiveTabCountBucketMins[bucket + 1] - 1;
+}
+
+bool IsInBucket(size_t num_live_tabs, size_t bucket) {
+  DCHECK_LT(bucket, kNumLiveTabCountBuckets);
+  return num_live_tabs >= BucketMin(bucket) &&
+         num_live_tabs <= BucketMax(bucket);
+}
+
+}  // namespace internal
+
+}  // namespace live_tab_count_metrics
diff --git a/components/live_tab_count_metrics/live_tab_count_metrics.h b/components/live_tab_count_metrics/live_tab_count_metrics.h
new file mode 100644
index 0000000..a990d0c
--- /dev/null
+++ b/components/live_tab_count_metrics/live_tab_count_metrics.h
@@ -0,0 +1,80 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_LIVE_TAB_COUNT_METRICS_LIVE_TAB_COUNT_METRICS_H_
+#define COMPONENTS_LIVE_TAB_COUNT_METRICS_LIVE_TAB_COUNT_METRICS_H_
+
+#include <string>
+
+#include "base/component_export.h"
+
+// This namespace contains functions for creating histograms bucketed by number
+// of live tabs.
+//
+// All bucket parameters --- number of buckets, bucket sizes, bucket names ---
+// are determined at compile time, and these methods are safe to call from any
+// thread.
+//
+// A typical example of creating a histogram bucketed by live tab count using
+// STATIC_HISTOGRAM_POINTER_GROUP looks something like this:
+//  const size_t live_tab_count = GetLiveTabCount();
+//  const size_t bucket =
+//      live_tab_count_metrics::BucketForLiveTabCount(live_tab_count);
+//  STATIC_HISTOGRAM_POINTER_GROUP(
+//      live_tab_count_metrics::HistogramName(constant_histogram_prefix,
+//                                            bucket),
+//      static_cast<int>(bucket),
+//      static_cast<int>(live_tab_count_metrics::kNumLiveTabCountBuckets),
+//      Add(sample),
+//      base::Histogram::FactoryGet(
+//          live_tab_count_metrics::HistogramName(constant_histogram_prefix,
+//                                                bucket),
+//          MINIMUM_SAMPLE, MAXIMUM_SAMPLE, BUCKET_COUNT,
+//          base::HistogramBase::kUmaTargetedHistogramFlag));
+//  }
+namespace live_tab_count_metrics {
+
+// |kNumLiveTabCountBuckets| is used in various constexpr arrays and as a bound
+// on the histogram array when using STATIC_HISTOGRAM_POINTER_GROUP. This value
+// must be equal to the length of the array of live tab count bucket min values
+// (|kLiveTabCountBucketMins|) and the array of bucket names
+// (|kLiveTabCountBucketNames|) found in the corresponding .cc file.
+constexpr size_t kNumLiveTabCountBuckets = 8;
+
+// Returns the histogram name for |bucket|. The histogram name is the
+// concatenation of |prefix| and the name corresponding to |bucket|, which is of
+// the form |prefix| + ".ByLiveTabCount." + <BucketRangeText>, where
+// <BucketRangeText> is a string describing the bucket range, e.g. "1Tab",
+// "3To4Tabs", etc. See |kLiveTabCountBucketNames| for all of the bucket names.
+// |bucket| must be in the interval [0, |kNumLiveTabCountBuckets|).
+COMPONENT_EXPORT(LIVE_TAB_COUNT_METRICS)
+std::string HistogramName(const std::string prefix, size_t bucket);
+
+// Return the bucket index for the |num_live_tabs|.
+COMPONENT_EXPORT(LIVE_TAB_COUNT_METRICS)
+size_t BucketForLiveTabCount(size_t num_live_tabs);
+
+// These are exposed for unit tests.
+namespace internal {
+
+// Returns the number of tabs corresponding to the minimum value of |bucket|.
+// |bucket| must be in the interval [0, |kNumLiveTabCountBuckets|).
+COMPONENT_EXPORT(LIVE_TAB_COUNT_METRICS)
+size_t BucketMin(size_t bucket);
+
+// Returns the number of tabs corresponding to the maximum value of |bucket|.
+// |bucket| must be in the interval [0, |kNumLiveTabCountBuckets|).
+COMPONENT_EXPORT(LIVE_TAB_COUNT_METRICS)
+size_t BucketMax(size_t bucket);
+
+// Returns true if |num_live_tabs| falls within |bucket|.
+// |bucket| must be in the interval [0, |kNumLiveTabCountBuckets|).
+COMPONENT_EXPORT(LIVE_TAB_COUNT_METRICS)
+bool IsInBucket(size_t num_live_tabs, size_t bucket);
+
+}  // namespace internal
+
+}  // namespace live_tab_count_metrics
+
+#endif  // COMPONENTS_LIVE_TAB_COUNT_METRICS_LIVE_TAB_COUNT_METRICS_H_
diff --git a/components/live_tab_count_metrics/live_tab_count_metrics_unittest.cc b/components/live_tab_count_metrics/live_tab_count_metrics_unittest.cc
new file mode 100644
index 0000000..56b1337
--- /dev/null
+++ b/components/live_tab_count_metrics/live_tab_count_metrics_unittest.cc
@@ -0,0 +1,168 @@
+// 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 "components/live_tab_count_metrics/live_tab_count_metrics.h"
+
+#include <algorithm>
+#include <array>
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace live_tab_count_metrics {
+
+namespace {
+
+// For unit tests, we create bucketed test enumeration histograms with a prefix
+// of |kTestMetricPrefix| and a range of [0, |kMaxLiveTabCount|]. We want good
+// test coverage in all buckets, but the overflow live tab count bucket is
+// unbounded, so we cap the max number of live tabs at a reasonable value.
+constexpr char kTestMetricPrefix[] = "TestMetric";
+constexpr size_t kMaxLiveTabCount = 400;
+
+// LiveTabCounts is a map of a live tab count to expected histogram counts,
+// which is used to validate the test histogram.
+//
+// When we record a sample for test metrics, the number of live tabs is used as
+// the bucket in the enumeration histogram. For example, if we record a sample
+// with the number of live tabs equal to 10, we use 10 as the sample in the
+// enumeration histogram. Suppose we have LiveTabCounts map, |expected_counts|.
+// When recording the metric, we would increment the value of
+// expected_counts[10]. |expected_counts| is then used to validate the
+// histogram.
+using LiveTabCounts = base::flat_map<size_t, size_t>;
+
+// Array of LiveTabCounts, indexed by live tab count bucket.
+//
+// Each value in this array corresponds to the expected counts for a different
+// histogram, i.e. the histogram corresponding to the live tab count bucket
+// equal to the array index. For example, given a
+// HistogramCountsByLiveTabCountBucket array, |expected_counts_by_bucket|,
+// expected_counts_by_bucket[1] are the expected counts for the histogram
+// corresponding to live tab count bucket 1.
+using HistogramCountsByLiveTabCountBucket =
+    std::array<LiveTabCounts, kNumLiveTabCountBuckets>;
+
+}  // namespace
+
+class LiveTabCountMetricsTest : public testing::Test {
+ public:
+  LiveTabCountMetricsTest() = default;
+  ~LiveTabCountMetricsTest() override = default;
+
+  void RecordLiveTabCountMetric(size_t live_tab_count) {
+    EXPECT_LE(live_tab_count, kMaxLiveTabCount);
+    const size_t bucket = BucketForLiveTabCount(live_tab_count);
+    STATIC_HISTOGRAM_POINTER_GROUP(
+        HistogramName(kTestMetricPrefix, bucket), static_cast<int>(bucket),
+        static_cast<int>(kNumLiveTabCountBuckets), Add(live_tab_count),
+        base::Histogram::FactoryGet(
+            HistogramName(kTestMetricPrefix, bucket), 1, kMaxLiveTabCount,
+            kMaxLiveTabCount + 1,
+            base::HistogramBase::kUmaTargetedHistogramFlag));
+  }
+
+ protected:
+  void ValidateHistograms(
+      const HistogramCountsByLiveTabCountBucket& expected_counts) {
+    for (size_t bucket = 0; bucket < expected_counts.size(); bucket++) {
+      size_t expected_total_count = 0;
+      std::string histogram_name = HistogramName(kTestMetricPrefix, bucket);
+      for (const auto& live_tab_counts : expected_counts[bucket]) {
+        histogram_tester_.ExpectBucketCount(
+            histogram_name, live_tab_counts.first, live_tab_counts.second);
+        expected_total_count += live_tab_counts.second;
+      }
+      histogram_tester_.ExpectTotalCount(histogram_name, expected_total_count);
+    }
+  }
+
+ private:
+  base::HistogramTester histogram_tester_;
+};
+
+TEST_F(LiveTabCountMetricsTest, HistogramNames) {
+  // Testing with hard-coded strings to check that the concatenated names
+  // produced by HistogramName() are indeed what we expect. If the bucket ranges
+  // change, these strings will need to as well.
+  const std::string kTestMetricNameBucket1("TestMetric.ByLiveTabCount.1Tab");
+  const std::string kTestMetricNameBucket3(
+      "TestMetric.ByLiveTabCount.3To4Tabs");
+  EXPECT_EQ(kTestMetricNameBucket1, HistogramName(kTestMetricPrefix, 1));
+  EXPECT_EQ(kTestMetricNameBucket3, HistogramName(kTestMetricPrefix, 3));
+}
+
+TEST_F(LiveTabCountMetricsTest, RecordMetricsForBucketLimits) {
+  // For each bucket, simulate recording metrics with the live tab count equal
+  // to the lower and upper bounds of each bucket. For the last bucket, we use
+  // an artificial upper bound since that bucket has no practical bound (max
+  // size_t value) and do not want to create a histogram with that many buckets.
+  HistogramCountsByLiveTabCountBucket expected_counts;
+  for (size_t bucket = 0; bucket < expected_counts.size(); bucket++) {
+    size_t num_live_tabs = internal::BucketMin(bucket);
+    RecordLiveTabCountMetric(num_live_tabs);
+    // Expect a count of 1 for the |num_live_tabs| case in the histogram
+    // corresponding to |bucket|.
+    expected_counts[bucket].emplace(num_live_tabs, 1);
+
+    num_live_tabs = std::min(internal::BucketMax(bucket), kMaxLiveTabCount);
+    RecordLiveTabCountMetric(num_live_tabs);
+    // Ensure that we aren't setting the artificial maximum for the last bucket
+    // too low.
+    EXPECT_LE(internal::BucketMin(bucket), num_live_tabs);
+
+    auto iter = expected_counts[bucket].find(num_live_tabs);
+    if (iter != expected_counts[bucket].end()) {
+      // If the lower bound for |bucket| is the same as the upper bound, we've
+      // already recorded a value for |num_live_tabs| in |bucket|.
+      EXPECT_EQ(iter->second, 1u);
+      ++iter->second;
+    } else {
+      expected_counts[bucket].emplace(num_live_tabs, 1);
+    }
+
+    ValidateHistograms(expected_counts);
+  }
+}
+
+TEST_F(LiveTabCountMetricsTest, RecordMetricsForAllBucketValues) {
+  // For each bucket, simulate recording metrics with the live tab count equal
+  // to each value in the bucket range, i.e. from BucketMin() to BucketMax().
+  // For the last bucket, we use an artificial upper bound since that bucket has
+  // no practical bound (max size_t value) and do not want to create a histogram
+  // with that many buckets.
+  HistogramCountsByLiveTabCountBucket expected_counts;
+  for (size_t bucket = 0; bucket < expected_counts.size(); bucket++) {
+    size_t num_live_tabs_lower_bound = internal::BucketMin(bucket);
+    size_t num_live_tabs_upper_bound =
+        std::min(internal::BucketMax(bucket), kMaxLiveTabCount);
+    EXPECT_LE(num_live_tabs_lower_bound, num_live_tabs_upper_bound);
+    for (size_t live_tab_count = num_live_tabs_lower_bound;
+         live_tab_count <= num_live_tabs_upper_bound; live_tab_count++) {
+      RecordLiveTabCountMetric(live_tab_count);
+      expected_counts[bucket].emplace(live_tab_count, 1);
+    }
+    ValidateHistograms(expected_counts);
+  }
+}
+
+TEST_F(LiveTabCountMetricsTest, BucketsDoNotOverlap) {
+  // For any number of tabs >= 0, the tab should belong to exactly one bucket.
+  for (size_t tab_count = 0; tab_count <= kMaxLiveTabCount; tab_count++) {
+    bool has_bucket_for_tab_count = false;
+    for (size_t bucket = 0; bucket < kNumLiveTabCountBuckets; bucket++) {
+      if (internal::IsInBucket(tab_count, bucket)) {
+        EXPECT_FALSE(has_bucket_for_tab_count);
+        has_bucket_for_tab_count = true;
+      }
+    }
+    EXPECT_TRUE(has_bucket_for_tab_count);
+  }
+}
+
+}  // namespace live_tab_count_metrics
diff --git a/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.cc b/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.cc
index 6a4a607..2cd0e8d 100644
--- a/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.cc
+++ b/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.cc
@@ -46,8 +46,7 @@
           weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
       base::BindRepeating(
           &ContextualContentSuggestionsServiceProxy::ReportEvent,
-          weak_ptr_factory_.GetWeakPtr(), last_ukm_source_id_, url.spec(),
-          ArticleSource::CONTEXTUAL_SUGGESTIONS));
+          weak_ptr_factory_.GetWeakPtr(), last_ukm_source_id_, url.spec()));
 }
 
 void ContextualContentSuggestionsServiceProxy::FetchContextualSuggestionImage(
@@ -90,7 +89,6 @@
 void ContextualContentSuggestionsServiceProxy::ReportEvent(
     ukm::SourceId ukm_source_id,
     const std::string& url,
-    ArticleSource article_source,
     ContextualSuggestionsEvent event) {
   // TODO(pnoland): investigate how we can get into this state(one known
   // example is if we switch tabs and there's no committed navigation in the new
@@ -105,7 +103,7 @@
     if (last_ukm_source_id_ != ukm::kInvalidSourceId)
       reporter_->Flush();
     last_ukm_source_id_ = ukm_source_id;
-    reporter_->SetupForPage(url, article_source, ukm_source_id);
+    reporter_->SetupForPage(url, ukm_source_id);
   }
 
   reporter_->RecordEvent(event);
diff --git a/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.h b/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.h
index ee43d49..64c82075 100644
--- a/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.h
+++ b/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.h
@@ -52,7 +52,6 @@
   // Reports user interface event to the service.
   void ReportEvent(ukm::SourceId,
                    const std::string& url,
-                   ArticleSource article_source,
                    ContextualSuggestionsEvent event);
 
   // Ensures that all metrics are properly flushed.
diff --git a/components/ntp_snippets/contextual/reporting/contextual_suggestions_composite_reporter.cc b/components/ntp_snippets/contextual/reporting/contextual_suggestions_composite_reporter.cc
index b43e7bc0..fcad5d8 100644
--- a/components/ntp_snippets/contextual/reporting/contextual_suggestions_composite_reporter.cc
+++ b/components/ntp_snippets/contextual/reporting/contextual_suggestions_composite_reporter.cc
@@ -16,10 +16,9 @@
 
 void ContextualSuggestionsCompositeReporter::SetupForPage(
     const std::string& url,
-    ArticleSource article_source,
     ukm::SourceId source_id) {
   for (ContextualSuggestionsReporter* reporter : raw_reporters_)
-    reporter->SetupForPage(url, article_source, source_id);
+    reporter->SetupForPage(url, source_id);
 }
 
 void ContextualSuggestionsCompositeReporter::RecordEvent(
diff --git a/components/ntp_snippets/contextual/reporting/contextual_suggestions_composite_reporter.h b/components/ntp_snippets/contextual/reporting/contextual_suggestions_composite_reporter.h
index fb51fbde..340a23d 100644
--- a/components/ntp_snippets/contextual/reporting/contextual_suggestions_composite_reporter.h
+++ b/components/ntp_snippets/contextual/reporting/contextual_suggestions_composite_reporter.h
@@ -26,7 +26,6 @@
 
   // ContextualSuggestionsReporter
   void SetupForPage(const std::string& url,
-                    ArticleSource article_source,
                     ukm::SourceId source_id) override;
   void RecordEvent(ContextualSuggestionsEvent event) override;
   void Flush() override;
diff --git a/components/ntp_snippets/contextual/reporting/contextual_suggestions_composite_reporter_unittest.cc b/components/ntp_snippets/contextual/reporting/contextual_suggestions_composite_reporter_unittest.cc
index eb89669..4cb15e9 100644
--- a/components/ntp_snippets/contextual/reporting/contextual_suggestions_composite_reporter_unittest.cc
+++ b/components/ntp_snippets/contextual/reporting/contextual_suggestions_composite_reporter_unittest.cc
@@ -20,10 +20,8 @@
   /* ContextualSuggestionsReporter */
 
   void SetupForPage(const std::string& url,
-                    ArticleSource article_source,
                     ukm::SourceId source_id) override {
     this->url_ = url;
-    this->article_source_ = article_source;
     this->source_id_ = source_id;
     called_setup_for_page_count_++;
   }
@@ -49,7 +47,6 @@
   static int reporter_destroy_count_;
 
   std::string url_;
-  ArticleSource article_source_;
   ukm::SourceId source_id_;
   int called_setup_for_page_count_ = 0;
   int called_record_event_count_ = 0;
@@ -69,8 +66,7 @@
     composite_reporter->AddOwnedReporter(std::move(reporter));
   }
 
-  composite_reporter->SetupForPage(
-      kTestUrl, ArticleSource::CONTEXTUAL_SUGGESTIONS, kSourceId);
+  composite_reporter->SetupForPage(kTestUrl, kSourceId);
   composite_reporter->RecordEvent(ContextualSuggestionsEvent::FETCH_REQUESTED);
   composite_reporter->Flush();
 
@@ -95,8 +91,7 @@
     reporters.push_back(std::move(reporter));
   }
 
-  composite_reporter->SetupForPage(
-      kTestUrl, ArticleSource::CONTEXTUAL_SUGGESTIONS, kSourceId);
+  composite_reporter->SetupForPage(kTestUrl, kSourceId);
   composite_reporter->RecordEvent(ContextualSuggestionsEvent::FETCH_REQUESTED);
   composite_reporter->Flush();
 
diff --git a/components/ntp_snippets/contextual/reporting/contextual_suggestions_debugging_reporter.cc b/components/ntp_snippets/contextual/reporting/contextual_suggestions_debugging_reporter.cc
index b93c67cf..294004d 100644
--- a/components/ntp_snippets/contextual/reporting/contextual_suggestions_debugging_reporter.cc
+++ b/components/ntp_snippets/contextual/reporting/contextual_suggestions_debugging_reporter.cc
@@ -29,7 +29,6 @@
 
 void ContextualSuggestionsDebuggingReporter::SetupForPage(
     const std::string& url,
-    ArticleSource article_source,
     ukm::SourceId source_id) {
   current_event_ = ContextualSuggestionsDebuggingEvent();
   current_event_.url = url;
diff --git a/components/ntp_snippets/contextual/reporting/contextual_suggestions_debugging_reporter.h b/components/ntp_snippets/contextual/reporting/contextual_suggestions_debugging_reporter.h
index ee835c8..8d813f1 100644
--- a/components/ntp_snippets/contextual/reporting/contextual_suggestions_debugging_reporter.h
+++ b/components/ntp_snippets/contextual/reporting/contextual_suggestions_debugging_reporter.h
@@ -55,7 +55,6 @@
 
   // ContextualSuggestionsReporter
   void SetupForPage(const std::string& url,
-                    ArticleSource article_source,
                     ukm::SourceId source_id) override;
   void RecordEvent(
       contextual_suggestions::ContextualSuggestionsEvent event) override;
diff --git a/components/ntp_snippets/contextual/reporting/contextual_suggestions_metrics_reporter.cc b/components/ntp_snippets/contextual/reporting/contextual_suggestions_metrics_reporter.cc
index 6485262..b111040 100644
--- a/components/ntp_snippets/contextual/reporting/contextual_suggestions_metrics_reporter.cc
+++ b/components/ntp_snippets/contextual/reporting/contextual_suggestions_metrics_reporter.cc
@@ -27,7 +27,6 @@
 
 void ContextualSuggestionsMetricsReporter::SetupForPage(
     const std::string& url,
-    ArticleSource article_source,
     ukm::SourceId source_id) {
   DCHECK(!ukm_entry_) << "Flush should be called before SetupForPage!";
   DCHECK(source_id != ukm::kInvalidSourceId);
diff --git a/components/ntp_snippets/contextual/reporting/contextual_suggestions_metrics_reporter.h b/components/ntp_snippets/contextual/reporting/contextual_suggestions_metrics_reporter.h
index 5b561881..7dffdce 100644
--- a/components/ntp_snippets/contextual/reporting/contextual_suggestions_metrics_reporter.h
+++ b/components/ntp_snippets/contextual/reporting/contextual_suggestions_metrics_reporter.h
@@ -28,7 +28,6 @@
 
   // ContextualSuggestionsReporter
   void SetupForPage(const std::string& url,
-                    ArticleSource article_source,
                     ukm::SourceId source_id) override;
   void RecordEvent(ContextualSuggestionsEvent event) override;
   void Flush() override;
diff --git a/components/ntp_snippets/contextual/reporting/contextual_suggestions_metrics_reporter_unittest.cc b/components/ntp_snippets/contextual/reporting/contextual_suggestions_metrics_reporter_unittest.cc
index b610a5d..162693e 100644
--- a/components/ntp_snippets/contextual/reporting/contextual_suggestions_metrics_reporter_unittest.cc
+++ b/components/ntp_snippets/contextual/reporting/contextual_suggestions_metrics_reporter_unittest.cc
@@ -73,8 +73,7 @@
 
 TEST_F(ContextualSuggestionsMetricsReporterTest, BaseTest) {
   base::HistogramTester histogram_tester;
-  GetReporter().SetupForPage(
-      kTestNavigationUrl, ArticleSource::CONTEXTUAL_SUGGESTIONS, GetSourceId());
+  GetReporter().SetupForPage(kTestNavigationUrl, GetSourceId());
   GetReporter().RecordEvent(FETCH_REQUESTED);
   GetReporter().RecordEvent(FETCH_COMPLETED);
   GetReporter().RecordEvent(UI_PEEK_REVERSE_SCROLL);
@@ -124,8 +123,7 @@
     ContextualSuggestionsEvent event) {
   std::unique_ptr<base::HistogramTester> histogram_tester =
       std::make_unique<base::HistogramTester>();
-  GetReporter().SetupForPage(
-      kTestNavigationUrl, ArticleSource::CONTEXTUAL_SUGGESTIONS, GetSourceId());
+  GetReporter().SetupForPage(kTestNavigationUrl, GetSourceId());
   // Always report a single FETCH_DELAYED event so we ensure there's a
   // histogram (otherwise the ExpectBucketCount may crash).
   GetReporter().RecordEvent(FETCH_DELAYED);
@@ -168,8 +166,7 @@
 TEST_F(ContextualSuggestionsMetricsReporterTest, MultipleEventsTest) {
   std::unique_ptr<base::HistogramTester> histogram_tester =
       std::make_unique<base::HistogramTester>();
-  GetReporter().SetupForPage(
-      kTestNavigationUrl, ArticleSource::CONTEXTUAL_SUGGESTIONS, GetSourceId());
+  GetReporter().SetupForPage(kTestNavigationUrl, GetSourceId());
   // Test multiple cycles of FETCH_REQUESTED, FETCH_COMPLETED.
   GetReporter().RecordEvent(FETCH_REQUESTED);
   histogram_tester->ExpectBucketCount(kEventsHistogramName, kFetchRequested, 1);
@@ -190,8 +187,7 @@
 TEST_F(ContextualSuggestionsMetricsReporterTest, EnumNotReorderedTest) {
   std::unique_ptr<base::HistogramTester> histogram_tester =
       std::make_unique<base::HistogramTester>();
-  GetReporter().SetupForPage(
-      kTestNavigationUrl, ArticleSource::CONTEXTUAL_SUGGESTIONS, GetSourceId());
+  GetReporter().SetupForPage(kTestNavigationUrl, GetSourceId());
   // Peek the UI, which starts the timer, expected by all the other UI or
   // suggestion events.
   GetReporter().RecordEvent(UI_PEEK_REVERSE_SCROLL);
@@ -204,8 +200,7 @@
 
 TEST_F(ContextualSuggestionsMetricsReporterTest, ButtonTest) {
   base::HistogramTester histogram_tester;
-  GetReporter().SetupForPage(
-      kTestNavigationUrl, ArticleSource::CONTEXTUAL_SUGGESTIONS, GetSourceId());
+  GetReporter().SetupForPage(kTestNavigationUrl, GetSourceId());
   GetReporter().RecordEvent(FETCH_REQUESTED);
   GetReporter().RecordEvent(FETCH_COMPLETED);
   GetReporter().RecordEvent(UI_BUTTON_SHOWN);
diff --git a/components/ntp_snippets/contextual/reporting/contextual_suggestions_reporter.h b/components/ntp_snippets/contextual/reporting/contextual_suggestions_reporter.h
index 163cf8c9..2f72c4e 100644
--- a/components/ntp_snippets/contextual/reporting/contextual_suggestions_reporter.h
+++ b/components/ntp_snippets/contextual/reporting/contextual_suggestions_reporter.h
@@ -13,11 +13,6 @@
 
 namespace contextual_suggestions {
 
-// This represents the source of the navigation to the current page. It's set
-// via referrer links. We only need EOC/other since we only want to compare
-// metrics across this dimension.
-enum class ArticleSource { CONTEXTUAL_SUGGESTIONS, OTHER };
-
 class ContextualSuggestionsDebuggingReporter;
 class ContextualSuggestionsReporter;
 
@@ -135,7 +130,6 @@
   // Sets up the page with the given |source_id| for event reporting.
   // All subsequent RecordEvent calls will apply to this page
   virtual void SetupForPage(const std::string& url,
-                            ArticleSource article_source,
                             ukm::SourceId source_id) = 0;
 
   // Reports that an event occurred for the current page.
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index ec5439f..cc41360 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -89,6 +89,8 @@
     "config_dir_policy_loader.h",
     "configuration_policy_provider.cc",
     "configuration_policy_provider.h",
+    "extension_policy_migrator.cc",
+    "extension_policy_migrator.h",
     "external_data_fetcher.cc",
     "external_data_fetcher.h",
     "external_data_manager.h",
@@ -312,6 +314,7 @@
     "cloud/device_management_service_unittest.cc",
     "cloud/policy_header_service_unittest.cc",
     "cloud/user_info_fetcher_unittest.cc",
+    "extension_policy_migrator_unittest.cc",
     "generate_policy_source_unittest.cc",
     "plist_writer_unittest.cc",
     "policy_bundle_unittest.cc",
diff --git a/components/policy/core/common/configuration_policy_provider.cc b/components/policy/core/common/configuration_policy_provider.cc
index a886bd4..a8f75ec 100644
--- a/components/policy/core/common/configuration_policy_provider.cc
+++ b/components/policy/core/common/configuration_policy_provider.cc
@@ -5,6 +5,7 @@
 #include "components/policy/core/common/configuration_policy_provider.h"
 
 #include "base/callback.h"
+#include "components/policy/core/common/extension_policy_migrator.h"
 #include "components/policy/core/common/external_data_fetcher.h"
 #include "components/policy/core/common/policy_map.h"
 
@@ -40,12 +41,21 @@
   return true;
 }
 
+void ConfigurationPolicyProvider::AddMigrator(
+    std::unique_ptr<ExtensionPolicyMigrator> migrator) {
+  DCHECK(migrator);
+  migrators_.push_back(std::move(migrator));
+}
+
 void ConfigurationPolicyProvider::UpdatePolicy(
     std::unique_ptr<PolicyBundle> bundle) {
-  if (bundle)
+  if (bundle) {
+    for (const auto& migrator : migrators_)
+      migrator->Migrate(bundle.get());
     policy_bundle_.Swap(bundle.get());
-  else
+  } else {
     policy_bundle_.Clear();
+  }
   for (auto& observer : observer_list_)
     observer.OnUpdatePolicy(this);
 }
diff --git a/components/policy/core/common/configuration_policy_provider.h b/components/policy/core/common/configuration_policy_provider.h
index 9f49b261..5147701 100644
--- a/components/policy/core/common/configuration_policy_provider.h
+++ b/components/policy/core/common/configuration_policy_provider.h
@@ -17,6 +17,8 @@
 
 namespace policy {
 
+class ExtensionPolicyMigrator;
+
 // A mostly-abstract super class for platform-specific policy providers.
 // Platform-specific policy providers (Windows Group Policy, gconf,
 // etc.) should implement a subclass of this class.
@@ -71,6 +73,10 @@
   virtual void AddObserver(Observer* observer);
   virtual void RemoveObserver(Observer* observer);
 
+  // Adds an ExtensionPolicyMigrator to be run before OnUpdatePolicy() is
+  // called.
+  void AddMigrator(std::unique_ptr<ExtensionPolicyMigrator> migrator);
+
   // SchemaRegistry::Observer:
   void OnSchemaRegistryUpdated(bool has_new_schemas) override;
   void OnSchemaRegistryReady() override;
@@ -97,6 +103,8 @@
 
   base::ObserverList<Observer, true>::Unchecked observer_list_;
 
+  std::vector<std::unique_ptr<ExtensionPolicyMigrator>> migrators_;
+
   DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyProvider);
 };
 
diff --git a/components/policy/core/common/configuration_policy_provider_test.cc b/components/policy/core/common/configuration_policy_provider_test.cc
index 240fb4c..5453ef3 100644
--- a/components/policy/core/common/configuration_policy_provider_test.cc
+++ b/components/policy/core/common/configuration_policy_provider_test.cc
@@ -12,6 +12,7 @@
 #include "base/task/task_traits.h"
 #include "base/values.h"
 #include "components/policy/core/common/configuration_policy_provider.h"
+#include "components/policy/core/common/extension_policy_migrator.h"
 #include "components/policy/core/common/external_data_fetcher.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/core/common/policy_bundle.h"
@@ -353,6 +354,19 @@
   provider_->RemoveObserver(&observer);
 }
 
+class MockPolicyMigrator : public ExtensionPolicyMigrator {
+ public:
+  MOCK_METHOD1(Migrate, void(PolicyBundle* bundle));
+};
+
+TEST_P(ConfigurationPolicyProviderTest, AddMigrator) {
+  MockPolicyMigrator* migrator = new MockPolicyMigrator;
+  EXPECT_CALL(*migrator, Migrate(_));
+  provider_->AddMigrator(std::unique_ptr<ExtensionPolicyMigrator>(migrator));
+  provider_->RefreshPolicies();
+  scoped_task_environment_.RunUntilIdle();
+}
+
 Configuration3rdPartyPolicyProviderTest::
     Configuration3rdPartyPolicyProviderTest() {}
 
diff --git a/components/policy/core/common/extension_policy_migrator.cc b/components/policy/core/common/extension_policy_migrator.cc
new file mode 100644
index 0000000..87ebbf8
--- /dev/null
+++ b/components/policy/core/common/extension_policy_migrator.cc
@@ -0,0 +1,35 @@
+// 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 "components/policy/core/common/extension_policy_migrator.h"
+
+namespace policy {
+
+ExtensionPolicyMigrator::~ExtensionPolicyMigrator() {}
+
+void ExtensionPolicyMigrator::CopyPoliciesIfUnset(
+    PolicyBundle* bundle,
+    const std::string& extension_id,
+    base::span<const Migration> migrations) {
+  PolicyMap& extension_map = bundle->Get(
+      PolicyNamespace(PolicyDomain::POLICY_DOMAIN_EXTENSIONS, extension_id));
+  if (extension_map.empty())
+    return;
+
+  PolicyMap& chrome_map = bundle->Get(PolicyNamespace(
+      PolicyDomain::POLICY_DOMAIN_CHROME, /* component_id */ std::string()));
+
+  for (const auto& migration : migrations) {
+    PolicyMap::Entry* entry = extension_map.GetMutable(migration.old_name);
+    if (entry) {
+      if (!chrome_map.Get(migration.new_name)) {
+        chrome_map.Set(migration.new_name, entry->DeepCopy());
+      }
+      // TODO(crbug/869958): Mark the old policy as deprecated for
+      // chrome://policy.
+    }
+  }
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/extension_policy_migrator.h b/components/policy/core/common/extension_policy_migrator.h
new file mode 100644
index 0000000..ae94bbe
--- /dev/null
+++ b/components/policy/core/common/extension_policy_migrator.h
@@ -0,0 +1,48 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_EXTENSION_POLICY_MIGRATOR_H_
+#define COMPONENTS_POLICY_CORE_COMMON_EXTENSION_POLICY_MIGRATOR_H_
+
+#include "base/containers/span.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+// A helper class that migrates a deprecated policy to a new policy across
+// domain boundaries, by setting up the new policy based on the old one. It can
+// migrate a deprecated extension policy to a new Chrome policy.
+//
+// For migrations that are only in the Chrome domain: you should use
+// |LegacyPoliciesDeprecatingPolicyHandler| instead.
+class POLICY_EXPORT ExtensionPolicyMigrator {
+ public:
+  virtual ~ExtensionPolicyMigrator();
+
+  // If there are deprecated policies in |bundle|, set the value of the new
+  // policies accordingly.
+  virtual void Migrate(PolicyBundle* bundle) = 0;
+
+  // Indicates how to rename a policy when migrating from the extension domain
+  // to the Chrome domain.
+  struct Migration {
+    // Old name for the policy, in the extension domain.
+    const char* old_name;
+    // New name for the policy, in the Chrome domain.
+    const char* new_name;
+  };
+
+ protected:
+  // Helper function intended for implementers who want to rename policies and
+  // copy them from an extension domain to the Chrome domain. If one of the
+  // Chrome domain policies is already set, it is not overridden.
+  void CopyPoliciesIfUnset(PolicyBundle* bundle,
+                           const std::string& extension_id,
+                           base::span<const Migration> migrations);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_EXTENSION_POLICY_MIGRATOR_H_
diff --git a/components/policy/core/common/extension_policy_migrator_unittest.cc b/components/policy/core/common/extension_policy_migrator_unittest.cc
new file mode 100644
index 0000000..17427ae
--- /dev/null
+++ b/components/policy/core/common/extension_policy_migrator_unittest.cc
@@ -0,0 +1,87 @@
+// 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 "components/policy/core/common/extension_policy_migrator.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+const char kExtensionId[] = "abcdefghijklmnopabcdefghijklmnop";
+
+const char kOldPolicy1[] = "OldPolicyOne";
+const char kOldPolicy2[] = "OldPolicyTwo";
+const char kOldPolicy3[] = "OldPolicyThree";
+const char kOldPolicy4[] = "OldPolicyFour";
+const char kNewPolicy1[] = "NewPolicyOne";
+const char kNewPolicy2[] = "NewPolicyTwo";
+const char kNewPolicy3[] = "NewPolicyThree";
+
+const int kOldValue1 = 111;
+const int kOldValue2 = 222;
+const int kOldValue3 = 333;
+const int kOldValue4 = 444;
+const int kNewValue3 = 999;
+
+const ExtensionPolicyMigrator::Migration kMigrations[] = {
+    {kOldPolicy1, kNewPolicy1},
+    {kOldPolicy2, kNewPolicy2},
+    {kOldPolicy3, kNewPolicy3},
+};
+
+void SetPolicy(PolicyMap* policy,
+               const char* policy_name,
+               std::unique_ptr<base::Value> value) {
+  policy->Set(policy_name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+              POLICY_SOURCE_CLOUD, std::move(value), nullptr);
+}
+
+class TestingPolicyMigrator : public ExtensionPolicyMigrator {
+ public:
+  void Migrate(PolicyBundle* bundle) override {
+    CopyPoliciesIfUnset(bundle, kExtensionId, kMigrations);
+  }
+};
+
+}  // namespace
+
+TEST(ExtensionPolicyMigratorTest, CopyPoliciesIfUnset) {
+  PolicyBundle bundle;
+
+  PolicyMap& chrome_map = bundle.Get(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, /* component_id */ std::string()));
+  SetPolicy(&chrome_map, kNewPolicy3,
+            std::make_unique<base::Value>(kNewValue3));
+
+  PolicyMap& extension_map =
+      bundle.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtensionId));
+  SetPolicy(&extension_map, kOldPolicy1,
+            std::make_unique<base::Value>(kOldValue1));
+  SetPolicy(&extension_map, kOldPolicy2,
+            std::make_unique<base::Value>(kOldValue2));
+  SetPolicy(&extension_map, kOldPolicy3,
+            std::make_unique<base::Value>(kOldValue3));
+  SetPolicy(&extension_map, kOldPolicy4,
+            std::make_unique<base::Value>(kOldValue4));
+
+  TestingPolicyMigrator().Migrate(&bundle);
+
+  // Policies in kMigrations should be renamed + copied into the Chrome domain.
+  EXPECT_EQ(3u, chrome_map.size());
+  ASSERT_TRUE(chrome_map.GetValue(kNewPolicy1));
+  EXPECT_EQ(base::Value(kOldValue1), *chrome_map.GetValue(kNewPolicy1));
+  ASSERT_TRUE(chrome_map.GetValue(kNewPolicy2));
+  EXPECT_EQ(base::Value(kOldValue2), *chrome_map.GetValue(kNewPolicy2));
+  // kNewPolicy3 is already set, and should not be overwritten.
+  ASSERT_TRUE(chrome_map.GetValue(kNewPolicy3));
+  EXPECT_EQ(base::Value(kNewValue3), *chrome_map.GetValue(kNewPolicy3));
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_map.cc b/components/policy/core/common/policy_map.cc
index 50750bb..4423a3f 100644
--- a/components/policy/core/common/policy_map.cc
+++ b/components/policy/core/common/policy_map.cc
@@ -8,6 +8,8 @@
 
 #include "base/callback.h"
 #include "base/stl_util.h"
+#include "base/strings/strcat.h"
+#include "base/strings/utf_string_conversions.h"
 
 namespace policy {
 
@@ -25,7 +27,8 @@
   copy.source = source;
   if (value)
     copy.value = value->CreateDeepCopy();
-  copy.error = error;
+  copy.error_strings_ = error_strings_;
+  copy.error_message_ids_ = error_message_ids_;
   if (external_data_fetcher) {
     copy.external_data_fetcher.reset(
         new ExternalDataFetcher(*external_data_fetcher));
@@ -48,13 +51,36 @@
   return level == other.level && scope == other.scope &&
          source == other.source &&  // Necessary for PolicyUIHandler observers.
                                     // They have to update when sources change.
-         error == other.error &&
+         error_strings_ == other.error_strings_ &&
+         error_message_ids_ == other.error_message_ids_ &&
          ((!value && !other.value) ||
           (value && other.value && *value == *other.value)) &&
          ExternalDataFetcher::Equals(external_data_fetcher.get(),
                                      other.external_data_fetcher.get());
 }
 
+void PolicyMap::Entry::AddError(base::StringPiece error) {
+  base::StrAppend(&error_strings_, {error, "\n"});
+}
+
+void PolicyMap::Entry::AddError(int message_id) {
+  error_message_ids_.push_back(message_id);
+}
+
+base::string16 PolicyMap::Entry::GetLocalizedErrors(
+    L10nLookupFunction lookup) const {
+  base::string16 error_string = base::UTF8ToUTF16(error_strings_);
+  base::string16 line_feed = base::UTF8ToUTF16("\n");
+  for (int message_id : error_message_ids_) {
+    error_string += lookup.Run(message_id);
+    error_string += line_feed;
+  }
+  // Remove the trailing newline.
+  if (!error_string.empty())
+    error_string.pop_back();
+  return error_string;
+}
+
 PolicyMap::PolicyMap() {}
 
 PolicyMap::~PolicyMap() {
@@ -101,8 +127,12 @@
   map_[policy] = std::move(entry);
 }
 
-void PolicyMap::SetError(const std::string& policy, const std::string& error) {
-  map_[policy].error = error;
+void PolicyMap::AddError(const std::string& policy, const std::string& error) {
+  map_[policy].AddError(error);
+}
+
+void PolicyMap::AddError(const std::string& policy, int message_id) {
+  map_[policy].AddError(message_id);
 }
 
 void PolicyMap::SetSourceForAll(PolicySource source) {
diff --git a/components/policy/core/common/policy_map.h b/components/policy/core/common/policy_map.h
index 0f0ef09..f693073a 100644
--- a/components/policy/core/common/policy_map.h
+++ b/components/policy/core/common/policy_map.h
@@ -26,11 +26,11 @@
  public:
   // Each policy maps to an Entry which keeps the policy value as well as other
   // relevant data about the policy.
-  struct POLICY_EXPORT Entry {
+  class POLICY_EXPORT Entry {
+   public:
     PolicyLevel level = POLICY_LEVEL_RECOMMENDED;
     PolicyScope scope = POLICY_SCOPE_USER;
     std::unique_ptr<base::Value> value;
-    std::string error;
     std::unique_ptr<ExternalDataFetcher> external_data_fetcher;
 
     // For debugging and displaying only. Set by provider delivering the policy.
@@ -51,6 +51,24 @@
 
     // Returns true if |this| equals |other|.
     bool Equals(const Entry& other) const;
+
+    // Add a non-localized error given its UTF-8 string contents.
+    void AddError(base::StringPiece error);
+    // Add a localized error given its l10n message ID.
+    void AddError(int message_id);
+
+    // Callback used to look up a localized string given its l10n message ID. It
+    // should return a UTF-16 string.
+    typedef base::RepeatingCallback<base::string16(int message_id)>
+        L10nLookupFunction;
+
+    // Returns localized errors added through AddError(), as UTF-16, and
+    // separated with LF characters.
+    base::string16 GetLocalizedErrors(L10nLookupFunction lookup) const;
+
+   private:
+    std::string error_strings_;
+    std::vector<int> error_message_ids_;
   };
 
   typedef std::map<std::string, Entry> PolicyMapType;
@@ -81,11 +99,15 @@
 
   void Set(const std::string& policy, Entry entry);
 
-  // Adds an |error| to the map for the key |policy| that should be shown to the
-  // user alongside the value in the policy UI. This is equivalent to calling
-  // |GetMutableValue(policy)->error = error|, so should only be called for
-  // policies that are already stored in this map.
-  void SetError(const std::string& policy, const std::string& error);
+  // Adds non-localized |error| to the map for the key |policy| that should be
+  // shown to the user alongside the value in the policy UI. This should only be
+  // called for policies that are already stored in this map.
+  void AddError(const std::string& policy, const std::string& error);
+
+  // Adds a localized error with |message_id| to the map for the key |policy|
+  // that should be shown to the user alongisde the value in the policy UI. This
+  // should only be called for policies that are already stored in the map.
+  void AddError(const std::string& policy, int message_id);
 
   // For all policies, overwrite the PolicySource with |source|.
   void SetSourceForAll(PolicySource source);
diff --git a/components/policy/core/common/policy_map_unittest.cc b/components/policy/core/common/policy_map_unittest.cc
index 691bd21..dffad87 100644
--- a/components/policy/core/common/policy_map_unittest.cc
+++ b/components/policy/core/common/policy_map_unittest.cc
@@ -9,6 +9,8 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
 #include "components/policy/core/common/external_data_manager.h"
 #include "components/policy/core/common/policy_types.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -68,14 +70,16 @@
   base::Value expected_b("bbb");
   EXPECT_TRUE(expected_b.Equals(map.GetValue(kTestPolicyName1)));
   SetPolicy(&map, kTestPolicyName1, CreateExternalDataFetcher("dummy"));
-  map.SetError(kTestPolicyName1, kTestError);
+  map.AddError(kTestPolicyName1, kTestError);
   EXPECT_FALSE(map.GetValue(kTestPolicyName1));
   const PolicyMap::Entry* entry = map.Get(kTestPolicyName1);
   ASSERT_TRUE(entry != nullptr);
   EXPECT_EQ(POLICY_LEVEL_MANDATORY, entry->level);
   EXPECT_EQ(POLICY_SCOPE_USER, entry->scope);
   EXPECT_EQ(POLICY_SOURCE_CLOUD, entry->source);
-  EXPECT_EQ(kTestError, entry->error);
+  PolicyMap::Entry::L10nLookupFunction lookup = base::BindRepeating(
+      static_cast<base::string16 (*)(int)>(&base::NumberToString16));
+  EXPECT_EQ(base::UTF8ToUTF16(kTestError), entry->GetLocalizedErrors(lookup));
   EXPECT_TRUE(
       ExternalDataFetcher::Equals(entry->external_data_fetcher.get(),
                                   CreateExternalDataFetcher("dummy").get()));
@@ -87,10 +91,26 @@
   EXPECT_EQ(POLICY_LEVEL_RECOMMENDED, entry->level);
   EXPECT_EQ(POLICY_SCOPE_MACHINE, entry->scope);
   EXPECT_EQ(POLICY_SOURCE_ENTERPRISE_DEFAULT, entry->source);
-  EXPECT_EQ("", entry->error);
+  EXPECT_EQ(base::string16(), entry->GetLocalizedErrors(lookup));
   EXPECT_FALSE(entry->external_data_fetcher);
 }
 
+TEST_F(PolicyMapTest, AddError) {
+  PolicyMap map;
+  SetPolicy(&map, kTestPolicyName1, std::make_unique<base::Value>(0));
+  PolicyMap::Entry* entry = map.GetMutable(kTestPolicyName1);
+  PolicyMap::Entry::L10nLookupFunction lookup = base::BindRepeating(
+      static_cast<base::string16 (*)(int)>(&base::NumberToString16));
+  EXPECT_EQ(base::string16(), entry->GetLocalizedErrors(lookup));
+  map.AddError(kTestPolicyName1, 1234);
+  EXPECT_EQ(base::UTF8ToUTF16("1234"), entry->GetLocalizedErrors(lookup));
+  map.AddError(kTestPolicyName1, 5678);
+  EXPECT_EQ(base::UTF8ToUTF16("1234\n5678"), entry->GetLocalizedErrors(lookup));
+  map.AddError(kTestPolicyName1, "abcd");
+  EXPECT_EQ(base::UTF8ToUTF16("abcd\n1234\n5678"),
+            entry->GetLocalizedErrors(lookup));
+}
+
 TEST_F(PolicyMapTest, Equals) {
   PolicyMap a;
   SetPolicy(&a, kTestPolicyName1, std::make_unique<base::Value>("aaa"));
diff --git a/components/policy/core/common/policy_proto_decoders.cc b/components/policy/core/common/policy_proto_decoders.cc
index c5c8414..5f13eddc 100644
--- a/components/policy/core/common/policy_proto_decoders.cc
+++ b/components/policy/core/common/policy_proto_decoders.cc
@@ -142,7 +142,7 @@
     map->Set(access->policy_key, level, scope, source,
              DecodeIntegerProto(proto, &error), nullptr);
     if (!error.empty())
-      map->SetError(access->policy_key, error);
+      map->AddError(access->policy_key, error);
   }
 
   for (const StringPolicyAccess* access = &kStringPolicyAccess[0];
@@ -169,7 +169,7 @@
     map->Set(access->policy_key, level, scope, source, std::move(value),
              std::move(external_data_fetcher));
     if (!error.empty())
-      map->SetError(access->policy_key, error);
+      map->AddError(access->policy_key, error);
   }
 
   for (const StringListPolicyAccess* access = &kStringListPolicyAccess[0];
@@ -186,4 +186,4 @@
   }
 }
 
-}  // namespace policy
\ No newline at end of file
+}  // namespace policy
diff --git a/components/previews/core/previews_experiments.cc b/components/previews/core/previews_experiments.cc
index 06fd343..eaa6d602 100644
--- a/components/previews/core/previews_experiments.cc
+++ b/components/previews/core/previews_experiments.cc
@@ -150,6 +150,11 @@
       5 * 60 /* 5 minutes */);
 }
 
+bool LitePagePreviewsTriggerOnLocalhost() {
+  return base::GetFieldTrialParamByFeatureAsBool(
+      features::kLitePageServerPreviews, "trigger_on_localhost", false);
+}
+
 GURL GetLitePagePreviewsDomainURL() {
   std::string variable_host_str = GetFieldTrialParamValueByFeature(
       features::kLitePageServerPreviews, "previews_host");
diff --git a/components/previews/core/previews_experiments.h b/components/previews/core/previews_experiments.h
index 33359a0..8efc4ea7 100644
--- a/components/previews/core/previews_experiments.h
+++ b/components/previews/core/previews_experiments.h
@@ -99,6 +99,10 @@
 // Primarily used to prohibit URLs that look like media requests.
 std::vector<std::string> LitePagePreviewsBlacklistedPathSuffixes();
 
+// Whether or not to trigger a preview for a navigation to localhost. Provided
+// as an experiment for automated and manual testing.
+bool LitePagePreviewsTriggerOnLocalhost();
+
 // The maximum number of seconds to loadshed the Previews server for.
 int PreviewServerLoadshedMaxSeconds();
 
diff --git a/components/previews/core/previews_features.cc b/components/previews/core/previews_features.cc
index 5bad470c..08a24c7 100644
--- a/components/previews/core/previews_features.cc
+++ b/components/previews/core/previews_features.cc
@@ -81,5 +81,10 @@
 const base::Feature kLitePageServerPreviews{"LitePageServerPreviews",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Shows a Previews icon and string in the Android Omnibox instead of an Infobar
+// when enabled. Only works and is honored on Android..
+const base::Feature kAndroidOmniboxPreviewsBadge{
+    "AndroidOmniboxPreviewsBadge", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace previews
diff --git a/components/previews/core/previews_features.h b/components/previews/core/previews_features.h
index 6c20004..2a8d70b 100644
--- a/components/previews/core/previews_features.h
+++ b/components/previews/core/previews_features.h
@@ -20,6 +20,7 @@
 constexpr char kOptimizationHintsExperimentNameParam[] = "experiment_name";
 extern const base::Feature kResourceLoadingHints;
 extern const base::Feature kLitePageServerPreviews;
+extern const base::Feature kAndroidOmniboxPreviewsBadge;
 
 }  // namespace features
 }  // namespace previews
diff --git a/components/services/pdf_compositor/BUILD.gn b/components/services/pdf_compositor/BUILD.gn
index 32b8993..0b2a6d5 100644
--- a/components/services/pdf_compositor/BUILD.gn
+++ b/components/services/pdf_compositor/BUILD.gn
@@ -24,7 +24,7 @@
     "//content/public/common:service_names",
     "//content/public/utility",
     "//printing/common",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//third_party/blink/public:blink_headers",
   ]
diff --git a/components/services/pdf_compositor/DEPS b/components/services/pdf_compositor/DEPS
index 952a2798..0c41bb8f 100644
--- a/components/services/pdf_compositor/DEPS
+++ b/components/services/pdf_compositor/DEPS
@@ -9,7 +9,7 @@
   "+printing/common",
   "+services/service_manager/public/cpp",
   "+services/service_manager/public/mojom",
-  "+services/ui/public/interfaces/constants.mojom.h",   # UI service name.
+  "+services/ws/public/mojom/constants.mojom.h",   # UI service name.
   "+skia",
   "+third_party/skia",
   "+third_party/blink/public/platform", # Test web sandbox support.
diff --git a/components/services/pdf_compositor/pdf_compositor_impl.cc b/components/services/pdf_compositor/pdf_compositor_impl.cc
index d0a0d26..58cb794 100644
--- a/components/services/pdf_compositor/pdf_compositor_impl.cc
+++ b/components/services/pdf_compositor/pdf_compositor_impl.cc
@@ -14,6 +14,7 @@
 #include "components/crash/core/common/crash_key.h"
 #include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_types.h"
 #include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.h"
+#include "mojo/public/cpp/base/shared_memory_utils.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "printing/common/metafile_utils.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -49,13 +50,19 @@
     uint64_t frame_guid,
     mojo::ScopedSharedBufferHandle serialized_content,
     const ContentToFrameMap& subframe_content_map) {
+  std::unique_ptr<base::SharedMemory> shared_memory =
+      GetShmFromMojoHandle(std::move(serialized_content));
+  if (!shared_memory) {
+    NotifyUnavailableSubframe(frame_guid);
+    return;
+  }
+
   // Add this frame and its serialized content.
   DCHECK(!base::ContainsKey(frame_info_map_, frame_guid));
   auto& frame_info =
       frame_info_map_.emplace(frame_guid, std::make_unique<FrameInfo>())
           .first->second;
-  frame_info->serialized_content =
-      GetShmFromMojoHandle(std::move(serialized_content));
+  frame_info->serialized_content = std::move(shared_memory);
 
   // Copy the subframe content information.
   frame_info->subframe_content_map = subframe_content_map;
@@ -171,11 +178,19 @@
     mojo::ScopedSharedBufferHandle serialized_content,
     const ContentToFrameMap& subframe_content_map,
     CompositeToPdfCallback callback) {
+  std::unique_ptr<base::SharedMemory> shared_memory =
+      GetShmFromMojoHandle(std::move(serialized_content));
+  if (!shared_memory) {
+    DLOG(ERROR) << "HandleCompositionRequest: Cannot map input.";
+    std::move(callback).Run(mojom::PdfCompositor::Status::HANDLE_MAP_ERROR,
+                            base::ReadOnlySharedMemoryRegion());
+    return;
+  }
+
   base::flat_set<uint64_t> pending_subframes;
   if (IsReadyToComposite(frame_guid, subframe_content_map,
                          &pending_subframes)) {
-    FulfillRequest(frame_guid, page_num,
-                   GetShmFromMojoHandle(std::move(serialized_content)),
+    FulfillRequest(frame_guid, page_num, std::move(shared_memory),
                    subframe_content_map, std::move(callback));
     return;
   }
@@ -187,8 +202,8 @@
     frame_info_map_[frame_guid] = std::make_unique<FrameInfo>();
 
   requests_.push_back(std::make_unique<RequestInfo>(
-      frame_guid, page_num, GetShmFromMojoHandle(std::move(serialized_content)),
-      subframe_content_map, std::move(pending_subframes), std::move(callback)));
+      frame_guid, page_num, std::move(shared_memory), subframe_content_map,
+      std::move(pending_subframes), std::move(callback)));
 }
 
 mojom::PdfCompositor::Status PdfCompositorImpl::CompositeToPdf(
@@ -226,7 +241,7 @@
   doc->close();
 
   base::MappedReadOnlyRegion region_mapping =
-      CreateReadOnlySharedMemoryRegion(wstream.bytesWritten());
+      mojo::CreateReadOnlySharedMemoryRegion(wstream.bytesWritten());
   if (!region_mapping.IsValid()) {
     DLOG(ERROR) << "CompositeToPdf: Cannot create new shared memory region.";
     return mojom::PdfCompositor::Status::HANDLE_MAP_ERROR;
diff --git a/components/services/pdf_compositor/pdf_compositor_impl_unittest.cc b/components/services/pdf_compositor/pdf_compositor_impl_unittest.cc
index 88ff8c3..97765aa 100644
--- a/components/services/pdf_compositor/pdf_compositor_impl_unittest.cc
+++ b/components/services/pdf_compositor/pdf_compositor_impl_unittest.cc
@@ -58,6 +58,10 @@
     // A stub for testing, no implementation.
   }
 
+  static mojo::ScopedSharedBufferHandle CreateTestData(size_t size) {
+    return mojo::SharedBufferHandle::Create(size);
+  }
+
  private:
   base::test::ScopedTaskEnvironment task_environment_;
   std::unique_ptr<base::RunLoop> run_loop_;
@@ -83,10 +87,8 @@
 TEST_F(PdfCompositorImplTest, IsReadyToComposite) {
   PdfCompositorImpl impl("unittest", nullptr);
   // Frame 2 and 3 are painted.
-  impl.AddSubframeContent(2u, mojo::SharedBufferHandle::Create(10),
-                          ContentToFrameMap());
-  impl.AddSubframeContent(3u, mojo::SharedBufferHandle::Create(10),
-                          ContentToFrameMap());
+  impl.AddSubframeContent(2u, CreateTestData(10), ContentToFrameMap());
+  impl.AddSubframeContent(3u, CreateTestData(10), ContentToFrameMap());
 
   // Frame 1 contains content 3 which corresponds to frame 2.
   // Frame 1 should be ready as frame 2 is ready.
@@ -112,8 +114,7 @@
   EXPECT_EQ(*pending_subframes.begin(), 4u);
 
   // Add content of frame 4. Now it is ready for composition.
-  impl.AddSubframeContent(4u, mojo::SharedBufferHandle::Create(10),
-                          ContentToFrameMap());
+  impl.AddSubframeContent(4u, CreateTestData(10), ContentToFrameMap());
   EXPECT_TRUE(
       impl.IsReadyToComposite(1u, subframe_content_map, &pending_subframes));
   EXPECT_TRUE(pending_subframes.empty());
@@ -123,8 +124,7 @@
   PdfCompositorImpl impl("unittest", nullptr);
   // Frame 3 has content 1 which refers to subframe 1.
   ContentToFrameMap subframe_content_map = {{1u, 1u}};
-  impl.AddSubframeContent(3u, mojo::SharedBufferHandle::Create(10),
-                          subframe_content_map);
+  impl.AddSubframeContent(3u, CreateTestData(10), subframe_content_map);
 
   // Frame 5 has content 3 which refers to subframe 3.
   // Although frame 3's content is added, its subframe 1's content is not added.
@@ -144,16 +144,14 @@
   EXPECT_EQ(*pending_subframes.begin(), 5u);
 
   // When frame 1's content is added, frame 5 is ready.
-  impl.AddSubframeContent(1u, mojo::SharedBufferHandle::Create(10),
-                          ContentToFrameMap());
+  impl.AddSubframeContent(1u, CreateTestData(10), ContentToFrameMap());
   subframe_content_map = {{3u, 3u}};
   EXPECT_TRUE(
       impl.IsReadyToComposite(5u, subframe_content_map, &pending_subframes));
   EXPECT_TRUE(pending_subframes.empty());
 
   // Add frame 5's content.
-  impl.AddSubframeContent(5u, mojo::SharedBufferHandle::Create(10),
-                          subframe_content_map);
+  impl.AddSubframeContent(5u, CreateTestData(10), subframe_content_map);
 
   // Frame 6 is ready too.
   subframe_content_map = {{1u, 5u}};
@@ -167,12 +165,10 @@
   // Frame 3 has content 1, which refers to frame 1.
   // Frame 1 has content 3, which refers to frame 3.
   ContentToFrameMap subframe_content_map = {{3u, 3u}};
-  impl.AddSubframeContent(1u, mojo::SharedBufferHandle::Create(10),
-                          subframe_content_map);
+  impl.AddSubframeContent(1u, CreateTestData(10), subframe_content_map);
 
   subframe_content_map = {{1u, 1u}};
-  impl.AddSubframeContent(3u, mojo::SharedBufferHandle::Create(10),
-                          subframe_content_map);
+  impl.AddSubframeContent(3u, CreateTestData(10), subframe_content_map);
 
   // Both frame 1 and 3 are painted, frame 5 should be ready.
   base::flat_set<uint64_t> pending_subframes;
@@ -183,8 +179,7 @@
 
   // Frame 6 has content 7, which refers to frame 7.
   subframe_content_map = {{7u, 7u}};
-  impl.AddSubframeContent(6, mojo::SharedBufferHandle::Create(10),
-                          subframe_content_map);
+  impl.AddSubframeContent(6, CreateTestData(10), subframe_content_map);
   // Frame 7 should be ready since frame 6's own content is added and it only
   // depends on frame 7.
   subframe_content_map = {{6u, 6u}};
@@ -200,14 +195,13 @@
   const ContentToFrameMap subframe_content_map = {{1u, 8u}};
   EXPECT_CALL(impl, OnFulfillRequest(testing::_, testing::_)).Times(0);
   impl.CompositePageToPdf(
-      3u, 0, mojo::SharedBufferHandle::Create(10), subframe_content_map,
+      3u, 0, CreateTestData(10), subframe_content_map,
       base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback));
   testing::Mock::VerifyAndClearExpectations(&impl);
 
   // When frame 8's content is ready, the previous request should be fulfilled.
   EXPECT_CALL(impl, OnFulfillRequest(3u, 0)).Times(1);
-  impl.AddSubframeContent(8u, mojo::SharedBufferHandle::Create(10),
-                          ContentToFrameMap());
+  impl.AddSubframeContent(8u, CreateTestData(10), ContentToFrameMap());
   testing::Mock::VerifyAndClearExpectations(&impl);
 
   // The following requests which only depends on frame 8 should be
@@ -215,11 +209,11 @@
   EXPECT_CALL(impl, OnFulfillRequest(3u, 1)).Times(1);
   EXPECT_CALL(impl, OnFulfillRequest(3u, -1)).Times(1);
   impl.CompositePageToPdf(
-      3u, 1, mojo::SharedBufferHandle::Create(10), subframe_content_map,
+      3u, 1, CreateTestData(10), subframe_content_map,
       base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback));
 
   impl.CompositeDocumentToPdf(
-      3u, mojo::SharedBufferHandle::Create(10), subframe_content_map,
+      3u, CreateTestData(10), subframe_content_map,
       base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback));
 }
 
@@ -230,17 +224,17 @@
   const ContentToFrameMap subframe_content_map = {{1u, 8u}};
   EXPECT_CALL(impl, OnFulfillRequest(testing::_, testing::_)).Times(0);
   impl.CompositePageToPdf(
-      3u, 0, mojo::SharedBufferHandle::Create(10), subframe_content_map,
+      3u, 0, CreateTestData(10), subframe_content_map,
       base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback));
 
   // The following requests which only depends on frame 8 should be
   // immediately fulfilled.
   impl.CompositePageToPdf(
-      3u, 1, mojo::SharedBufferHandle::Create(10), subframe_content_map,
+      3u, 1, CreateTestData(10), subframe_content_map,
       base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback));
 
   impl.CompositeDocumentToPdf(
-      3u, mojo::SharedBufferHandle::Create(10), subframe_content_map,
+      3u, CreateTestData(10), subframe_content_map,
       base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback));
   testing::Mock::VerifyAndClearExpectations(&impl);
 
@@ -249,8 +243,7 @@
   EXPECT_CALL(impl, OnFulfillRequest(3u, 0)).Times(1);
   EXPECT_CALL(impl, OnFulfillRequest(3u, 1)).Times(1);
   EXPECT_CALL(impl, OnFulfillRequest(3u, -1)).Times(1);
-  impl.AddSubframeContent(8u, mojo::SharedBufferHandle::Create(10),
-                          ContentToFrameMap());
+  impl.AddSubframeContent(8u, CreateTestData(10), ContentToFrameMap());
 }
 
 TEST_F(PdfCompositorImplTest, MultiRequestsDepOrder) {
@@ -261,7 +254,7 @@
   EXPECT_CALL(impl, OnFulfillRequest(testing::_, testing::_)).Times(0);
   ContentToFrameMap subframe_content_map = {{1u, 2u}};
   impl.CompositePageToPdf(
-      1u, 0, mojo::SharedBufferHandle::Create(10), subframe_content_map,
+      1u, 0, CreateTestData(10), subframe_content_map,
       base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback));
 
   // Page 1 with frame 1 has content 1, which refers to frame
@@ -269,7 +262,7 @@
   // fulfilled either.
   subframe_content_map = {{1u, 3u}};
   impl.CompositePageToPdf(
-      1u, 1, mojo::SharedBufferHandle::Create(10), subframe_content_map,
+      1u, 1, CreateTestData(10), subframe_content_map,
       base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback));
   testing::Mock::VerifyAndClearExpectations(&impl);
 
@@ -278,10 +271,8 @@
   testing::Sequence order;
   EXPECT_CALL(impl, OnFulfillRequest(1, 1)).Times(1).InSequence(order);
   EXPECT_CALL(impl, OnFulfillRequest(1, 0)).Times(1).InSequence(order);
-  impl.AddSubframeContent(3u, mojo::SharedBufferHandle::Create(10),
-                          ContentToFrameMap());
-  impl.AddSubframeContent(2u, mojo::SharedBufferHandle::Create(10),
-                          ContentToFrameMap());
+  impl.AddSubframeContent(3u, CreateTestData(10), ContentToFrameMap());
+  impl.AddSubframeContent(2u, CreateTestData(10), ContentToFrameMap());
 }
 
 TEST_F(PdfCompositorImplTest, NotifyUnavailableSubframe) {
@@ -291,7 +282,7 @@
   const ContentToFrameMap subframe_content_map = {{1u, 8u}};
   EXPECT_CALL(impl, OnFulfillRequest(testing::_, testing::_)).Times(0);
   impl.CompositePageToPdf(
-      3u, 0, mojo::SharedBufferHandle::Create(10), subframe_content_map,
+      3u, 0, CreateTestData(10), subframe_content_map,
       base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback));
   testing::Mock::VerifyAndClearExpectations(&impl);
 
diff --git a/components/services/pdf_compositor/pdf_compositor_service.cc b/components/services/pdf_compositor/pdf_compositor_service.cc
index 3cd761e3..34bf7a2e 100644
--- a/components/services/pdf_compositor/pdf_compositor_service.cc
+++ b/components/services/pdf_compositor/pdf_compositor_service.cc
@@ -18,7 +18,7 @@
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/service_context.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/base/ui_base_features.h"
 
 #if defined(OS_WIN)
diff --git a/components/services/pdf_compositor/pdf_compositor_service_unittest.cc b/components/services/pdf_compositor/pdf_compositor_service_unittest.cc
index ccdc45d..48c4e63 100644
--- a/components/services/pdf_compositor/pdf_compositor_service_unittest.cc
+++ b/components/services/pdf_compositor/pdf_compositor_service_unittest.cc
@@ -6,10 +6,7 @@
 #include <utility>
 
 #include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
 #include "base/memory/read_only_shared_memory_region.h"
-#include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/test/test_discardable_memory_allocator.h"
 #include "cc/paint/paint_flags.h"
diff --git a/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.cc b/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.cc
index b814cf60..d41eec7 100644
--- a/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.cc
+++ b/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.cc
@@ -7,26 +7,10 @@
 #include <utility>
 
 #include "base/memory/shared_memory.h"
-#include "base/memory/writable_shared_memory_region.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 
 namespace printing {
 
-base::MappedReadOnlyRegion CreateReadOnlySharedMemoryRegion(size_t size) {
-  mojo::ScopedSharedBufferHandle handle =
-      mojo::SharedBufferHandle::Create(size);
-  base::WritableSharedMemoryRegion writable_region =
-      UnwrapWritableSharedMemoryRegion(std::move(handle));
-  base::WritableSharedMemoryMapping mapping = writable_region.Map();
-  if (!mapping.IsValid())
-    return {};
-
-  base::ReadOnlySharedMemoryRegion readonly_region =
-      base::WritableSharedMemoryRegion::ConvertToReadOnly(
-          std::move(writable_region));
-  return {std::move(readonly_region), std::move(mapping)};
-}
-
 std::unique_ptr<base::SharedMemory> GetShmFromMojoHandle(
     mojo::ScopedSharedBufferHandle handle) {
   base::SharedMemoryHandle memory_handle;
diff --git a/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.h b/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.h
index 1e0bd92f..acb864b 100644
--- a/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.h
+++ b/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.h
@@ -7,7 +7,6 @@
 
 #include <memory>
 
-#include "base/memory/read_only_shared_memory_region.h"
 #include "mojo/public/cpp/system/buffer.h"
 
 namespace base {
@@ -16,10 +15,6 @@
 
 namespace printing {
 
-// Similar to base::ReadOnlySharedMemoryRegion::Create(), except it works inside
-// sandboxed environments.
-base::MappedReadOnlyRegion CreateReadOnlySharedMemoryRegion(size_t size);
-
 std::unique_ptr<base::SharedMemory> GetShmFromMojoHandle(
     mojo::ScopedSharedBufferHandle handle);
 
diff --git a/components/viz/host/BUILD.gn b/components/viz/host/BUILD.gn
index 93fc819..2b0df116 100644
--- a/components/viz/host/BUILD.gn
+++ b/components/viz/host/BUILD.gn
@@ -53,9 +53,9 @@
   public_deps = [
     "//gpu/command_buffer/client",
     "//gpu/ipc/host",
-    "//services/ui/public/interfaces",
     "//services/viz/privileged/interfaces/compositing",
     "//services/viz/public/interfaces",
+    "//services/ws/public/mojom",
     "//ui/gfx/geometry",
   ]
 
diff --git a/components/viz/host/DEPS b/components/viz/host/DEPS
index 1fe6066e..7450578 100644
--- a/components/viz/host/DEPS
+++ b/components/viz/host/DEPS
@@ -10,10 +10,10 @@
   "+gpu/ipc/common",
   "+gpu/ipc/host",
   "+mojo/public/cpp",
-  "+services/ui/public/interfaces",
   "+services/viz/privileged/interfaces",
   "+services/viz/public/interfaces",
   "+services/viz/public/interfaces/hit_test",
+  "+services/ws/public/mojom",
   "+skia",
   "+third_party/skia",
   "+ui/accelerated_widget_mac",
diff --git a/components/viz/host/gpu_client.h b/components/viz/host/gpu_client.h
index 7a388be..f968ad8 100644
--- a/components/viz/host/gpu_client.h
+++ b/components/viz/host/gpu_client.h
@@ -10,7 +10,7 @@
 #include "components/viz/host/gpu_client_delegate.h"
 #include "components/viz/host/viz_host_export.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 
 namespace viz {
 
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index 33fc82ac..2034f07 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -358,8 +358,8 @@
     "//gpu/ipc/service",
     "//media",
     "//media/capture:capture_lib",
-    "//services/ui/public/interfaces",
     "//services/viz/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index e87dc33..96086de 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -53,12 +53,6 @@
 constexpr bool kChildIsRoot = false;
 constexpr bool kNeedsSyncPoints = false;
 
-SurfaceId InvalidSurfaceId() {
-  static SurfaceId invalid(
-      FrameSinkId(), LocalSurfaceId(0xdeadbeef, 0xdeadbeef, kArbitraryToken));
-  return invalid;
-}
-
 gfx::Size SurfaceSize() {
   static gfx::Size size(100, 100);
   return size;
@@ -139,23 +133,20 @@
 
     // If |fallback_surface_id| is a valid surface Id then this will generate
     // two SurfaceDrawQuads.
-    static Quad SurfaceQuad(const SurfaceId& primary_surface_id,
-                            const SurfaceId& fallback_surface_id,
+    static Quad SurfaceQuad(const SurfaceRange& surface_range,
                             SkColor default_background_color,
                             const gfx::Rect& primary_surface_rect,
                             bool stretch_content_to_fill_bounds) {
       Quad quad;
       quad.material = DrawQuad::SURFACE_CONTENT;
       quad.primary_surface_rect = primary_surface_rect;
-      quad.primary_surface_id = primary_surface_id;
-      quad.fallback_surface_id = fallback_surface_id;
+      quad.surface_range = surface_range;
       quad.default_background_color = default_background_color;
       quad.stretch_content_to_fill_bounds = stretch_content_to_fill_bounds;
       return quad;
     }
 
-    static Quad SurfaceQuad(const SurfaceId& primary_surface_id,
-                            const SurfaceId& fallback_surface_id,
+    static Quad SurfaceQuad(const SurfaceRange& surface_range,
                             SkColor default_background_color,
                             const gfx::Rect& primary_surface_rect,
                             float opacity,
@@ -166,8 +157,7 @@
       quad.primary_surface_rect = primary_surface_rect;
       quad.opacity = opacity;
       quad.to_target_transform = transform;
-      quad.primary_surface_id = primary_surface_id;
-      quad.fallback_surface_id = fallback_surface_id;
+      quad.surface_range = surface_range;
       quad.default_background_color = default_background_color;
       quad.stretch_content_to_fill_bounds = stretch_content_to_fill_bounds;
       return quad;
@@ -182,8 +172,7 @@
 
     DrawQuad::Material material;
     // Set when material==DrawQuad::SURFACE_CONTENT.
-    SurfaceId primary_surface_id;
-    SurfaceId fallback_surface_id;
+    SurfaceRange surface_range;
     SkColor default_background_color;
     bool stretch_content_to_fill_bounds;
     gfx::Rect primary_surface_rect;
@@ -231,8 +220,8 @@
         break;
       case DrawQuad::SURFACE_CONTENT:
         AddSurfaceQuad(pass, desc.primary_surface_rect, desc.opacity,
-                       desc.to_target_transform, desc.primary_surface_id,
-                       desc.fallback_surface_id, desc.default_background_color,
+                       desc.to_target_transform, desc.surface_range,
+                       desc.default_background_color,
                        desc.stretch_content_to_fill_bounds);
         break;
       case DrawQuad::RENDER_PASS:
@@ -312,8 +301,7 @@
                              const gfx::Rect& primary_surface_rect,
                              float opacity,
                              const gfx::Transform& transform,
-                             const SurfaceId& primary_surface_id,
-                             const SurfaceId& fallback_surface_id,
+                             const SurfaceRange& surface_range,
                              SkColor default_background_color,
                              bool stretch_content_to_fill_bounds) {
     gfx::Transform layer_to_target_transform = transform;
@@ -331,14 +319,10 @@
 
     SurfaceDrawQuad* surface_quad =
         pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
-    surface_quad->SetNew(
-        pass->shared_quad_state_list.back(), primary_surface_rect,
-        primary_surface_rect,
-        SurfaceRange(fallback_surface_id.is_valid()
-                         ? base::Optional<SurfaceId>(fallback_surface_id)
-                         : base::nullopt,
-                     primary_surface_id),
-        default_background_color, stretch_content_to_fill_bounds);
+    surface_quad->SetNew(pass->shared_quad_state_list.back(),
+                         primary_surface_rect, primary_surface_rect,
+                         surface_range, default_background_color,
+                         stretch_content_to_fill_bounds);
   }
 
   static void AddRenderPassQuad(RenderPass* pass, RenderPassId render_pass_id) {
@@ -537,9 +521,9 @@
                         device_scale_factor);
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
   {
-    Quad quads[] = {Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(),
-                                      SK_ColorWHITE, gfx::Rect(5, 5), .5f,
-                                      gfx::Transform(), false)};
+    Quad quads[] = {Quad::SurfaceQuad(
+        SurfaceRange(base::nullopt, embedded_surface_id), SK_ColorWHITE,
+        gfx::Rect(5, 5), .5f, gfx::Transform(), false)};
     Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
 
     SubmitCompositorFrame(support_.get(), passes, base::size(passes),
@@ -559,9 +543,9 @@
   // For the case where opacity is close to 1.f, we treat it as opaque, and not
   // use a render surface.
   {
-    Quad quads[] = {Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(),
-                                      SK_ColorWHITE, gfx::Rect(5, 5), .9999f,
-                                      gfx::Transform(), false)};
+    Quad quads[] = {Quad::SurfaceQuad(
+        SurfaceRange(base::nullopt, embedded_surface_id), SK_ColorWHITE,
+        gfx::Rect(5, 5), .9999f, gfx::Transform(), false)};
     Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
 
     SubmitCompositorFrame(support_.get(), passes, base::size(passes),
@@ -596,9 +580,9 @@
                         device_scale_factor);
   gfx::Transform rotate;
   rotate.Rotate(30);
-  Quad quads[] = {Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(),
-                                    SK_ColorWHITE, gfx::Rect(5, 5), 1.f, rotate,
-                                    false)};
+  Quad quads[] = {
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, embedded_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), 1.f, rotate, false)};
   Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
 
   SubmitCompositorFrame(support_.get(), passes, base::size(passes),
@@ -715,8 +699,8 @@
 
   Quad root_quads[] = {
       Quad::SolidColorQuad(SK_ColorWHITE, gfx::Rect(5, 5)),
-      Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false),
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, embedded_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false),
       Quad::SolidColorQuad(SK_ColorBLACK, gfx::Rect(5, 5))};
   Pass root_passes[] = {
       Pass(root_quads, base::size(root_quads), SurfaceSize())};
@@ -771,9 +755,9 @@
   // Try to embed |primary_child_surface_id| and if unavailable, embed
   // |fallback_child_surface_id|.
   constexpr gfx::Rect surface_quad_rect(12, 15);
-  Quad root_quads[] = {
-      Quad::SurfaceQuad(primary_child_surface_id, fallback_child_surface_id,
-                        SK_ColorWHITE, surface_quad_rect, false)};
+  Quad root_quads[] = {Quad::SurfaceQuad(
+      SurfaceRange(fallback_child_surface_id, primary_child_surface_id),
+      SK_ColorWHITE, surface_quad_rect, false)};
   Pass root_passes[] = {
       Pass(root_quads, base::size(root_quads), SurfaceSize())};
 
@@ -918,9 +902,9 @@
   }
 
   constexpr gfx::Rect surface_quad_rect(10, 5);
-  Quad root_quads[] = {
-      Quad::SurfaceQuad(primary_child_surface_id, primary_child_surface_id,
-                        SK_ColorWHITE, surface_quad_rect, true)};
+  Quad root_quads[] = {Quad::SurfaceQuad(SurfaceRange(primary_child_surface_id),
+                                         SK_ColorWHITE, surface_quad_rect,
+                                         true)};
   Pass root_passes[] = {
       Pass(root_quads, base::size(root_quads), SurfaceSize())};
 
@@ -987,9 +971,9 @@
   }
 
   constexpr gfx::Rect surface_quad_rect(10, 5);
-  Quad root_quads[] = {
-      Quad::SurfaceQuad(primary_child_surface_id, primary_child_surface_id,
-                        SK_ColorWHITE, surface_quad_rect, true)};
+  Quad root_quads[] = {Quad::SurfaceQuad(SurfaceRange(primary_child_surface_id),
+                                         SK_ColorWHITE, surface_quad_rect,
+                                         true)};
   Pass root_passes[] = {
       Pass(root_quads, base::size(root_quads), SurfaceSize())};
 
@@ -1057,9 +1041,9 @@
   }
 
   constexpr gfx::Rect surface_quad_rect(10, 5);
-  Quad root_quads[] = {
-      Quad::SurfaceQuad(primary_child_surface_id, primary_child_surface_id,
-                        SK_ColorWHITE, surface_quad_rect, true)};
+  Quad root_quads[] = {Quad::SurfaceQuad(SurfaceRange(primary_child_surface_id),
+                                         SK_ColorWHITE, surface_quad_rect,
+                                         true)};
   Pass root_passes[] = {
       Pass(root_quads, base::size(root_quads), SurfaceSize())};
 
@@ -1138,9 +1122,9 @@
 
   // Try to embed |primary_child_surface_id| and if unavailabe, embed
   // |fallback_child_surface_id|.
-  Quad root_quads[] = {
-      Quad::SurfaceQuad(primary_child_surface_id, fallback_child_surface_id,
-                        SK_ColorWHITE, gfx::Rect(5, 5), false)};
+  Quad root_quads[] = {Quad::SurfaceQuad(
+      SurfaceRange(fallback_child_surface_id, primary_child_surface_id),
+      SK_ColorWHITE, gfx::Rect(5, 5), false)};
   constexpr gfx::Size root_size(75, 75);
   Pass root_passes[] = {
       Pass(root_quads, base::size(root_quads), root_size, NoDamage())};
@@ -1214,8 +1198,8 @@
 
   Quad root_quads[] = {
       Quad::SolidColorQuad(SK_ColorWHITE, gfx::Rect(5, 5)),
-      Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false),
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, embedded_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false),
       Quad::SolidColorQuad(SK_ColorBLACK, gfx::Rect(5, 5))};
   Pass root_passes[] = {
       Pass(root_quads, base::size(root_quads), SurfaceSize())};
@@ -1276,8 +1260,8 @@
 
   Quad root_quads[] = {
       Quad::SolidColorQuad(SK_ColorWHITE, gfx::Rect(5, 5)),
-      Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false),
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, embedded_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false),
       Quad::SolidColorQuad(SK_ColorBLACK, gfx::Rect(5, 5))};
   Quad root_quads2[] = {Quad::SolidColorQuad(SK_ColorRED, gfx::Rect(5, 5))};
   Pass root_passes[] = {
@@ -1366,8 +1350,8 @@
 
   Quad parent_quads[] = {
       Quad::SolidColorQuad(SK_ColorGRAY, gfx::Rect(5, 5)),
-      Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false),
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, embedded_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false),
       Quad::SolidColorQuad(SK_ColorLTGRAY, gfx::Rect(5, 5))};
   Pass parent_passes[] = {
       Pass(parent_quads, base::size(parent_quads), SurfaceSize())};
@@ -1461,8 +1445,8 @@
   Quad root_quads[][2] = {
       {Quad::SolidColorQuad(5, gfx::Rect(5, 5)),
        Quad::SolidColorQuad(6, gfx::Rect(5, 5))},
-      {Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                         gfx::Rect(5, 5), false),
+      {Quad::SurfaceQuad(SurfaceRange(base::nullopt, embedded_surface_id),
+                         SK_ColorWHITE, gfx::Rect(5, 5), false),
        Quad::RenderPassQuad(pass_ids[0])},
       {Quad::SolidColorQuad(7, gfx::Rect(5, 5)),
        Quad::RenderPassQuad(pass_ids[1])}};
@@ -1583,8 +1567,11 @@
 // be dropped.
 TEST_F(SurfaceAggregatorValidSurfaceTest, InvalidSurfaceReference) {
   Quad quads[] = {Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
-                  Quad::SurfaceQuad(InvalidSurfaceId(), InvalidSurfaceId(),
-                                    SK_ColorWHITE, gfx::Rect(5, 5), false),
+                  Quad::SurfaceQuad(
+                      SurfaceRange(SurfaceId(
+                          FrameSinkId(), LocalSurfaceId(0xdeadbeef, 0xdeadbeef,
+                                                        kArbitraryToken))),
+                      SK_ColorWHITE, gfx::Rect(5, 5), false),
                   Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
   Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
 
@@ -1611,7 +1598,7 @@
 
   Quad quads[] = {
       Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
-      Quad::SurfaceQuad(surface_with_no_frame_id, InvalidSurfaceId(),
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, surface_with_no_frame_id),
                         SK_ColorYELLOW, gfx::Rect(5, 5), false),
       Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
   Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
@@ -1640,9 +1627,8 @@
   const SurfaceId surface_with_no_frame_id(support_->frame_sink_id(),
                                            empty_local_surface_id);
 
-  Quad quads[] = {Quad::SurfaceQuad(surface_with_no_frame_id,
-                                    surface_with_no_frame_id, SK_ColorYELLOW,
-                                    gfx::Rect(5, 5), false)};
+  Quad quads[] = {Quad::SurfaceQuad(SurfaceRange(surface_with_no_frame_id),
+                                    SK_ColorYELLOW, gfx::Rect(5, 5), false)};
   Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
 
   constexpr float device_scale_factor = 1.0f;
@@ -1668,9 +1654,10 @@
 // The quad creating the cycle should be dropped from the final frame.
 TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleCyclicalReference) {
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  Quad quads[] = {Quad::SurfaceQuad(root_surface_id, InvalidSurfaceId(),
-                                    SK_ColorWHITE, gfx::Rect(5, 5), false),
-                  Quad::SolidColorQuad(SK_ColorYELLOW, gfx::Rect(5, 5))};
+  Quad quads[] = {
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, root_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false),
+      Quad::SolidColorQuad(SK_ColorYELLOW, gfx::Rect(5, 5))};
   Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
 
   constexpr float device_scale_factor = 1.0f;
@@ -1694,8 +1681,8 @@
 
   Quad parent_quads[] = {
       Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5)),
-      Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false),
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false),
       Quad::SolidColorQuad(SK_ColorCYAN, gfx::Rect(5, 5))};
   Pass parent_passes[] = {
       Pass(parent_quads, base::size(parent_quads), SurfaceSize())};
@@ -1708,8 +1695,8 @@
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
   Quad child_quads[] = {
       Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
-      Quad::SurfaceQuad(root_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false),
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, root_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false),
       Quad::SolidColorQuad(SK_ColorMAGENTA, gfx::Rect(5, 5))};
   Pass child_passes[] = {
       Pass(child_quads, base::size(child_quads), SurfaceSize())};
@@ -1760,8 +1747,8 @@
   // Pass IDs from the parent surface may collide with ones from the child.
   RenderPassId parent_pass_id[] = {3u, 2u};
   Quad parent_quad[][1] = {
-      {Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                         gfx::Rect(5, 5), false)},
+      {Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                         SK_ColorWHITE, gfx::Rect(5, 5), false)},
       {Quad::RenderPassQuad(parent_pass_id[0])}};
   Pass parent_passes[] = {Pass(parent_quad[0], base::size(parent_quad[0]),
                                parent_pass_id[0], SurfaceSize()),
@@ -2030,9 +2017,9 @@
   SurfaceId middle_surface_id(middle_support->frame_sink_id(),
                               middle_local_surface_id);
   {
-    Quad middle_quads[] = {Quad::SurfaceQuad(child_surface_id,
-                                             InvalidSurfaceId(), SK_ColorWHITE,
-                                             gfx::Rect(5, 5), false)};
+    Quad middle_quads[] = {
+        Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                          SK_ColorWHITE, gfx::Rect(5, 5), false)};
     Pass middle_passes[] = {
         Pass(middle_quads, base::size(middle_quads), SurfaceSize()),
     };
@@ -2056,8 +2043,8 @@
   // Root surface.
   Quad secondary_quads[] = {
       Quad::SolidColorQuad(1, gfx::Rect(5, 5)),
-      Quad::SurfaceQuad(middle_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false)};
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, middle_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false)};
   Quad root_quads[] = {Quad::SolidColorQuad(1, gfx::Rect(5, 5))};
   Pass root_passes[] = {
       Pass(secondary_quads, base::size(secondary_quads), SurfaceSize()),
@@ -2183,8 +2170,8 @@
                                         std::move(child_frame));
 
   Quad parent_surface_quads[] = {
-      Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false)};
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false)};
   Pass parent_surface_passes[] = {Pass(parent_surface_quads,
                                        base::size(parent_surface_quads), 1,
                                        SurfaceSize())};
@@ -2202,8 +2189,8 @@
                                         std::move(parent_surface_frame));
 
   Quad root_surface_quads[] = {
-      Quad::SurfaceQuad(parent_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false)};
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, parent_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false)};
   Quad root_render_pass_quads[] = {Quad::RenderPassQuad(1)};
 
   Pass root_passes[] = {
@@ -2370,8 +2357,8 @@
                                         std::move(child_frame));
 
   Quad parent_surface_quads[] = {
-      Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false)};
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false)};
   Pass parent_surface_passes[] = {Pass(parent_surface_quads,
                                        base::size(parent_surface_quads), 1,
                                        SurfaceSize())};
@@ -2389,8 +2376,8 @@
                                         std::move(parent_surface_frame));
 
   Quad root_surface_quads[] = {
-      Quad::SurfaceQuad(parent_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(50, 50), true)};
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, parent_surface_id),
+                        SK_ColorWHITE, gfx::Rect(50, 50), true)};
   Quad root_render_pass_quads[] = {Quad::RenderPassQuad(1)};
 
   Pass root_passes[] = {
@@ -2476,8 +2463,8 @@
                                         std::move(child_frame));
 
   Quad parent_surface_quads[] = {
-      Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false)};
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false)};
   Pass parent_surface_passes[] = {Pass(parent_surface_quads,
                                        base::size(parent_surface_quads), 1,
                                        SurfaceSize())};
@@ -2495,8 +2482,8 @@
                                         std::move(parent_surface_frame));
 
   Quad root_surface_quads[] = {
-      Quad::SurfaceQuad(parent_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(200, 200), true)};
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, parent_surface_id),
+                        SK_ColorWHITE, gfx::Rect(200, 200), true)};
   Quad root_render_pass_quads[] = {Quad::RenderPassQuad(1)};
 
   Pass root_passes[] = {
@@ -2647,9 +2634,9 @@
   constexpr float device_scale_factor = 1.0f;
   SubmitCompositorFrame(embedded_support.get(), embedded_passes,
                         base::size(embedded_passes), id2, device_scale_factor);
-  Quad quads[] = {Quad::SurfaceQuad(primary_surface_id, fallback_surface_id,
-                                    SK_ColorWHITE, gfx::Rect(5, 5), 1.f,
-                                    gfx::Transform(), false)};
+  Quad quads[] = {Quad::SurfaceQuad(
+      SurfaceRange(fallback_surface_id, primary_surface_id), SK_ColorWHITE,
+      gfx::Rect(5, 5), 1.f, gfx::Transform(), false)};
   Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
 
   SubmitCompositorFrame(support_.get(), passes, base::size(passes),
@@ -2705,9 +2692,9 @@
   constexpr float device_scale_factor = 1.0f;
   SubmitCompositorFrame(embedded_support.get(), embedded_passes,
                         base::size(embedded_passes), id2, device_scale_factor);
-  Quad quads[] = {Quad::SurfaceQuad(primary_surface_id, fallback_surface_id,
-                                    SK_ColorWHITE, gfx::Rect(5, 5), 1.f,
-                                    gfx::Transform(), false)};
+  Quad quads[] = {Quad::SurfaceQuad(
+      SurfaceRange(fallback_surface_id, primary_surface_id), SK_ColorWHITE,
+      gfx::Rect(5, 5), 1.f, gfx::Transform(), false)};
   Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
 
   SubmitCompositorFrame(support_.get(), passes, base::size(passes),
@@ -2752,9 +2739,9 @@
   LocalSurfaceId id2 = allocator_.GenerateId();
   LocalSurfaceId id3 = allocator_.GenerateId();
   SurfaceId primary_surface_id(kArbitraryFrameSinkId1, id2);
-  Quad quads[] = {Quad::SurfaceQuad(primary_surface_id, InvalidSurfaceId(),
-                                    SK_ColorWHITE, gfx::Rect(5, 5), 1.f,
-                                    gfx::Transform(), false)};
+  Quad quads[] = {Quad::SurfaceQuad(
+      SurfaceRange(base::nullopt, primary_surface_id), SK_ColorWHITE,
+      gfx::Rect(5, 5), 1.f, gfx::Transform(), false)};
   Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
 
   constexpr float device_scale_factor = 1.0f;
@@ -2802,7 +2789,7 @@
   SubmitCompositorFrame(embedded_support.get(), embedded_passes,
                         base::size(embedded_passes), id2, device_scale_factor);
 
-  Quad quads[] = {Quad::SurfaceQuad(surface_id, surface_id, SK_ColorWHITE,
+  Quad quads[] = {Quad::SurfaceQuad(SurfaceRange(surface_id), SK_ColorWHITE,
                                     gfx::Rect(5, 5), 1.f, gfx::Transform(),
                                     false)};
   Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
@@ -2885,9 +2872,9 @@
   }
 
   {
-    Quad root_quads[] = {Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(),
-                                           SK_ColorWHITE, gfx::Rect(5, 5),
-                                           false)};
+    Quad root_quads[] = {
+        Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                          SK_ColorWHITE, gfx::Rect(5, 5), false)};
 
     Pass root_passes[] = {
         Pass(root_quads, base::size(root_quads), SurfaceSize())};
@@ -2920,9 +2907,9 @@
 
   // Create a root surface with a smaller damage rect.
   {
-    Quad root_quads[] = {Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(),
-                                           SK_ColorWHITE, gfx::Rect(5, 5),
-                                           false)};
+    Quad root_quads[] = {
+        Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                          SK_ColorWHITE, gfx::Rect(5, 5), false)};
 
     Pass root_passes[] = {
         Pass(root_quads, base::size(root_quads), SurfaceSize())};
@@ -3025,9 +3012,9 @@
   // of it and its descendant passes should be aggregated.
   {
     int root_pass_ids[] = {1, 2, 3};
-    Quad root_quads1[] = {Quad::SurfaceQuad(child_surface_id,
-                                            InvalidSurfaceId(), SK_ColorWHITE,
-                                            gfx::Rect(5, 5), false)};
+    Quad root_quads1[] = {
+        Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                          SK_ColorWHITE, gfx::Rect(5, 5), false)};
     Quad root_quads2[] = {Quad::RenderPassQuad(root_pass_ids[0])};
     Quad root_quads3[] = {Quad::RenderPassQuad(root_pass_ids[1])};
     Pass root_passes[] = {Pass(root_quads1, base::size(root_quads1),
@@ -3079,8 +3066,8 @@
     };
     Quad root_quads2[] = {
         Quad::RenderPassQuad(root_pass_ids[0]),
-        Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                          gfx::Rect(5, 5), false)};
+        Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                          SK_ColorWHITE, gfx::Rect(5, 5), false)};
     Pass root_passes[] = {Pass(root_quads1, base::size(root_quads1),
                                root_pass_ids[0], SurfaceSize()),
                           Pass(root_quads2, base::size(root_quads2),
@@ -3131,8 +3118,8 @@
     };
     Quad root_quads2[] = {
         Quad::RenderPassQuad(root_pass_ids[0]),
-        Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                          gfx::Rect(5, 5), false)};
+        Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                          SK_ColorWHITE, gfx::Rect(5, 5), false)};
     Pass root_passes[] = {Pass(root_quads1, base::size(root_quads1),
                                root_pass_ids[0], SurfaceSize()),
                           Pass(root_quads2, base::size(root_quads2),
@@ -3571,8 +3558,8 @@
                                         std::move(child_surface_frame));
 
   Quad root_surface_quads[] = {
-      Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false)};
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false)};
   Pass root_passes[] = {Pass(root_surface_quads, base::size(root_surface_quads),
                              1, SurfaceSize())};
 
@@ -3651,8 +3638,8 @@
                                         std::move(child_surface_frame));
 
   Quad root_surface_quads[] = {
-      Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false)};
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false)};
   Pass root_passes[] = {Pass(root_surface_quads, base::size(root_surface_quads),
                              1, SurfaceSize())};
 
@@ -3693,7 +3680,7 @@
 
     Quad new_child_surface_quads[] = {
         child_surface_quads[0],
-        Quad::SurfaceQuad(grand_child_surface_id, InvalidSurfaceId(),
+        Quad::SurfaceQuad(SurfaceRange(base::nullopt, grand_child_surface_id),
                           SK_ColorWHITE, gfx::Rect(5, 5), false)};
     Pass new_child_surface_passes[] = {Pass(new_child_surface_quads,
                                             base::size(new_child_surface_quads),
@@ -3769,8 +3756,8 @@
                                         std::move(child_frame));
 
   Quad root_surface_quads[] = {
-      Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false)};
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false)};
   Quad root_render_pass_quads[] = {Quad::RenderPassQuad(1)};
 
   Pass root_passes[] = {
@@ -3938,8 +3925,8 @@
                                         std::move(child_frame));
 
   Quad root_surface_quads[] = {
-      Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                        gfx::Rect(5, 5), false)};
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                        SK_ColorWHITE, gfx::Rect(5, 5), false)};
 
   Pass root_passes[] = {Pass(root_surface_quads, base::size(root_surface_quads),
                              1, SurfaceSize())};
@@ -4063,8 +4050,8 @@
   {
     int pass_id[] = {1, 2};
     Quad root_quads[][1] = {
-        {Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                           gfx::Rect(5, 5), false)},
+        {Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                           SK_ColorWHITE, gfx::Rect(5, 5), false)},
         {Quad::RenderPassQuad(pass_id[0])},
     };
     Pass root_passes[] = {Pass(root_quads[0], base::size(root_quads[0]),
@@ -4103,8 +4090,8 @@
   {
     int pass_id[] = {1, 2};
     Quad root_quads[][1] = {
-        {Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
-                           gfx::Rect(5, 5), false)},
+        {Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                           SK_ColorWHITE, gfx::Rect(5, 5), false)},
         {Quad::RenderPassQuad(pass_id[0])},
     };
     Pass root_passes[] = {Pass(root_quads[0], base::size(root_quads[0]),
diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
index a821cd5..dedcdc3 100644
--- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
@@ -140,6 +140,10 @@
     synthetic_begin_frame_source_->OnUpdateVSyncParameters(timebase, interval);
 }
 
+void RootCompositorFrameSinkImpl::ForceImmediateDrawAndSwapIfPossible() {
+  display_->ForceImmediateDrawAndSwapIfPossible();
+}
+
 #if defined(OS_ANDROID)
 void RootCompositorFrameSinkImpl::SetVSyncPaused(bool paused) {
   if (external_begin_frame_source_)
diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
index 94f3bb5e..7908859 100644
--- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
@@ -50,6 +50,7 @@
   void SetAuthoritativeVSyncInterval(base::TimeDelta interval) override;
   void SetDisplayVSyncParameters(base::TimeTicks timebase,
                                  base::TimeDelta interval) override;
+  void ForceImmediateDrawAndSwapIfPossible() override;
 #if defined(OS_ANDROID)
   void SetVSyncPaused(bool paused) override;
 #endif
diff --git a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
index 3393fc7..8174fbf 100644
--- a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
+++ b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
@@ -167,11 +167,9 @@
         surface_id);
   }
 
-  Surface* GetLatestInFlightSurface(
-      const SurfaceId& primary_surface_id,
-      const base::Optional<SurfaceId>& fallback_surface_id) {
+  Surface* GetLatestInFlightSurface(const SurfaceRange& surface_range) {
     return frame_sink_manager().surface_manager()->GetLatestInFlightSurface(
-        SurfaceRange(fallback_surface_id, primary_surface_id));
+        surface_range);
   }
 
   FakeExternalBeginFrameSource* begin_frame_source() {
@@ -2120,7 +2118,7 @@
   EXPECT_TRUE(HasTemporaryReference(child_id1));
   EXPECT_THAT(GetChildReferences(parent_id), IsEmpty());
   EXPECT_EQ(GetSurfaceForId(child_id1),
-            GetLatestInFlightSurface(child_id2, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id2)));
 
   parent_support().SubmitCompositorFrame(
       parent_id.local_surface_id(),
@@ -2137,7 +2135,7 @@
   EXPECT_FALSE(HasTemporaryReference(child_id1));
   EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1));
   EXPECT_EQ(GetSurfaceForId(child_id1),
-            GetLatestInFlightSurface(child_id2, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id2)));
 
   // Don't assign owner for temporary reference to |child_id2|.
   DisableAssignTemporaryReferences();
@@ -2153,12 +2151,12 @@
   EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1));
 
   EXPECT_EQ(GetSurfaceForId(child_id2),
-            GetLatestInFlightSurface(child_id3, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id3)));
 
   // If the primary surface is old, then we shouldn't return an in-flight
   // surface that is newer than the primary.
   EXPECT_EQ(GetSurfaceForId(child_id1),
-            GetLatestInFlightSurface(child_id1, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1)));
 
   // Submit a child CompositorFrame to a new SurfaceId and verify that
   // GetLatestInFlightSurface returns the right surface.
@@ -2169,12 +2167,12 @@
   EXPECT_TRUE(HasTemporaryReference(child_id3));
 
   EXPECT_EQ(GetSurfaceForId(child_id3),
-            GetLatestInFlightSurface(child_id4, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4)));
   EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1));
 
   // If the primary surface is active, we return it.
   EXPECT_EQ(GetSurfaceForId(child_id3),
-            GetLatestInFlightSurface(child_id3, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id3)));
 }
 
 // This test verifies that GetLatestInFlightSurface will return nullptr
@@ -2205,11 +2203,12 @@
 
   // If primary exists and active return it regardless of the fallback.
   EXPECT_EQ(GetSurfaceForId(child_id1),
-            GetLatestInFlightSurface(child_id1, bogus_child_id));
+            GetLatestInFlightSurface(SurfaceRange(bogus_child_id, child_id1)));
 
   // If primary is not active and fallback is doesn't exist, we always return
   // nullptr.
-  EXPECT_EQ(nullptr, GetLatestInFlightSurface(child_id2, bogus_child_id));
+  EXPECT_EQ(nullptr,
+            GetLatestInFlightSurface(SurfaceRange(bogus_child_id, child_id2)));
 }
 
 // This test verifies that GetLatestInFlightSurface will return the primary or
@@ -2241,11 +2240,11 @@
 
   // Verify that |child_id1| is the latest active surface.
   EXPECT_EQ(GetSurfaceForId(child_id1),
-            GetLatestInFlightSurface(child_id2, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id2)));
 
   // Fallback is not specified and |child_id1| is the latest.
   EXPECT_EQ(GetSurfaceForId(child_id1),
-            GetLatestInFlightSurface(child_id2, base::nullopt));
+            GetLatestInFlightSurface(SurfaceRange(base::nullopt, child_id2)));
 
   // Activate |child_id2|
   child_support1().SubmitCompositorFrame(child_id2.local_surface_id(),
@@ -2257,11 +2256,11 @@
 
   // Verify that |child_id2| is the latest active surface.
   EXPECT_EQ(GetSurfaceForId(child_id2),
-            GetLatestInFlightSurface(child_id2, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id2)));
 
   // Fallback is not specified but primary exists so we return it.
   EXPECT_EQ(GetSurfaceForId(child_id2),
-            GetLatestInFlightSurface(child_id2, base::nullopt));
+            GetLatestInFlightSurface(SurfaceRange(base::nullopt, child_id2)));
 }
 
 // This test verifies that GetLatestInFlightSurface will not return null if the
@@ -2343,7 +2342,7 @@
             nullptr);
 
   EXPECT_EQ(GetSurfaceForId(child_id3),
-            GetLatestInFlightSurface(child_id4, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4)));
 }
 
 // This test verifies that in the case of different frame sinks
@@ -2375,7 +2374,7 @@
   // Primary's frame sink id empty and |child_id2| is the latest in fallback's
   // frame sink.
   EXPECT_EQ(GetSurfaceForId(child_id2),
-            GetLatestInFlightSurface(child_id4, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4)));
 
   // Activate |child_id3| which is in different frame sink.
   child_support2().SubmitCompositorFrame(child_id3.local_surface_id(),
@@ -2383,7 +2382,7 @@
 
   // |child_id3| is the latest in primary's frame sink.
   EXPECT_EQ(GetSurfaceForId(child_id3),
-            GetLatestInFlightSurface(child_id4, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4)));
 }
 
 // This test verifies that GetLatestInFlightSurface will return the
@@ -2410,7 +2409,7 @@
                                          MakeDefaultCompositorFrame());
 
   EXPECT_EQ(GetSurfaceForId(child_id2),
-            GetLatestInFlightSurface(child_id3, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id3)));
 
   child_support1().SubmitCompositorFrame(child_id3.local_surface_id(),
                                          MakeDefaultCompositorFrame());
@@ -2418,7 +2417,7 @@
   // GetLatestInFlightSurface will return the primary surface ID if it's
   // available.
   EXPECT_EQ(GetSurfaceForId(child_id3),
-            GetLatestInFlightSurface(child_id3, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id3)));
 }
 
 // This test verifies that GetLatestInFlightSurface can use persistent
@@ -2463,7 +2462,7 @@
 
   // Verify that GetLatestInFlightSurface returns |child_id2|.
   EXPECT_EQ(GetSurfaceForId(child_id2),
-            GetLatestInFlightSurface(child_id3, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id3)));
 }
 
 // This test verifies that GetLatestInFlightSurface will skip a surface if
@@ -2503,7 +2502,7 @@
                                          MakeDefaultCompositorFrame());
 
   EXPECT_EQ(GetSurfaceForId(child_id2),
-            GetLatestInFlightSurface(child_id4, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4)));
 
   child_support1().SubmitCompositorFrame(child_id3.local_surface_id(),
                                          MakeDefaultCompositorFrame());
@@ -2511,12 +2510,12 @@
   // GetLatestInFlightSurface will return child_id3 because the nonce
   // matches that of child_id4.
   EXPECT_EQ(GetSurfaceForId(child_id3),
-            GetLatestInFlightSurface(child_id4, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4)));
 
   // GetLatestInFlightSurface will return child_id2 because the nonce
   // doesn't match |child_id1| or |child_id5|.
   EXPECT_EQ(GetSurfaceForId(child_id2),
-            GetLatestInFlightSurface(child_id5, child_id1));
+            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id5)));
 }
 
 // This test verifies that if a child submits a LocalSurfaceId newer that the
diff --git a/components/viz/service/gl/DEPS b/components/viz/service/gl/DEPS
index 1e459f8..b33ef28 100644
--- a/components/viz/service/gl/DEPS
+++ b/components/viz/service/gl/DEPS
@@ -19,6 +19,6 @@
 
 specific_include_rules = {
   "gpu_service_impl_unittest\.cc": [
-    "+services/ui/public/interfaces",
+    "+services/ws/public/mojom",
   ]
 }
diff --git a/components/viz/service/gl/gpu_service_impl_unittest.cc b/components/viz/service/gl/gpu_service_impl_unittest.cc
index ac88289..9f772c7 100644
--- a/components/viz/service/gl/gpu_service_impl_unittest.cc
+++ b/components/viz/service/gl/gpu_service_impl_unittest.cc
@@ -13,7 +13,7 @@
 #include "base/single_thread_task_runner.h"
 #include "gpu/config/gpu_info.h"
 #include "gpu/ipc/service/gpu_watchdog_thread.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace viz {
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc
index f75afe7..69b41d91 100644
--- a/components/viz/service/surfaces/surface.cc
+++ b/components/viz/service/surfaces/surface.cc
@@ -46,6 +46,10 @@
   UnrefFrameResourcesAndRunCallbacks(std::move(pending_frame_data_));
   UnrefFrameResourcesAndRunCallbacks(std::move(active_frame_data_));
 
+  // Remove this surface as an observer.
+  for (const FrameSinkId& sink_id : observed_sinks_)
+    surface_manager_->RemoveActivationObserver(sink_id, surface_info_.id());
+
   if (deadline_)
     deadline_->Cancel();
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index be7c107..fb02e52d 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2328,7 +2328,7 @@
   if (use_aura) {
     deps += [
       "//services/ui/public/cpp",
-      "//services/ui/public/interfaces",
+      "//services/ws/public/mojom",
       "//ui/aura",
       "//ui/aura_extra",
       "//ui/strings",
diff --git a/content/browser/devtools/protocol/browser_handler.cc b/content/browser/devtools/protocol/browser_handler.cc
index 618ca84..2c570588 100644
--- a/content/browser/devtools/protocol/browser_handler.cc
+++ b/content/browser/devtools/protocol/browser_handler.cc
@@ -13,7 +13,11 @@
 #include "base/metrics/statistics_recorder.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
+#include "content/browser/devtools/devtools_manager.h"
+#include "content/browser/permissions/permission_controller_impl.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/permission_type.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/user_agent.h"
@@ -27,6 +31,24 @@
 
 BrowserHandler::~BrowserHandler() {}
 
+Response BrowserHandler::Disable() {
+  for (auto& browser_context_id : contexts_with_overridden_permissions_) {
+    content::BrowserContext* browser_context = nullptr;
+    std::string error;
+    Maybe<std::string> context_id =
+        browser_context_id == "" ? Maybe<std::string>()
+                                 : Maybe<std::string>(browser_context_id);
+    FindBrowserContext(context_id, &browser_context);
+    if (browser_context) {
+      PermissionControllerImpl* permission_controller =
+          PermissionControllerImpl::FromBrowserContext(browser_context);
+      permission_controller->ResetPermissionOverridesForDevTools();
+    }
+  }
+  contexts_with_overridden_permissions_.clear();
+  return Response::OK();
+}
+
 void BrowserHandler::Wire(UberDispatcher* dispatcher) {
   Browser::Dispatcher::wire(dispatcher, this);
 }
@@ -81,6 +103,47 @@
       .Build();
 }
 
+Response FromProtocolPermissionType(
+    const protocol::Browser::PermissionType& type,
+    PermissionType* out_type) {
+  if (type == protocol::Browser::PermissionTypeEnum::Notifications) {
+    *out_type = PermissionType::NOTIFICATIONS;
+  } else if (type == protocol::Browser::PermissionTypeEnum::Geolocation) {
+    *out_type = PermissionType::GEOLOCATION;
+  } else if (type ==
+             protocol::Browser::PermissionTypeEnum::ProtectedMediaIdentifier) {
+    *out_type = PermissionType::PROTECTED_MEDIA_IDENTIFIER;
+  } else if (type == protocol::Browser::PermissionTypeEnum::Midi) {
+    *out_type = PermissionType::MIDI;
+  } else if (type == protocol::Browser::PermissionTypeEnum::MidiSysex) {
+    *out_type = PermissionType::MIDI_SYSEX;
+  } else if (type == protocol::Browser::PermissionTypeEnum::DurableStorage) {
+    *out_type = PermissionType::DURABLE_STORAGE;
+  } else if (type == protocol::Browser::PermissionTypeEnum::AudioCapture) {
+    *out_type = PermissionType::AUDIO_CAPTURE;
+  } else if (type == protocol::Browser::PermissionTypeEnum::VideoCapture) {
+    *out_type = PermissionType::VIDEO_CAPTURE;
+  } else if (type == protocol::Browser::PermissionTypeEnum::BackgroundSync) {
+    *out_type = PermissionType::BACKGROUND_SYNC;
+  } else if (type == protocol::Browser::PermissionTypeEnum::Flash) {
+    *out_type = PermissionType::FLASH;
+  } else if (type == protocol::Browser::PermissionTypeEnum::Sensors) {
+    *out_type = PermissionType::SENSORS;
+  } else if (type ==
+             protocol::Browser::PermissionTypeEnum::AccessibilityEvents) {
+    *out_type = PermissionType::ACCESSIBILITY_EVENTS;
+  } else if (type == protocol::Browser::PermissionTypeEnum::ClipboardRead) {
+    *out_type = PermissionType::CLIPBOARD_READ;
+  } else if (type == protocol::Browser::PermissionTypeEnum::ClipboardWrite) {
+    *out_type = PermissionType::CLIPBOARD_WRITE;
+  } else if (type == protocol::Browser::PermissionTypeEnum::PaymentHandler) {
+    *out_type = PermissionType::PAYMENT_HANDLER;
+  } else {
+    return Response::InvalidParams("Unknown permission type: " + type);
+  }
+  return Response::OK();
+}
+
 }  // namespace
 
 Response BrowserHandler::GetHistograms(
@@ -101,6 +164,72 @@
   return Response::OK();
 }
 
+Response BrowserHandler::FindBrowserContext(
+    const Maybe<std::string>& browser_context_id,
+    BrowserContext** browser_context) {
+  DevToolsManagerDelegate* delegate =
+      DevToolsManager::GetInstance()->delegate();
+  if (!delegate)
+    return Response::Error("Browser context management is not supported.");
+  if (!browser_context_id.isJust()) {
+    *browser_context = delegate->GetDefaultBrowserContext();
+    if (*browser_context == nullptr)
+      return Response::Error("Browser context management is not supported.");
+    return Response::OK();
+  }
+
+  std::string context_id = browser_context_id.fromJust();
+  for (auto* context : delegate->GetBrowserContexts()) {
+    if (context->UniqueId() == context_id) {
+      *browser_context = context;
+      return Response::OK();
+    }
+  }
+  return Response::InvalidParams("Failed to find browser context for id " +
+                                 context_id);
+}
+
+Response BrowserHandler::GrantPermissions(
+    const std::string& origin,
+    std::unique_ptr<protocol::Array<protocol::Browser::PermissionType>>
+        permissions,
+    Maybe<std::string> browser_context_id) {
+  BrowserContext* browser_context = nullptr;
+  Response response = FindBrowserContext(browser_context_id, &browser_context);
+  if (!response.isSuccess())
+    return response;
+  PermissionControllerImpl::PermissionOverrides overrides;
+  for (size_t i = 0; i < permissions->length(); ++i) {
+    PermissionType type;
+    Response type_response =
+        FromProtocolPermissionType(permissions->get(i), &type);
+    if (!type_response.isSuccess())
+      return type_response;
+    overrides.insert(type);
+  }
+
+  PermissionControllerImpl* permission_controller =
+      PermissionControllerImpl::FromBrowserContext(browser_context);
+  GURL url = GURL(origin).GetOrigin();
+  permission_controller->SetPermissionOverridesForDevTools(url, overrides);
+  contexts_with_overridden_permissions_.insert(
+      browser_context_id.fromMaybe(""));
+  return Response::OK();
+}
+
+Response BrowserHandler::ResetPermissions(
+    Maybe<std::string> browser_context_id) {
+  BrowserContext* browser_context = nullptr;
+  Response response = FindBrowserContext(browser_context_id, &browser_context);
+  if (!response.isSuccess())
+    return response;
+  PermissionControllerImpl* permission_controller =
+      PermissionControllerImpl::FromBrowserContext(browser_context);
+  permission_controller->ResetPermissionOverridesForDevTools();
+  contexts_with_overridden_permissions_.erase(browser_context_id.fromMaybe(""));
+  return Response::OK();
+}
+
 Response BrowserHandler::GetHistogram(
     const std::string& in_name,
     const Maybe<bool> in_delta,
@@ -119,8 +248,8 @@
 }
 
 Response BrowserHandler::GetBrowserCommandLine(
-    std::unique_ptr<protocol::Array<String>>* arguments) {
-  *arguments = protocol::Array<String>::create();
+    std::unique_ptr<protocol::Array<std::string>>* arguments) {
+  *arguments = protocol::Array<std::string>::create();
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   // The commandline is potentially sensitive, only return it if it
   // contains kEnableAutomation.
diff --git a/content/browser/devtools/protocol/browser_handler.h b/content/browser/devtools/protocol/browser_handler.h
index 9ced024..5175b0ff 100644
--- a/content/browser/devtools/protocol/browser_handler.h
+++ b/content/browser/devtools/protocol/browser_handler.h
@@ -5,11 +5,15 @@
 #ifndef CONTENT_BROWSER_DEVTOOLS_PROTOCOL_BROWSER_HANDLER_H_
 #define CONTENT_BROWSER_DEVTOOLS_PROTOCOL_BROWSER_HANDLER_H_
 
+#include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "content/browser/devtools/protocol/browser.h"
 #include "content/browser/devtools/protocol/devtools_domain_handler.h"
 
 namespace content {
+
+class BrowserContext;
+
 namespace protocol {
 
 class BrowserHandler : public DevToolsDomainHandler, public Browser::Backend {
@@ -19,6 +23,8 @@
 
   void Wire(UberDispatcher* dispatcher) override;
 
+  Response Disable() override;
+
   // Protocol methods.
   Response GetVersion(std::string* protocol_version,
                       std::string* product,
@@ -37,9 +43,22 @@
       std::unique_ptr<Browser::Histogram>* out_histogram) override;
 
   Response GetBrowserCommandLine(
-      std::unique_ptr<protocol::Array<String>>* arguments) override;
+      std::unique_ptr<protocol::Array<std::string>>* arguments) override;
+
+  Response GrantPermissions(
+      const std::string& origin,
+      std::unique_ptr<protocol::Array<protocol::Browser::PermissionType>>
+          permissions,
+      Maybe<std::string> browser_context_id) override;
+
+  Response ResetPermissions(Maybe<std::string> browser_context_id) override;
 
  private:
+  Response FindBrowserContext(const Maybe<std::string>& browser_context_id,
+                              BrowserContext** browser_context);
+
+  base::flat_set<std::string> contexts_with_overridden_permissions_;
+
   DISALLOW_COPY_AND_ASSIGN(BrowserHandler);
 };
 
diff --git a/content/browser/devtools/protocol_config.json b/content/browser/devtools/protocol_config.json
index 72482e3..1c77929 100644
--- a/content/browser/devtools/protocol_config.json
+++ b/content/browser/devtools/protocol_config.json
@@ -11,7 +11,7 @@
         "options": [
             {
                 "domain": "Browser",
-                "include": ["getVersion", "getHistograms", "getHistogram", "getBrowserCommandLine"]
+                "include": ["getVersion", "getHistograms", "getHistogram", "getBrowserCommandLine", "grantPermissions", "resetPermissions"]
             },
             {
                 "domain": "DOM",
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 0d8c115..afe116bc 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -77,7 +77,7 @@
 #include "services/service_manager/runner/common/client_util.h"
 #include "services/service_manager/sandbox/sandbox_type.h"
 #include "services/service_manager/sandbox/switches.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/display/display_switches.h"
diff --git a/content/browser/media/flinging_renderer.cc b/content/browser/media/flinging_renderer.cc
index e890f44..ae8234f 100644
--- a/content/browser/media/flinging_renderer.cc
+++ b/content/browser/media/flinging_renderer.cc
@@ -81,7 +81,6 @@
 void FlingingRenderer::StartPlayingFrom(base::TimeDelta time) {
   DVLOG(2) << __func__;
   controller_->GetMediaController()->Seek(time);
-  controller_->GetMediaController()->Play();
 }
 
 void FlingingRenderer::SetPlaybackRate(double playback_rate) {
@@ -98,8 +97,7 @@
 }
 
 base::TimeDelta FlingingRenderer::GetMediaTime() {
-  // TODO(https://crbug.com/830871): return correct media time.
-  return base::TimeDelta();
+  return controller_->GetApproximateCurrentTime();
 }
 
 void FlingingRenderer::OnMediaStatusUpdated(const media::MediaStatus& status) {
diff --git a/content/browser/media/flinging_renderer_unittest.cc b/content/browser/media/flinging_renderer_unittest.cc
index dade51a..3717bbc7 100644
--- a/content/browser/media/flinging_renderer_unittest.cc
+++ b/content/browser/media/flinging_renderer_unittest.cc
@@ -61,14 +61,12 @@
 
 TEST_F(FlingingRendererTest, StartPlayingFromTime) {
   base::TimeDelta seek_time = base::TimeDelta::FromSeconds(10);
-  EXPECT_CALL(*media_controller_, Play());
   EXPECT_CALL(*media_controller_, Seek(seek_time));
 
   renderer_->StartPlayingFrom(seek_time);
 }
 
 TEST_F(FlingingRendererTest, StartPlayingFromBeginning) {
-  EXPECT_CALL(*media_controller_, Play());
   EXPECT_CALL(*media_controller_, Seek(base::TimeDelta()));
 
   renderer_->StartPlayingFrom(base::TimeDelta());
diff --git a/content/browser/media/media_browsertest.cc b/content/browser/media/media_browsertest.cc
index 8e6045d5..93bea4f 100644
--- a/content/browser/media/media_browsertest.cc
+++ b/content/browser/media/media_browsertest.cc
@@ -156,11 +156,15 @@
   PlayVideo("bear.webm", GetParam());
 }
 
-IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearOpusWebm) {
+IN_PROC_BROWSER_TEST_P(MediaTest, AudioBearOpusWebm) {
   PlayVideo("bear-opus.webm", GetParam());
 }
 
-IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearOpusOgg) {
+IN_PROC_BROWSER_TEST_P(MediaTest, AudioBearOpusMp4) {
+  PlayVideo("bear-opus.mp4", GetParam());
+}
+
+IN_PROC_BROWSER_TEST_P(MediaTest, AudioBearOpusOgg) {
   PlayVideo("bear-opus.ogg", GetParam());
 }
 
@@ -179,11 +183,6 @@
 }
 #endif
 
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearMp4) {
-  PlayVideo("bear.mp4", GetParam());
-}
-
 IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearMp4Vp9) {
   PlayVideo("bear-320x240-v_frag-vp9.mp4", GetParam());
 }
@@ -196,20 +195,6 @@
   PlayAudio("bear-flac-192kHz.mp4", GetParam());
 }
 
-// Android devices usually only support baseline, main and high.
-#if !defined(OS_ANDROID)
-IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearHighBitDepthMp4) {
-  PlayVideo("bear-320x180-hi10p.mp4", GetParam());
-}
-#endif  // !defined(OS_ANDROID)
-
-IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearSilentMp4) {
-  PlayVideo("bear_silent.mp4", GetParam());
-}
-
-// While we support the big endian (be) PCM codecs on Chromium, Quicktime seems
-// to be the only creator of this format and only for .mov files.
-// TODO(dalecurtis/ihf): Find or create some .wav test cases for "be" format.
 IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearMovPcmS16be) {
   PlayVideo("bear_pcm_s16be.mov", GetParam());
 }
@@ -218,6 +203,15 @@
   PlayVideo("bear_pcm_s24be.mov", GetParam());
 }
 
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearMp4) {
+  PlayVideo("bear.mp4", GetParam());
+}
+
+IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearSilentMp4) {
+  PlayVideo("bear_silent.mp4", GetParam());
+}
+
 IN_PROC_BROWSER_TEST_F(MediaTest, VideoBearRotated0) {
   RunVideoSizeTest("bear_rotate_0.mp4", 1280, 720);
 }
@@ -234,18 +228,21 @@
   RunVideoSizeTest("bear_rotate_270.mp4", 720, 1280);
 }
 
+#if !defined(OS_ANDROID)
+// Android devices usually only support baseline, main and high.
+IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearHighBitDepthMp4) {
+  PlayVideo("bear-320x180-hi10p.mp4", GetParam());
+}
+
 // Android can't reliably load lots of videos on a page.
 // See http://crbug.com/749265
-#if !defined(OS_ANDROID)
 IN_PROC_BROWSER_TEST_F(MediaTest, LoadManyVideos) {
   base::StringPairs query_params;
   RunMediaTestPage("load_many_videos.html", query_params, media::kEnded, true);
 }
 #endif  // !defined(OS_ANDROID)
-#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 #if defined(OS_CHROMEOS)
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
 IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearAviMp3Mpeg4) {
   PlayVideo("bear_mpeg4_mp3.avi", GetParam());
 }
@@ -269,8 +266,8 @@
 IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearWavGsmms) {
   PlayAudio("bear_gsm_ms.wav", GetParam());
 }
-#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 #endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 IN_PROC_BROWSER_TEST_P(MediaTest, AudioBearFlac) {
   PlayAudio("bear.flac", GetParam());
diff --git a/content/browser/media/media_canplaytype_browsertest.cc b/content/browser/media/media_canplaytype_browsertest.cc
index c517a4b..47a3ad4 100644
--- a/content/browser/media/media_canplaytype_browsertest.cc
+++ b/content/browser/media/media_canplaytype_browsertest.cc
@@ -203,11 +203,13 @@
     EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc1.4D401E, vorbis\"'"));
     EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc3.64001F, vorbis\"'"));
 
-    EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"opus\"'"));
-    EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc1, opus\"'"));
-    EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc3, opus\"'"));
-    EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc1.4D401E, opus\"'"));
-    EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc3.64001F, opus\"'"));
+    if (mime != "audio/mp4" && mime != "video/mp4") {
+      EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"opus\"'"));
+      EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc1, opus\"'"));
+      EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc3, opus\"'"));
+      EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc1.4D401E, opus\"'"));
+      EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc3.64001F, opus\"'"));
+    }
 
     EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"vp8\"'"));
     EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"vp9\"'"));
@@ -825,6 +827,8 @@
   EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc1, avc3\"'"));
   EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc1, flac\"'"));
   EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc3, flac\"'"));
+  EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc1, opus\"'"));
+  EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc3, opus\"'"));
 
   EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc1.42E01E\"'"));
   EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc1.42101E\"'"));
@@ -896,6 +900,11 @@
             CanPlay("'video/mp4; codecs=\"avc1.4D401E, flac\"'"));
   EXPECT_EQ(kPropProbably,
             CanPlay("'video/mp4; codecs=\"avc3.64001F, flac\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'video/mp4; codecs=\"opus\"'"));
+  EXPECT_EQ(kPropProbably,
+            CanPlay("'video/mp4; codecs=\"avc1.4D401E, opus\"'"));
+  EXPECT_EQ(kPropProbably,
+            CanPlay("'video/mp4; codecs=\"avc3.64001F, opus\"'"));
 
   TestMPEGUnacceptableCombinations("video/mp4");
   // This result is incorrect. See https://crbug.com/592889.
@@ -970,6 +979,7 @@
   EXPECT_EQ(kNot, CanPlay("'video/x-m4v; codecs=\"avc1.640028,mp4a.A6\"'"));
 
   EXPECT_EQ(kNot, CanPlay("'video/x-m4v; codecs=\"flac\"'"));
+  EXPECT_EQ(kNot, CanPlay("'video/x-m4v; codecs=\"opus\"'"));
 
   TestMPEGUnacceptableCombinations("video/x-m4v");
 
@@ -988,6 +998,7 @@
   EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.40.29\"'"));
 
   EXPECT_EQ(kProbably, CanPlay("'audio/mp4; codecs=\"flac\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'audio/mp4; codecs=\"opus\"'"));
 
   EXPECT_EQ(kNot, CanPlay("'audio/mp4; codecs=\"avc1\"'"));
   EXPECT_EQ(kNot, CanPlay("'audio/mp4; codecs=\"avc3\"'"));
@@ -1055,6 +1066,7 @@
   EXPECT_EQ(kNot, CanPlay("'audio/x-m4a; codecs=\"mp4a.A6\"'"));
 
   EXPECT_EQ(kNot, CanPlay("'video/x-m4a; codecs=\"flac\"'"));
+  EXPECT_EQ(kNot, CanPlay("'video/x-m4a; codecs=\"opus\"'"));
 
   TestMPEGUnacceptableCombinations("audio/x-m4a");
 }
diff --git a/content/browser/permissions/permission_controller_impl.cc b/content/browser/permissions/permission_controller_impl.cc
index 965cfcb..b182e8d 100644
--- a/content/browser/permissions/permission_controller_impl.cc
+++ b/content/browser/permissions/permission_controller_impl.cc
@@ -9,12 +9,27 @@
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/permission_controller_delegate.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
 #include "third_party/blink/public/platform/modules/permissions/permission_status.mojom.h"
 
 class GURL;
 
 namespace content {
 
+namespace {
+
+blink::mojom::PermissionStatus GetPermissionOverrideStatus(
+    const PermissionControllerImpl::PermissionOverrides& permission_overrides,
+    const PermissionType& permission) {
+  if (permission_overrides.find(permission) == permission_overrides.end())
+    return blink::mojom::PermissionStatus::DENIED;
+  return blink::mojom::PermissionStatus::GRANTED;
+}
+
+}  // namespace
+
 PermissionControllerImpl::PermissionControllerImpl(
     BrowserContext* browser_context)
     : browser_context_(browser_context) {}
@@ -26,7 +41,78 @@
       BrowserContext::GetPermissionController(browser_context));
 }
 
-PermissionControllerImpl::~PermissionControllerImpl() = default;
+struct PermissionControllerImpl::Subscription {
+  PermissionType permission;
+  GURL requesting_origin;
+  GURL embedding_origin;
+  int render_frame_id = -1;
+  int render_process_id = -1;
+  base::Callback<void(blink::mojom::PermissionStatus)> callback;
+  int delegate_subscription_id;
+};
+
+PermissionControllerImpl::~PermissionControllerImpl() {
+  // Ideally we need to unsubscribe from delegate subscriptions here,
+  // but browser_context_ is already destroyed by this point, so
+  // we can't fetch our delegate.
+}
+
+blink::mojom::PermissionStatus
+PermissionControllerImpl::GetSubscriptionCurrentValue(
+    const Subscription& subscription) {
+  // The RFH may be null if the request is for a worker.
+  content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
+      subscription.render_process_id, subscription.render_frame_id);
+  if (rfh) {
+    return GetPermissionStatusForFrame(subscription.permission, rfh,
+                                       subscription.requesting_origin);
+  }
+  return GetPermissionStatus(subscription.permission,
+                             subscription.requesting_origin,
+                             subscription.embedding_origin);
+}
+
+void PermissionControllerImpl::SetPermissionOverridesForDevTools(
+    const GURL& origin,
+    const PermissionOverrides& overrides) {
+  std::vector<base::Closure> callbacks;
+  for (SubscriptionsMap::iterator iter(&subscriptions_); !iter.IsAtEnd();
+       iter.Advance()) {
+    Subscription* subscription = iter.GetCurrentValue();
+    if (subscription->requesting_origin != origin)
+      continue;
+    blink::mojom::PermissionStatus current_value =
+        GetSubscriptionCurrentValue(*subscription);
+    blink::mojom::PermissionStatus new_value =
+        GetPermissionOverrideStatus(overrides, subscription->permission);
+    if (current_value != new_value)
+      callbacks.push_back(base::Bind(subscription->callback, new_value));
+  }
+  devtools_permission_overrides_[origin] = overrides;
+  for (const auto& callback : callbacks)
+    callback.Run();
+}
+
+void PermissionControllerImpl::ResetPermissionOverridesForDevTools() {
+  std::map<GURL, PermissionOverrides> old_overrides;
+  old_overrides.swap(devtools_permission_overrides_);
+  std::vector<base::Closure> callbacks;
+  for (SubscriptionsMap::iterator iter(&subscriptions_); !iter.IsAtEnd();
+       iter.Advance()) {
+    Subscription* subscription = iter.GetCurrentValue();
+    auto overrides_it = old_overrides.find(subscription->requesting_origin);
+    if (overrides_it == old_overrides.end())
+      continue;
+    blink::mojom::PermissionStatus current_value = GetPermissionOverrideStatus(
+        overrides_it->second, subscription->permission);
+    blink::mojom::PermissionStatus new_value =
+        GetSubscriptionCurrentValue(*subscription);
+    if (current_value != new_value)
+      callbacks.push_back(base::Bind(subscription->callback, new_value));
+  }
+  for (const auto& callback : callbacks)
+    callback.Run();
+}
 
 int PermissionControllerImpl::RequestPermission(
     PermissionType permission,
@@ -34,6 +120,12 @@
     const GURL& requesting_origin,
     bool user_gesture,
     const base::Callback<void(blink::mojom::PermissionStatus)>& callback) {
+  auto it = devtools_permission_overrides_.find(requesting_origin.GetOrigin());
+  if (it != devtools_permission_overrides_.end()) {
+    callback.Run(GetPermissionOverrideStatus(it->second, permission));
+    return kNoPendingOperation;
+  }
+
   PermissionControllerDelegate* delegate =
       browser_context_->GetPermissionControllerDelegate();
   if (!delegate) {
@@ -51,6 +143,15 @@
     bool user_gesture,
     const base::Callback<
         void(const std::vector<blink::mojom::PermissionStatus>&)>& callback) {
+  auto it = devtools_permission_overrides_.find(requesting_origin.GetOrigin());
+  if (it != devtools_permission_overrides_.end()) {
+    std::vector<blink::mojom::PermissionStatus> result;
+    for (auto& permission : permissions)
+      result.push_back(GetPermissionOverrideStatus(it->second, permission));
+    callback.Run(result);
+    return kNoPendingOperation;
+  }
+
   PermissionControllerDelegate* delegate =
       browser_context_->GetPermissionControllerDelegate();
   if (!delegate) {
@@ -68,6 +169,10 @@
     PermissionType permission,
     const GURL& requesting_origin,
     const GURL& embedding_origin) {
+  auto it = devtools_permission_overrides_.find(requesting_origin.GetOrigin());
+  if (it != devtools_permission_overrides_.end())
+    return GetPermissionOverrideStatus(it->second, permission);
+
   PermissionControllerDelegate* delegate =
       browser_context_->GetPermissionControllerDelegate();
   if (!delegate)
@@ -81,6 +186,10 @@
     PermissionType permission,
     RenderFrameHost* render_frame_host,
     const GURL& requesting_origin) {
+  auto it = devtools_permission_overrides_.find(requesting_origin.GetOrigin());
+  if (it != devtools_permission_overrides_.end())
+    return GetPermissionOverrideStatus(it->second, permission);
+
   PermissionControllerDelegate* delegate =
       browser_context_->GetPermissionControllerDelegate();
   if (!delegate)
@@ -99,26 +208,67 @@
   delegate->ResetPermission(permission, requesting_origin, embedding_origin);
 }
 
+void PermissionControllerImpl::OnDelegatePermissionStatusChange(
+    Subscription* subscription,
+    blink::mojom::PermissionStatus status) {
+  auto overrides_it =
+      devtools_permission_overrides_.find(subscription->requesting_origin);
+  if (overrides_it == devtools_permission_overrides_.end())
+    subscription->callback.Run(status);
+}
+
 int PermissionControllerImpl::SubscribePermissionStatusChange(
     PermissionType permission,
     RenderFrameHost* render_frame_host,
     const GURL& requesting_origin,
     const base::Callback<void(blink::mojom::PermissionStatus)>& callback) {
+  auto subscription = std::make_unique<Subscription>();
+  subscription->permission = permission;
+  subscription->callback = callback;
+  subscription->requesting_origin = requesting_origin;
+
+  // The RFH may be null if the request is for a worker.
+  if (render_frame_host) {
+    content::WebContents* web_contents =
+        content::WebContents::FromRenderFrameHost(render_frame_host);
+    subscription->embedding_origin =
+        web_contents->GetLastCommittedURL().GetOrigin();
+    subscription->render_frame_id = render_frame_host->GetRoutingID();
+    subscription->render_process_id = render_frame_host->GetProcess()->GetID();
+  } else {
+    subscription->embedding_origin = requesting_origin;
+    subscription->render_frame_id = -1;
+    subscription->render_process_id = -1;
+  }
+
   PermissionControllerDelegate* delegate =
       browser_context_->GetPermissionControllerDelegate();
-  if (!delegate)
-    return kNoPendingOperation;
-  return delegate->SubscribePermissionStatusChange(
-      permission, render_frame_host, requesting_origin, callback);
+  if (delegate) {
+    subscription->delegate_subscription_id =
+        delegate->SubscribePermissionStatusChange(
+            permission, render_frame_host, requesting_origin,
+            base::Bind(
+                &PermissionControllerImpl::OnDelegatePermissionStatusChange,
+                base::Unretained(this), subscription.get()));
+  } else {
+    subscription->delegate_subscription_id = kNoPendingOperation;
+  }
+  return subscriptions_.Add(std::move(subscription));
 }
 
 void PermissionControllerImpl::UnsubscribePermissionStatusChange(
     int subscription_id) {
+  Subscription* subscription = subscriptions_.Lookup(subscription_id);
+  if (!subscription)
+    return;
   PermissionControllerDelegate* delegate =
       browser_context_->GetPermissionControllerDelegate();
-  if (!delegate)
-    return;
-  delegate->UnsubscribePermissionStatusChange(subscription_id);
+  if (delegate &&
+      subscription->delegate_subscription_id != kNoPendingOperation) {
+    delegate->UnsubscribePermissionStatusChange(
+        subscription->delegate_subscription_id);
+  }
+  subscriptions_.Remove(subscription_id);
 }
 
 }  // namespace content
diff --git a/content/browser/permissions/permission_controller_impl.h b/content/browser/permissions/permission_controller_impl.h
index 3b9de91..f6f0c2c 100644
--- a/content/browser/permissions/permission_controller_impl.h
+++ b/content/browser/permissions/permission_controller_impl.h
@@ -5,8 +5,10 @@
 #ifndef CONTENT_BROWSER_PERMISSIONS_PERMISSION_CONTROLLER_IMPL_H_
 #define CONTENT_BROWSER_PERMISSIONS_PERMISSION_CONTROLLER_IMPL_H_
 
+#include "base/containers/id_map.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/permission_controller.h"
+#include "url/gurl.h"
 
 namespace content {
 
@@ -23,6 +25,13 @@
   static PermissionControllerImpl* FromBrowserContext(
       BrowserContext* browser_context);
 
+  using PermissionOverrides = std::set<PermissionType>;
+  // For the given |origin|, grant permissions that belong to |overrides|
+  // and reject all others.
+  void SetPermissionOverridesForDevTools(const GURL& origin,
+                                         const PermissionOverrides& overrides);
+  void ResetPermissionOverridesForDevTools();
+
   // PermissionController implementation.
   blink::mojom::PermissionStatus GetPermissionStatus(
       PermissionType permission,
@@ -62,6 +71,16 @@
   void UnsubscribePermissionStatusChange(int subscription_id);
 
  private:
+  struct Subscription;
+  using SubscriptionsMap = base::IDMap<std::unique_ptr<Subscription>>;
+
+  blink::mojom::PermissionStatus GetSubscriptionCurrentValue(
+      const Subscription& subscription);
+  void OnDelegatePermissionStatusChange(Subscription* subscription,
+                                        blink::mojom::PermissionStatus status);
+
+  std::map<GURL, PermissionOverrides> devtools_permission_overrides_;
+  SubscriptionsMap subscriptions_;
   BrowserContext* browser_context_;
 
   DISALLOW_COPY_AND_ASSIGN(PermissionControllerImpl);
diff --git a/content/browser/push_messaging/push_messaging_router.cc b/content/browser/push_messaging/push_messaging_router.cc
index 4904c59f..43bf1f33 100644
--- a/content/browser/push_messaging/push_messaging_router.cc
+++ b/content/browser/push_messaging/push_messaging_router.cc
@@ -11,7 +11,6 @@
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/browser/service_worker/service_worker_storage.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
diff --git a/content/browser/renderer_host/DEPS b/content/browser/renderer_host/DEPS
index 8ac26e10..48de9f81 100644
--- a/content/browser/renderer_host/DEPS
+++ b/content/browser/renderer_host/DEPS
@@ -4,6 +4,7 @@
   "+components/viz/host",
   "+components/viz/service",
   "+services/ui/public",
+  "+services/ws/public",
   "+third_party/blink/public/platform/web_gesture_curve.h",
   "+third_party/zlib",
   "+ui/events/gestures/blink/web_gesture_curve_impl.h",
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index b6ba885..3b69e20 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -702,9 +702,6 @@
       enable_viz_(
           base::FeatureList::IsEnabled(features::kVizDisplayCompositor)),
       weak_factory_(this) {
-  GetHostFrameSinkManager()->RegisterFrameSinkId(frame_sink_id_, this);
-  GetHostFrameSinkManager()->SetFrameSinkDebugLabel(frame_sink_id_,
-                                                    "CompositorImpl");
   DCHECK(client);
 
   SetRootWindow(root_window);
@@ -719,7 +716,6 @@
   DetachRootWindow();
   // Clean-up any surface references.
   SetSurface(NULL);
-  GetHostFrameSinkManager()->InvalidateFrameSinkId(frame_sink_id_);
 }
 
 void CompositorImpl::DetachRootWindow() {
@@ -857,28 +853,24 @@
 
 void CompositorImpl::SetVisible(bool visible) {
   TRACE_EVENT1("cc", "CompositorImpl::SetVisible", "visible", visible);
+
   if (!visible) {
     DCHECK(host_->IsVisible());
-
-    // Make a best effort to try to complete pending readbacks.
-    // TODO(crbug.com/637035): Consider doing this in a better way,
-    // ideally with the guarantee of readbacks completing.
-    if (display_.get() && HavePendingReadbacks())
-      display_->ForceImmediateDrawAndSwapIfPossible();
-
+    // Tear down the display first, synchronously completing any pending
+    // draws/readbacks if poosible.
+    TearDownDisplayAndUnregisterRootFrameSink();
+    // Hide the LayerTreeHost and release its frame sink.
     host_->SetVisible(false);
     host_->ReleaseLayerTreeFrameSink();
     has_layer_tree_frame_sink_ = false;
     pending_frames_ = 0;
-    if (display_) {
-      GetFrameSinkManager()->UnregisterBeginFrameSource(
-          root_window_->GetBeginFrameSource());
-    }
-    display_.reset();
+    // Handle GPU visibility signals.
     GpuDataManagerImpl::GetInstance()->SetApplicationVisible(false);
     SendOnBackgroundedToGpuService();
     EnqueueLowEndBackgroundCleanup();
   } else {
+    DCHECK(!host_->IsVisible());
+    RegisterRootFrameSink();
     host_->SetVisible(true);
     has_submitted_frame_since_became_visible_ = false;
     if (layer_tree_frame_sink_request_pending_)
@@ -887,8 +879,46 @@
     SendOnForegroundedToGpuService();
     low_end_background_cleanup_task_.Cancel();
   }
-  if (display_private_)
-    display_private_->SetDisplayVisible(visible);
+}
+
+void CompositorImpl::TearDownDisplayAndUnregisterRootFrameSink() {
+  if (enable_viz_) {
+    // Make a best effort to try to complete pending readbacks.
+    // TODO(crbug.com/637035): Consider doing this in a better way,
+    // ideally with the guarantee of readbacks completing.
+    if (display_private_ && HavePendingReadbacks()) {
+      // Note that while this is not a Sync IPC, the call to
+      // InvalidateFrameSinkId below will end up triggering a sync call to
+      // FrameSinkManager::DestroyCompositorFrameSink, as this is the root
+      // frame sink. Because |display_private_| is an associated interface to
+      // FrameSinkManager, this subsequent sync call will ensure ordered
+      // execution of this call.
+      display_private_->ForceImmediateDrawAndSwapIfPossible();
+    }
+
+    GetHostFrameSinkManager()->InvalidateFrameSinkId(frame_sink_id_);
+    display_private_.reset();
+  } else {
+    // Make a best effort to try to complete pending readbacks.
+    // TODO(crbug.com/637035): Consider doing this in a better way,
+    // ideally with the guarantee of readbacks completing.
+    if (display_ && HavePendingReadbacks())
+      display_->ForceImmediateDrawAndSwapIfPossible();
+
+    if (display_) {
+      GetFrameSinkManager()->UnregisterBeginFrameSource(
+          root_window_->GetBeginFrameSource());
+    }
+
+    GetHostFrameSinkManager()->InvalidateFrameSinkId(frame_sink_id_);
+    display_.reset();
+  }
+}
+
+void CompositorImpl::RegisterRootFrameSink() {
+  GetHostFrameSinkManager()->RegisterFrameSinkId(frame_sink_id_, this);
+  GetHostFrameSinkManager()->SetFrameSinkDebugLabel(frame_sink_id_,
+                                                    "CompositorImpl");
 }
 
 void CompositorImpl::SetWindowBounds(const gfx::Size& size) {
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index 6ade9d2..04ad635 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -190,6 +190,13 @@
   // returns an empty surface.
   viz::LocalSurfaceId GenerateLocalSurfaceId() const;
 
+  // Tears down the display for both Viz and non-Viz, unregistering the root
+  // frame sink ID in the process.
+  void TearDownDisplayAndUnregisterRootFrameSink();
+
+  // Registers the root frame sink ID.
+  void RegisterRootFrameSink();
+
   // Viz specific functions:
   void InitializeVizLayerTreeFrameSink(
       scoped_refptr<ui::ContextProviderCommandBuffer> context_provider);
diff --git a/content/browser/renderer_host/frame_connector_delegate.h b/content/browser/renderer_host/frame_connector_delegate.h
index c96135e..c8bedcde 100644
--- a/content/browser/renderer_host/frame_connector_delegate.h
+++ b/content/browser/renderer_host/frame_connector_delegate.h
@@ -15,7 +15,7 @@
 #include "ui/gfx/geometry/rect.h"
 
 #if defined(USE_AURA)
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #endif
 
 namespace blink {
diff --git a/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc b/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc
index 2f338a7..a72e8229 100644
--- a/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc
+++ b/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc
@@ -189,7 +189,9 @@
 class MockMoveGestureTarget : public MockSyntheticGestureTarget {
  public:
   MockMoveGestureTarget()
-      : total_abs_move_distance_length_(0), precise_scrolling_deltas_(false) {}
+      : total_abs_move_distance_length_(0),
+        precise_scrolling_deltas_(false),
+        scroll_by_page_(false) {}
   ~MockMoveGestureTarget() override {}
 
   gfx::Vector2dF start_to_end_distance() const {
@@ -199,11 +201,13 @@
     return total_abs_move_distance_length_;
   }
   bool precise_scrolling_deltas() const { return precise_scrolling_deltas_; }
+  bool scroll_by_page() const { return scroll_by_page_; }
 
  protected:
   gfx::Vector2dF start_to_end_distance_;
   float total_abs_move_distance_length_;
   bool precise_scrolling_deltas_;
+  bool scroll_by_page_;
 };
 
 class MockScrollMouseTarget : public MockMoveGestureTarget {
@@ -219,6 +223,7 @@
     start_to_end_distance_ += delta;
     total_abs_move_distance_length_ += delta.Length();
     precise_scrolling_deltas_ = mouse_wheel_event.has_precise_scrolling_deltas;
+    scroll_by_page_ = mouse_wheel_event.scroll_by_page;
   }
 };
 
@@ -1258,6 +1263,27 @@
             scroll_target->precise_scrolling_deltas());
 }
 
+TEST_F(SyntheticGestureControllerTest, SingleScrollGestureMouseScrollByPage) {
+  CreateControllerAndTarget<MockScrollMouseTarget>();
+
+  SyntheticSmoothMoveGestureParams params;
+  params.input_type = SyntheticSmoothMoveGestureParams::MOUSE_WHEEL_INPUT;
+  params.start_point.SetPoint(39, 86);
+  params.distances.push_back(gfx::Vector2d(0, -132));
+  params.scroll_by_page = true;
+
+  std::unique_ptr<SyntheticSmoothMoveGesture> gesture(
+      new SyntheticSmoothMoveGesture(params));
+  QueueSyntheticGesture(std::move(gesture));
+  FlushInputUntilComplete();
+
+  MockMoveGestureTarget* scroll_target =
+      static_cast<MockMoveGestureTarget*>(target_);
+  EXPECT_EQ(1, num_success_);
+  EXPECT_EQ(0, num_failure_);
+  EXPECT_EQ(params.scroll_by_page, scroll_target->scroll_by_page());
+}
+
 void CheckIsWithinRangeMulti(float scroll_distance,
                              int target_distance,
                              SyntheticGestureTarget* target) {
diff --git a/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc b/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
index 6f53e5f..f66e36f 100644
--- a/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
+++ b/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
@@ -81,9 +81,13 @@
     return;
   }
   base::TimeTicks timestamp = web_wheel.TimeStamp();
-  int modifiers = web_wheel.has_precise_scrolling_deltas
-                      ? ui::EF_PRECISION_SCROLLING_DELTA
-                      : ui::EF_NONE;
+  int modifiers = ui::EF_NONE;
+  if (web_wheel.has_precise_scrolling_deltas)
+    modifiers |= ui::EF_PRECISION_SCROLLING_DELTA;
+
+  if (web_wheel.scroll_by_page)
+    modifiers |= ui::EF_SCROLL_BY_PAGE;
+
   ui::MouseWheelEvent wheel_event(
       gfx::Vector2d(web_wheel.delta_x, web_wheel.delta_y), gfx::Point(),
       gfx::Point(), timestamp, modifiers, ui::EF_NONE);
diff --git a/content/browser/renderer_host/input/synthetic_smooth_move_gesture.cc b/content/browser/renderer_host/input/synthetic_smooth_move_gesture.cc
index f993152..11325e37 100644
--- a/content/browser/renderer_host/input/synthetic_smooth_move_gesture.cc
+++ b/content/browser/renderer_host/input/synthetic_smooth_move_gesture.cc
@@ -33,7 +33,8 @@
       fling_velocity_y(0),
       prevent_fling(true),
       add_slop(true),
-      precise_scrolling_deltas(false) {}
+      precise_scrolling_deltas(false),
+      scroll_by_page(false) {}
 
 SyntheticSmoothMoveGestureParams::SyntheticSmoothMoveGestureParams(
     const SyntheticSmoothMoveGestureParams& other) = default;
@@ -270,7 +271,8 @@
     const base::TimeTicks& timestamp) const {
   blink::WebMouseWheelEvent mouse_wheel_event =
       SyntheticWebMouseWheelEventBuilder::Build(
-          0, 0, delta.x(), delta.y(), 0, params_.precise_scrolling_deltas);
+          0, 0, delta.x(), delta.y(), 0, params_.precise_scrolling_deltas,
+          params_.scroll_by_page);
 
   mouse_wheel_event.SetPositionInWidget(
       current_move_segment_start_position_.x(),
diff --git a/content/browser/renderer_host/input/synthetic_smooth_move_gesture.h b/content/browser/renderer_host/input/synthetic_smooth_move_gesture.h
index fb58bae..353b10d 100644
--- a/content/browser/renderer_host/input/synthetic_smooth_move_gesture.h
+++ b/content/browser/renderer_host/input/synthetic_smooth_move_gesture.h
@@ -39,6 +39,7 @@
   bool prevent_fling;
   bool add_slop;
   bool precise_scrolling_deltas;
+  bool scroll_by_page;
 };
 
 // This class is used as helper class for simulation of scroll and drag.
diff --git a/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc b/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc
index 7b6ffe9..977fe39 100644
--- a/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc
+++ b/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc
@@ -51,6 +51,7 @@
     move_params.input_type = GetInputSourceType(gesture_type);
     move_params.add_slop = true;
     move_params.precise_scrolling_deltas = params_.precise_scrolling_deltas;
+    move_params.scroll_by_page = params_.scroll_by_page;
     move_gesture_.reset(new SyntheticSmoothMoveGesture(move_params));
     return true;
   }
diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc
index 5b35700..bc66c96 100644
--- a/content/browser/renderer_host/render_message_filter.cc
+++ b/content/browser/renderer_host/render_message_filter.cc
@@ -48,6 +48,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/resource_context.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/context_menu_params.h"
@@ -63,6 +64,7 @@
 #include "net/http/http_cache.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/mojom/network_context.mojom.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -238,24 +240,10 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (!base::FeatureList::IsEnabled(features::kIsolatedCodeCache)) {
-    net::HttpCache* cache = request_context_->GetURLRequestContext()
-                                ->http_transaction_factory()
-                                ->GetCache();
-    if (!cache)
-      return;
-
-    // Use the same priority for the metadata write as for script
-    // resources (see defaultPriorityForResourceType() in WebKit's
-    // CachedResource.cpp). Note that WebURLRequest::PriorityMedium
-    // corresponds to net::LOW (see ConvertWebKitPriorityToNetPriority()
-    // in weburlloader_impl.cc).
-    const net::RequestPriority kPriority = net::LOW;
-    scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(data.size()));
-
-    if (!data.empty())
-      memcpy(buf->data(), &data.front(), data.size());
-    cache->WriteMetadata(url, kPriority, expected_response_time, buf.get(),
-                         data.size());
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::BindOnce(&RenderMessageFilter::DidGenerateCacheableMetadataOnUI,
+                       this, url, expected_response_time, data));
   } else {
     if (!generated_code_cache_context_->generated_code_cache())
       return;
@@ -356,4 +344,23 @@
   GpuProcessHost::GetHasGpuProcess(std::move(callback));
 }
 
+void RenderMessageFilter::DidGenerateCacheableMetadataOnUI(
+    const GURL& url,
+    base::Time expected_response_time,
+    const std::vector<uint8_t>& data) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  RenderProcessHost* host = RenderProcessHost::FromID(render_process_id_);
+  if (!host)
+    return;
+
+  // Use the same priority for the metadata write as for script
+  // resources (see defaultPriorityForResourceType() in WebKit's
+  // CachedResource.cpp). Note that WebURLRequest::PriorityMedium
+  // corresponds to net::LOW (see ConvertWebKitPriorityToNetPriority()
+  // in weburlloader_impl.cc).
+  const net::RequestPriority kPriority = net::LOW;
+  host->GetStoragePartition()->GetNetworkContext()->WriteCacheMetadata(
+      url, kPriority, expected_response_time, data);
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/render_message_filter.h b/content/browser/renderer_host/render_message_filter.h
index ec2febc5..76eda81 100644
--- a/content/browser/renderer_host/render_message_filter.h
+++ b/content/browser/renderer_host/render_message_filter.h
@@ -140,6 +140,11 @@
   bool CheckBenchmarkingEnabled() const;
   bool CheckPreparsedJsCachingEnabled() const;
 
+  // NetworkContext must be called from the UI thread.
+  void DidGenerateCacheableMetadataOnUI(const GURL& url,
+                                        base::Time expected_response_time,
+                                        const std::vector<uint8_t>& data);
+
   // Cached resource request dispatcher host, guaranteed to be non-null. We do
   // not own it; it is managed by the BrowserProcess, which has a wider scope
   // than we do.
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index c2edcee..129064c 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -53,8 +53,8 @@
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/mojom/service.mojom.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
 #include "services/viz/public/interfaces/compositing/compositing_mode_watcher.mojom.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
 #include "third_party/blink/public/mojom/associated_interfaces/associated_interfaces.mojom.h"
 #include "third_party/blink/public/mojom/dom_storage/storage_partition_service.mojom.h"
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index ff2723e..5b53af4 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -55,7 +55,7 @@
 #include "gpu/ipc/common/gpu_messages.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "services/ui/common/switches.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "third_party/blink/public/web/web_ime_text_span.h"
 #include "ui/accessibility/platform/aura_window_properties.h"
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h
index 00a7b63..2fddd5f7f 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.h
+++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -46,7 +46,7 @@
 #if defined(USE_AURA)
 #include "base/containers/flat_map.h"
 #include "content/common/render_widget_window_tree_client_factory.mojom.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #endif
 
 struct ViewHostMsg_SelectionBounds_Params;
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 e727c25..e2dc4f9 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
@@ -49,7 +49,7 @@
 #include "ui/touch_selection/touch_selection_controller.h"
 
 #if defined(USE_AURA)
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/env.h"
 #endif
 
diff --git a/content/browser/service_manager/common_browser_interfaces.cc b/content/browser/service_manager/common_browser_interfaces.cc
index 1d19c0d..76d7a5e 100644
--- a/content/browser/service_manager/common_browser_interfaces.cc
+++ b/content/browser/service_manager/common_browser_interfaces.cc
@@ -25,7 +25,7 @@
 #include "content/public/common/service_names.mojom.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 #include "ui/base/ui_base_features.h"
 
 #if defined(OS_WIN)
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index 191b0d1..289d474 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -24,7 +24,6 @@
 #include "content/common/background_fetch/background_fetch_types.h"
 #include "content/common/renderer.mojom.h"
 #include "content/common/service_worker/service_worker.mojom.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_context.h"
 #include "mojo/public/cpp/bindings/associated_binding_set.h"
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 44b7d7ae..1d2e312 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -45,7 +45,6 @@
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/browser/service_worker/service_worker_version.h"
 #include "content/browser/web_contents/web_contents_impl.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/content/browser/service_worker/service_worker_context_unittest.cc b/content/browser/service_worker/service_worker_context_unittest.cc
index 17018481..7ca85191 100644
--- a/content/browser/service_worker/service_worker_context_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_unittest.cc
@@ -20,7 +20,6 @@
 #include "content/browser/service_worker/service_worker_storage.h"
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/browser/service_worker/service_worker_version.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/public/browser/service_worker_context_observer.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
diff --git a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
index fae1505..7bdd0811 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
@@ -21,7 +21,6 @@
 #include "content/browser/service_worker/service_worker_navigation_handle_core.h"
 #include "content/browser/service_worker/service_worker_object_host.h"
 #include "content/browser/service_worker/service_worker_test_utils.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/browser/child_process_termination_info.h"
 #include "content/public/common/browser_side_navigation_policy.h"
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.cc b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
index 9e73a9d..21e9f3c 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.cc
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
@@ -25,7 +25,6 @@
 #include "content/browser/service_worker/service_worker_version.h"
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/common/service_worker/service_worker.mojom.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/browser_side_navigation_policy.h"
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc
index 724ffb5..5d375cd 100644
--- a/content/browser/service_worker/service_worker_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -24,7 +24,6 @@
 #include "content/browser/service_worker/service_worker_registration_object_host.h"
 #include "content/browser/service_worker/service_worker_registration_status.h"
 #include "content/browser/service_worker/service_worker_test_utils.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/content/browser/service_worker/service_worker_provider_host_unittest.cc b/content/browser/service_worker/service_worker_provider_host_unittest.cc
index e7b246b..926cb6b 100644
--- a/content/browser/service_worker/service_worker_provider_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_provider_host_unittest.cc
@@ -19,7 +19,6 @@
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/browser/service_worker/service_worker_version.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/url_schemes.h"
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/common/child_process_host.h"
diff --git a/content/browser/service_worker/service_worker_register_job.cc b/content/browser/service_worker/service_worker_register_job.cc
index 81b62c81..f832ed1 100644
--- a/content/browser/service_worker/service_worker_register_job.cc
+++ b/content/browser/service_worker/service_worker_register_job.cc
@@ -20,7 +20,6 @@
 #include "content/browser/service_worker/service_worker_version.h"
 #include "content/browser/service_worker/service_worker_write_to_cache_job.h"
 #include "content/common/service_worker/service_worker.mojom.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/content/browser/service_worker/service_worker_registration.cc b/content/browser/service_worker/service_worker_registration.cc
index dec3431..f215fd80 100644
--- a/content/browser/service_worker/service_worker_registration.cc
+++ b/content/browser/service_worker/service_worker_registration.cc
@@ -13,7 +13,6 @@
 #include "content/browser/service_worker/service_worker_info.h"
 #include "content/browser/service_worker/service_worker_metrics.h"
 #include "content/browser/service_worker/service_worker_register_job.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/public/browser/browser_thread.h"
 #include "third_party/blink/public/common/service_worker/service_worker_utils.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
diff --git a/content/browser/service_worker/service_worker_url_request_job_unittest.cc b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
index 4a76ea93..fef3544e 100644
--- a/content/browser/service_worker/service_worker_url_request_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
@@ -31,7 +31,6 @@
 #include "content/browser/service_worker/service_worker_response_info.h"
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/browser/service_worker/service_worker_version.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/browser/blob_handle.h"
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 5d0a566..9141797 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -31,7 +31,6 @@
 #include "content/browser/service_worker/service_worker_installed_scripts_sender.h"
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/common/service_worker/embedded_worker.mojom.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index 6222b229..d55fd0d 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -1759,8 +1759,9 @@
   MOCK_METHOD1(
       OnTransportAvailabilityEnumerated,
       void(device::FidoRequestHandlerBase::TransportAvailabilityInfo data));
-  MOCK_METHOD2(FidoAuthenticatorAdded,
-               void(const device::FidoAuthenticator&, bool*));
+  MOCK_METHOD1(EmbedderControlsAuthenticatorDispatch,
+               bool(const device::FidoAuthenticator&));
+  MOCK_METHOD1(FidoAuthenticatorAdded, void(const device::FidoAuthenticator&));
   MOCK_METHOD1(FidoAuthenticatorRemoved, void(base::StringPiece));
 
  private:
@@ -1873,7 +1874,7 @@
   EXPECT_CALL(*mock_delegate_ptr, OnTransportAvailabilityEnumerated(_));
 
   base::RunLoop ble_device_found_done;
-  EXPECT_CALL(*mock_delegate_ptr, FidoAuthenticatorAdded(_, _))
+  EXPECT_CALL(*mock_delegate_ptr, FidoAuthenticatorAdded(_))
       .WillOnce(testing::InvokeWithoutArgs(
           [&ble_device_found_done]() { ble_device_found_done.Quit(); }));
 
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 39cf968..cc72e9a 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -250,7 +250,6 @@
     "service_manager/service_manager_connection_impl.h",
     "service_worker/service_worker_loader_helpers.cc",
     "service_worker/service_worker_loader_helpers.h",
-    "service_worker/service_worker_messages.h",
     "service_worker/service_worker_types.cc",
     "service_worker/service_worker_types.h",
     "service_worker/service_worker_utils.cc",
@@ -349,9 +348,9 @@
     "//services/service_manager/public/mojom",
     "//services/service_manager/runner/common",
     "//services/service_manager/zygote:zygote_buildflags",
-    "//services/ui/public/interfaces",
     "//services/video_capture/public/mojom",
     "//services/viz/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//storage/common",
     "//third_party/angle:angle_gpu_info_util",
@@ -587,10 +586,10 @@
     "//mojo/public/mojom/base",
     "//services/network/public/mojom",
     "//services/service_manager/public/mojom",
-    "//services/ui/public/interfaces",
-    "//services/ui/public/interfaces/ime",
     "//services/video_capture/public/mojom",
     "//services/viz/public/interfaces",
+    "//services/ws/public/mojom",
+    "//services/ws/public/mojom/ime",
     "//skia/public/interfaces",
     "//third_party/blink/public:mojo_bindings",
     "//third_party/blink/public:web_feature_mojo_bindings",
diff --git a/content/common/background_fetch/background_fetch_struct_traits.cc b/content/common/background_fetch/background_fetch_struct_traits.cc
index a8bde81..df09e13 100644
--- a/content/common/background_fetch/background_fetch_struct_traits.cc
+++ b/content/common/background_fetch/background_fetch_struct_traits.cc
@@ -6,7 +6,6 @@
 
 #include "content/common/service_worker/service_worker.mojom.h"
 #include "content/common/service_worker/service_worker_fetch_request_mojom_traits.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "mojo/public/cpp/bindings/array_data_view.h"
 #include "third_party/blink/public/common/manifest/manifest_mojom_traits.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
diff --git a/content/common/content_message_generator.h b/content/common/content_message_generator.h
index 522bb456..c604ee0e 100644
--- a/content/common/content_message_generator.h
+++ b/content/common/content_message_generator.h
@@ -73,12 +73,6 @@
 #ifndef CONTENT_COMMON_RESOURCE_MESSAGES_H_
 #error "Failed to include content/common/resource_messages.h"
 #endif
-#undef CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_MESSAGES_H_
-#include "content/common/service_worker/service_worker_messages.h"
-#ifndef CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_MESSAGES_H_
-#error \
-    "Failed to include content/common/service_worker/service_worker_messages.h"
-#endif
 #undef CONTENT_COMMON_TEXT_INPUT_CLIENT_MESSAGES_H_
 #include "content/common/text_input_client_messages.h"
 #ifndef CONTENT_COMMON_TEXT_INPUT_CLIENT_MESSAGES_H_
diff --git a/content/common/input/input_handler.mojom b/content/common/input/input_handler.mojom
index 389e82b6..641f2d6 100644
--- a/content/common/input/input_handler.mojom
+++ b/content/common/input/input_handler.mojom
@@ -8,7 +8,7 @@
 import "content/common/native_types.mojom";
 import "mojo/public/mojom/base/string16.mojom";
 import "mojo/public/mojom/base/time.mojom";
-import "services/ui/public/interfaces/ime/ime.mojom";
+import "services/ws/public/mojom/ime/ime.mojom";
 import "third_party/blink/public/web/selection_menu_behavior.mojom";
 import "ui/events/mojo/event.mojom";
 import "ui/events/mojo/event_constants.mojom";
diff --git a/content/common/input/synthetic_smooth_scroll_gesture_params.cc b/content/common/input/synthetic_smooth_scroll_gesture_params.cc
index e9baed38..ee9ec1e 100644
--- a/content/common/input/synthetic_smooth_scroll_gesture_params.cc
+++ b/content/common/input/synthetic_smooth_scroll_gesture_params.cc
@@ -18,7 +18,8 @@
       speed_in_pixels_s(kDefaultSpeedInPixelsS),
       fling_velocity_x(0),
       fling_velocity_y(0),
-      precise_scrolling_deltas(false) {}
+      precise_scrolling_deltas(false),
+      scroll_by_page(false) {}
 
 SyntheticSmoothScrollGestureParams::SyntheticSmoothScrollGestureParams(
     const SyntheticSmoothScrollGestureParams& other) = default;
diff --git a/content/common/input/synthetic_smooth_scroll_gesture_params.h b/content/common/input/synthetic_smooth_scroll_gesture_params.h
index b0bb27da..d590892 100644
--- a/content/common/input/synthetic_smooth_scroll_gesture_params.h
+++ b/content/common/input/synthetic_smooth_scroll_gesture_params.h
@@ -31,6 +31,7 @@
   float fling_velocity_x;
   float fling_velocity_y;
   bool precise_scrolling_deltas;
+  bool scroll_by_page;
 
   static const SyntheticSmoothScrollGestureParams* Cast(
       const SyntheticGestureParams* gesture_params);
diff --git a/content/common/input/synthetic_web_input_event_builders.cc b/content/common/input/synthetic_web_input_event_builders.cc
index 22f090f..e4b9aad 100644
--- a/content/common/input/synthetic_web_input_event_builders.cc
+++ b/content/common/input/synthetic_web_input_event_builders.cc
@@ -50,23 +50,27 @@
   return result;
 }
 
-WebMouseWheelEvent SyntheticWebMouseWheelEventBuilder::Build(float x,
-                                                             float y,
-                                                             float dx,
-                                                             float dy,
-                                                             int modifiers,
-                                                             bool precise) {
-  return Build(x, y, 0, 0, dx, dy, modifiers, precise);
+WebMouseWheelEvent SyntheticWebMouseWheelEventBuilder::Build(
+    float x,
+    float y,
+    float dx,
+    float dy,
+    int modifiers,
+    bool precise,
+    bool scroll_by_page) {
+  return Build(x, y, 0, 0, dx, dy, modifiers, precise, scroll_by_page);
 }
 
-WebMouseWheelEvent SyntheticWebMouseWheelEventBuilder::Build(float x,
-                                                             float y,
-                                                             float global_x,
-                                                             float global_y,
-                                                             float dx,
-                                                             float dy,
-                                                             int modifiers,
-                                                             bool precise) {
+WebMouseWheelEvent SyntheticWebMouseWheelEventBuilder::Build(
+    float x,
+    float y,
+    float global_x,
+    float global_y,
+    float dx,
+    float dy,
+    int modifiers,
+    bool precise,
+    bool scroll_by_page) {
   WebMouseWheelEvent result(WebInputEvent::kMouseWheel, modifiers,
                             ui::EventTimeForNow());
   result.SetPositionInScreen(global_x, global_y);
@@ -78,6 +82,7 @@
   if (dy)
     result.wheel_ticks_y = dy > 0.0f ? 1.0f : -1.0f;
   result.has_precise_scrolling_deltas = precise;
+  result.scroll_by_page = scroll_by_page;
   return result;
 }
 
diff --git a/content/common/input/synthetic_web_input_event_builders.h b/content/common/input/synthetic_web_input_event_builders.h
index b101ce7..52c0b72 100644
--- a/content/common/input/synthetic_web_input_event_builders.h
+++ b/content/common/input/synthetic_web_input_event_builders.h
@@ -38,7 +38,8 @@
                                          float dx,
                                          float dy,
                                          int modifiers,
-                                         bool precise);
+                                         bool precise,
+                                         bool scroll_by_page = false);
   static blink::WebMouseWheelEvent Build(float x,
                                          float y,
                                          float global_x,
@@ -46,7 +47,8 @@
                                          float dx,
                                          float dy,
                                          int modifiers,
-                                         bool precise);
+                                         bool precise,
+                                         bool scroll_by_page = false);
 };
 
 class CONTENT_EXPORT SyntheticWebKeyboardEventBuilder {
diff --git a/content/common/input_messages.h b/content/common/input_messages.h
index b49a5c340..7d5b879 100644
--- a/content/common/input_messages.h
+++ b/content/common/input_messages.h
@@ -122,6 +122,8 @@
   IPC_STRUCT_TRAITS_MEMBER(speed_in_pixels_s)
   IPC_STRUCT_TRAITS_MEMBER(fling_velocity_x)
   IPC_STRUCT_TRAITS_MEMBER(fling_velocity_y)
+  IPC_STRUCT_TRAITS_MEMBER(precise_scrolling_deltas)
+  IPC_STRUCT_TRAITS_MEMBER(scroll_by_page)
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_TRAITS_BEGIN(content::SyntheticPinchGestureParams)
diff --git a/content/common/render_widget_host_ns_view.mojom b/content/common/render_widget_host_ns_view.mojom
index c0afeb7..6d6b5e9 100644
--- a/content/common/render_widget_host_ns_view.mojom
+++ b/content/common/render_widget_host_ns_view.mojom
@@ -7,7 +7,7 @@
 import "content/common/native_types.mojom";
 import "content/common/input/input_handler.mojom";
 import "mojo/public/mojom/base/string16.mojom";
-import "services/ui/public/interfaces/ime/ime.mojom";
+import "services/ws/public/mojom/ime/ime.mojom";
 import "ui/display/mojo/display.mojom";
 import "ui/events/mojo/event.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
diff --git a/content/common/render_widget_window_tree_client_factory.mojom b/content/common/render_widget_window_tree_client_factory.mojom
index 0c1ae21..629845c 100644
--- a/content/common/render_widget_window_tree_client_factory.mojom
+++ b/content/common/render_widget_window_tree_client_factory.mojom
@@ -5,7 +5,7 @@
 module content.mojom;
 
 import "mojo/public/mojom/base/unguessable_token.mojom";
-import "services/ui/public/interfaces/window_tree.mojom";
+import "services/ws/public/mojom/window_tree.mojom";
 
 interface RenderWidgetWindowTreeClient {
   // Asks the renderer to create a Window for the frame with the routing id
diff --git a/content/common/sandbox_policy_fuchsia.cc b/content/common/sandbox_policy_fuchsia.cc
index 2489f24..723586c 100644
--- a/content/common/sandbox_policy_fuchsia.cc
+++ b/content/common/sandbox_policy_fuchsia.cc
@@ -80,6 +80,11 @@
             base::fuchsia::ComponentContext::GetDefault());
     for (const char* service_name : services_list)
       service_directory_->AddService(service_name);
+
+    // Bind the service directory and store the client channel for
+    // UpdateLaunchOptionsForSandbox()'s use.
+    service_directory_client_channel_ = service_directory_->ConnectClient();
+    CHECK(service_directory_client_channel_);
   }
 }
 
@@ -110,7 +115,7 @@
   if (service_directory_) {
     // Provide the child process with a restricted set of services.
     options->paths_to_transfer.push_back(base::PathToTransfer{
-        base::FilePath("/svc"), service_directory_->ConnectClient().release()});
+        base::FilePath("/svc"), service_directory_client_channel_.release()});
   }
 }
 
diff --git a/content/common/sandbox_policy_fuchsia.h b/content/common/sandbox_policy_fuchsia.h
index 4f2592c..e3ba853 100644
--- a/content/common/sandbox_policy_fuchsia.h
+++ b/content/common/sandbox_policy_fuchsia.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_COMMON_SANDBOX_POLICY_FUCHSIA_H_
 #define CONTENT_COMMON_SANDBOX_POLICY_FUCHSIA_H_
 
+#include <lib/zx/channel.h>
+
 #include "base/memory/ref_counted.h"
 #include "services/service_manager/sandbox/sandbox_type.h"
 
@@ -40,6 +42,7 @@
 
   // Services directory used for the /svc namespace of the child process.
   std::unique_ptr<base::fuchsia::FilteredServiceDirectory> service_directory_;
+  zx::channel service_directory_client_channel_;
   scoped_refptr<base::SequencedTaskRunner> service_directory_task_runner_;
 };
 
diff --git a/content/common/service_worker/service_worker_fetch_request.typemap b/content/common/service_worker/service_worker_fetch_request.typemap
index 013db54..70e608f8 100644
--- a/content/common/service_worker/service_worker_fetch_request.typemap
+++ b/content/common/service_worker/service_worker_fetch_request.typemap
@@ -7,8 +7,6 @@
 public_headers = [
   "//content/common/service_worker/service_worker_types.h",
   "//content/public/common/request_context_type.h",
-  "//storage/common/blob_storage/blob_handle.h",
-  "//storage/common/storage_common_export.h",
 ]
 traits_headers = [
   "//content/common/service_worker/service_worker_fetch_request_mojom_traits.h",
@@ -19,5 +17,4 @@
 type_mappings = [
   "blink.mojom.FetchAPIRequest=::content::ServiceWorkerFetchRequest",
   "blink.mojom.RequestContextType=::content::RequestContextType",
-  "blink.mojom.ServiceWorkerFetchType=::content::ServiceWorkerFetchType",
 ]
diff --git a/content/common/service_worker/service_worker_messages.h b/content/common/service_worker/service_worker_messages.h
deleted file mode 100644
index f06db99..0000000
--- a/content/common/service_worker/service_worker_messages.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_MESSAGES_H_
-#define CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_MESSAGES_H_
-
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "base/strings/string16.h"
-#include "base/time/time.h"
-#include "content/common/service_worker/service_worker_types.h"
-#include "content/public/common/platform_notification_data.h"
-#include "ipc/ipc_message_macros.h"
-#include "ipc/ipc_param_traits.h"
-#include "services/network/public/mojom/fetch_api.mojom.h"
-#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
-#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_error.h"
-
-#undef IPC_MESSAGE_EXPORT
-#define IPC_MESSAGE_EXPORT CONTENT_EXPORT
-
-#define IPC_MESSAGE_START ServiceWorkerMsgStart
-
-// TODO(leonhsl): Figure out what's the purpose of all these traits then
-// eliminate this file finally.
-IPC_STRUCT_TRAITS_BEGIN(content::ServiceWorkerFetchRequest)
-  IPC_STRUCT_TRAITS_MEMBER(mode)
-  IPC_STRUCT_TRAITS_MEMBER(is_main_resource_load)
-  IPC_STRUCT_TRAITS_MEMBER(request_context_type)
-  IPC_STRUCT_TRAITS_MEMBER(frame_type)
-  IPC_STRUCT_TRAITS_MEMBER(url)
-  IPC_STRUCT_TRAITS_MEMBER(method)
-  IPC_STRUCT_TRAITS_MEMBER(headers)
-  IPC_STRUCT_TRAITS_MEMBER(referrer)
-  IPC_STRUCT_TRAITS_MEMBER(credentials_mode)
-  IPC_STRUCT_TRAITS_MEMBER(redirect_mode)
-  IPC_STRUCT_TRAITS_MEMBER(integrity)
-  IPC_STRUCT_TRAITS_MEMBER(keepalive)
-  IPC_STRUCT_TRAITS_MEMBER(client_id)
-  IPC_STRUCT_TRAITS_MEMBER(is_reload)
-IPC_STRUCT_TRAITS_END()
-
-#endif  // CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_MESSAGES_H_
diff --git a/content/ppapi_plugin/DEPS b/content/ppapi_plugin/DEPS
index fc443d2..3bcfb601 100644
--- a/content/ppapi_plugin/DEPS
+++ b/content/ppapi_plugin/DEPS
@@ -7,5 +7,5 @@
   "+ppapi/proxy",
   "+services/service_manager/public/cpp",
   "+services/service_manager/sandbox",
-  "+services/ui/public/interfaces",
+  "+services/ws/public/mojom",
 ]
diff --git a/content/ppapi_plugin/ppapi_thread.cc b/content/ppapi_plugin/ppapi_thread.cc
index 8b4fe965..f987f94 100644
--- a/content/ppapi_plugin/ppapi_thread.cc
+++ b/content/ppapi_plugin/ppapi_thread.cc
@@ -51,7 +51,7 @@
 #include "ppapi/proxy/ppapi_messages.h"
 #include "ppapi/proxy/resource_reply_thread_registrar.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "third_party/blink/public/web/blink.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 09d5e0a..33a0107 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -341,7 +341,7 @@
     "//services/resource_coordinator/public/cpp:resource_coordinator_cpp",
     "//services/service_manager/public/cpp",
     "//services/tracing/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//third_party/webrtc/modules/desktop_capture",
 
     # We expose skia headers in the public API.
diff --git a/content/public/browser/DEPS b/content/public/browser/DEPS
index dd3a17f..571c647e 100644
--- a/content/public/browser/DEPS
+++ b/content/public/browser/DEPS
@@ -10,7 +10,7 @@
   "+services/network/public/cpp",
   "+services/service_manager/sandbox",
   "+services/resource_coordinator/public",
-  "+services/ui/public/interfaces",
+  "+services/ws/public/mojom",
 ]
 
 specific_include_rules = {
diff --git a/content/public/browser/authenticator_request_client_delegate.cc b/content/public/browser/authenticator_request_client_delegate.cc
index 8275010..caf5961 100644
--- a/content/public/browser/authenticator_request_client_delegate.cc
+++ b/content/public/browser/authenticator_request_client_delegate.cc
@@ -48,12 +48,16 @@
 void AuthenticatorRequestClientDelegate::OnTransportAvailabilityEnumerated(
     device::FidoRequestHandlerBase::TransportAvailabilityInfo data) {}
 
+bool AuthenticatorRequestClientDelegate::EmbedderControlsAuthenticatorDispatch(
+    const device::FidoAuthenticator& authenticator) {
+  return false;
+}
+
 void AuthenticatorRequestClientDelegate::BluetoothAdapterPowerChanged(
     bool is_powered_on) {}
 
 void AuthenticatorRequestClientDelegate::FidoAuthenticatorAdded(
-    const device::FidoAuthenticator& authenticator,
-    bool* hold_off_request) {}
+    const device::FidoAuthenticator& authenticator) {}
 
 void AuthenticatorRequestClientDelegate::FidoAuthenticatorRemoved(
     base::StringPiece device_id) {}
diff --git a/content/public/browser/authenticator_request_client_delegate.h b/content/public/browser/authenticator_request_client_delegate.h
index 0fc7278..df7164c 100644
--- a/content/public/browser/authenticator_request_client_delegate.h
+++ b/content/public/browser/authenticator_request_client_delegate.h
@@ -94,9 +94,20 @@
   // device::FidoRequestHandlerBase::TransportAvailabilityObserver:
   void OnTransportAvailabilityEnumerated(
       device::FidoRequestHandlerBase::TransportAvailabilityInfo data) override;
+  // If true, the request handler will defer dispatch of its request onto the
+  // given authenticator to the embedder. The embedder needs to call
+  // |StartAuthenticatorRequest| when it wants to initiate request dispatch.
+  //
+  // This method is invoked before |FidoAuthenticatorAdded|, and may be
+  // invoked multiple times for the same authenticator. Depending on the
+  // result, the request handler might decide not to make the authenticator
+  // available, in which case it never gets passed to
+  // |FidoAuthenticatorAdded|.
+  bool EmbedderControlsAuthenticatorDispatch(
+      const device::FidoAuthenticator& authenticator) override;
   void BluetoothAdapterPowerChanged(bool is_powered_on) override;
-  void FidoAuthenticatorAdded(const device::FidoAuthenticator& authenticator,
-                              bool* hold_off_request) override;
+  void FidoAuthenticatorAdded(
+      const device::FidoAuthenticator& authenticator) override;
   void FidoAuthenticatorRemoved(base::StringPiece device_id) override;
 
  private:
diff --git a/content/public/browser/devtools_manager_delegate.cc b/content/public/browser/devtools_manager_delegate.cc
index c53227b..5284bd8b 100644
--- a/content/public/browser/devtools_manager_delegate.cc
+++ b/content/public/browser/devtools_manager_delegate.cc
@@ -36,12 +36,15 @@
   return nullptr;
 }
 
-std::vector<content::BrowserContext*>
-DevToolsManagerDelegate::GetBrowserContexts() {
-  return std::vector<content::BrowserContext*>();
+std::vector<BrowserContext*> DevToolsManagerDelegate::GetBrowserContexts() {
+  return std::vector<BrowserContext*>();
 }
 
-content::BrowserContext* DevToolsManagerDelegate::CreateBrowserContext() {
+BrowserContext* DevToolsManagerDelegate::GetDefaultBrowserContext() {
+  return nullptr;
+}
+
+BrowserContext* DevToolsManagerDelegate::CreateBrowserContext() {
   return nullptr;
 }
 
diff --git a/content/public/browser/devtools_manager_delegate.h b/content/public/browser/devtools_manager_delegate.h
index 93aa2889..781af156 100644
--- a/content/public/browser/devtools_manager_delegate.h
+++ b/content/public/browser/devtools_manager_delegate.h
@@ -49,8 +49,12 @@
   // Get all live browser contexts created by CreateBrowserContext() method.
   virtual std::vector<BrowserContext*> GetBrowserContexts();
 
-  // May return null if not supported or not possible. Delegate must take
-  // ownership of the created browser context, and may destroy it at will.
+  // Get default browser context. May return null if not supported.
+  virtual BrowserContext* GetDefaultBrowserContext();
+
+  // Create new browser context. May return null if not supported or not
+  // possible. Delegate must take ownership of the created browser context, and
+  // may destroy it at will.
   virtual BrowserContext* CreateBrowserContext();
 
   // Dispose browser context that was created with |CreateBrowserContext|
diff --git a/content/public/browser/gpu_client.h b/content/public/browser/gpu_client.h
index b8d1245..528e85ad 100644
--- a/content/public/browser/gpu_client.h
+++ b/content/public/browser/gpu_client.h
@@ -9,7 +9,7 @@
 
 #include "components/viz/host/gpu_client.h"
 #include "content/common/content_export.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 
 namespace content {
 
diff --git a/content/renderer/gpu/gpu_benchmarking_extension.cc b/content/renderer/gpu/gpu_benchmarking_extension.cc
index f55fc6df..bef6372a 100644
--- a/content/renderer/gpu/gpu_benchmarking_extension.cc
+++ b/content/renderer/gpu/gpu_benchmarking_extension.cc
@@ -289,7 +289,8 @@
                        float start_x,
                        float start_y,
                        float fling_velocity,
-                       bool precise_scrolling_deltas) {
+                       bool precise_scrolling_deltas,
+                       bool scroll_by_page) {
   gfx::Rect rect = context->render_view_impl()->GetWidget()->ViewRect();
   rect -= rect.OffsetFromOrigin();
   if (!rect.Contains(start_x, start_y)) {
@@ -327,6 +328,7 @@
   gesture_params.speed_in_pixels_s = speed_in_pixels_s;
   gesture_params.prevent_fling = prevent_fling;
   gesture_params.precise_scrolling_deltas = precise_scrolling_deltas;
+  gesture_params.scroll_by_page = scroll_by_page;
 
   gesture_params.anchor.SetPoint(start_x, start_y);
 
@@ -659,6 +661,7 @@
   std::string direction = "down";
   float speed_in_pixels_s = 800;
   bool precise_scrolling_deltas = true;
+  bool scroll_by_page = false;
 
   if (!GetOptionalArg(args, &pixels_to_scroll) ||
       !GetOptionalArg(args, &callback) || !GetOptionalArg(args, &start_x) ||
@@ -666,17 +669,23 @@
       !GetOptionalArg(args, &gesture_source_type) ||
       !GetOptionalArg(args, &direction) ||
       !GetOptionalArg(args, &speed_in_pixels_s) ||
-      !GetOptionalArg(args, &precise_scrolling_deltas)) {
+      !GetOptionalArg(args, &precise_scrolling_deltas) ||
+      !GetOptionalArg(args, &scroll_by_page)) {
     return false;
   }
 
+  // For all touch inputs, always scroll by precise deltas.
   DCHECK(gesture_source_type != SyntheticGestureParams::TOUCH_INPUT ||
          precise_scrolling_deltas);
+  // Scroll by page only for mouse inputs.
+  DCHECK(!scroll_by_page ||
+         gesture_source_type == SyntheticGestureParams::MOUSE_INPUT);
+
   EnsureRemoteInterface();
   return BeginSmoothScroll(&context, args, input_injector_, pixels_to_scroll,
                            callback, gesture_source_type, direction,
                            speed_in_pixels_s, true, start_x, start_y, 0,
-                           precise_scrolling_deltas);
+                           precise_scrolling_deltas, scroll_by_page);
 }
 
 bool GpuBenchmarking::SmoothDrag(gin::Arguments* args) {
@@ -745,7 +754,8 @@
   return BeginSmoothScroll(&context, args, input_injector_, -pixels_to_scroll,
                            callback, gesture_source_type, direction,
                            speed_in_pixels_s, false, start_x, start_y,
-                           fling_velocity, true);
+                           fling_velocity, true /* precise_scrolling_deltas */,
+                           false /* scroll_by_page */);
 }
 
 bool GpuBenchmarking::ScrollBounce(gin::Arguments* args) {
diff --git a/content/renderer/media/video_capture_impl.cc b/content/renderer/media/video_capture_impl.cc
index 990a11524..77ed36bf 100644
--- a/content/renderer/media/video_capture_impl.cc
+++ b/content/renderer/media/video_capture_impl.cc
@@ -360,7 +360,7 @@
       frame = media::VideoFrame::WrapExternalSharedMemory(
           static_cast<media::VideoPixelFormat>(info->pixel_format),
           info->coded_size, info->visible_rect, info->visible_rect.size(),
-          reinterpret_cast<uint8_t*>(buffer_context->shared_memory()->memory()),
+          static_cast<uint8_t*>(buffer_context->shared_memory()->memory()),
           buffer_context->shared_memory_size(),
           buffer_context->shared_memory()->handle(),
           0 /* shared_memory_offset */, info->timestamp);
@@ -523,7 +523,7 @@
   if (!video_capture_host_.get())
     video_capture_host_.Bind(std::move(video_capture_host_info_));
   return video_capture_host_.get();
-};
+}
 
 // static
 void VideoCaptureImpl::DidFinishConsumingFrame(
diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
index fd1f38b..a26dc43e 100644
--- a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
+++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
@@ -313,6 +313,19 @@
   if (!GetPcFactory().get())
     return nullptr;
 
+  std::unique_ptr<P2PPortAllocator> port_allocator =
+      CreatePortAllocator(web_frame);
+  return GetPcFactory()
+      ->CreatePeerConnection(config, std::move(port_allocator), nullptr,
+                             observer)
+      .get();
+}
+
+std::unique_ptr<P2PPortAllocator>
+PeerConnectionDependencyFactory::CreatePortAllocator(
+    blink::WebLocalFrame* web_frame) {
+  DCHECK(web_frame);
+
   // Copy the flag from Preference associated with this WebLocalFrame.
   P2PPortAllocator::Config port_config;
   uint16_t min_port = 0;
@@ -417,16 +430,13 @@
   } else {
     network_manager.reset(new EmptyNetworkManager(network_manager_));
   }
-  std::unique_ptr<P2PPortAllocator> port_allocator(new P2PPortAllocator(
+  auto port_allocator = std::make_unique<P2PPortAllocator>(
       p2p_socket_dispatcher_, std::move(network_manager), socket_factory_.get(),
-      port_config, requesting_origin));
+      port_config, requesting_origin);
   if (IsValidPortRange(min_port, max_port))
     port_allocator->SetPortRange(min_port, max_port);
 
-  return GetPcFactory()
-      ->CreatePeerConnection(config, std::move(port_allocator),
-                             nullptr, observer)
-      .get();
+  return port_allocator;
 }
 
 scoped_refptr<webrtc::MediaStreamInterface>
@@ -561,6 +571,12 @@
                                            : nullptr;
 }
 
+rtc::Thread* PeerConnectionDependencyFactory::GetWebRtcWorkerThreadRtcThread()
+    const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return chrome_worker_thread_.IsRunning() ? worker_thread_ : nullptr;
+}
+
 scoped_refptr<base::SingleThreadTaskRunner>
 PeerConnectionDependencyFactory::GetWebRtcSignalingThread() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory.h b/content/renderer/media/webrtc/peer_connection_dependency_factory.h
index db7919f..39f6f9ed 100644
--- a/content/renderer/media/webrtc/peer_connection_dependency_factory.h
+++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.h
@@ -42,6 +42,7 @@
 
 class IpcNetworkManager;
 class IpcPacketSocketFactory;
+class P2PPortAllocator;
 class WebRtcAudioDeviceImpl;
 
 // Object factory for RTC PeerConnections.
@@ -81,6 +82,11 @@
       blink::WebLocalFrame* web_frame,
       webrtc::PeerConnectionObserver* observer);
 
+  // Creates a PortAllocator that uses Chrome IPC sockets and enforces privacy
+  // controls according to the permissions granted on the page.
+  virtual std::unique_ptr<P2PPortAllocator> CreatePortAllocator(
+      blink::WebLocalFrame* web_frame);
+
   // Creates a libjingle representation of a Session description. Used by a
   // RTCPeerConnectionHandler instance.
   virtual webrtc::SessionDescriptionInterface* CreateSessionDescription(
@@ -105,6 +111,8 @@
 
   void EnsureInitialized();
   scoped_refptr<base::SingleThreadTaskRunner> GetWebRtcWorkerThread() const;
+  // TODO(bugs.webrtc.org/9419): Remove once WebRTC can be built as a component.
+  rtc::Thread* GetWebRtcWorkerThreadRtcThread() const;
   virtual scoped_refptr<base::SingleThreadTaskRunner> GetWebRtcSignalingThread()
       const;
 
diff --git a/content/renderer/media/webrtc/rtc_video_encoder.cc b/content/renderer/media/webrtc/rtc_video_encoder.cc
index 5df15b2..db58c8ad 100644
--- a/content/renderer/media/webrtc/rtc_video_encoder.cc
+++ b/content/renderer/media/webrtc/rtc_video_encoder.cc
@@ -556,9 +556,9 @@
     capture_timestamp_ms = current_time_ms;
   }
 
-  webrtc::EncodedImage image(
-      reinterpret_cast<uint8_t*>(output_buffer->memory()),
-      metadata.payload_size_bytes, output_buffer->mapped_size());
+  webrtc::EncodedImage image(static_cast<uint8_t*>(output_buffer->memory()),
+                             metadata.payload_size_bytes,
+                             output_buffer->mapped_size());
   image._encodedWidth = input_visible_size_.width();
   image._encodedHeight = input_visible_size_.height();
   image._timeStamp = rtp_timestamp.value();
@@ -654,7 +654,7 @@
     frame = media::VideoFrame::WrapExternalSharedMemory(
         media::PIXEL_FORMAT_I420, input_frame_coded_size_,
         gfx::Rect(input_visible_size_), input_visible_size_,
-        reinterpret_cast<uint8_t*>(input_buffer->memory()),
+        static_cast<uint8_t*>(input_buffer->memory()),
         input_buffer->mapped_size(), input_buffer->handle(), 0, timestamp);
     if (!frame.get()) {
       LogAndNotifyError(FROM_HERE, "failed to create frame",
diff --git a/content/renderer/media_recorder/vea_encoder.cc b/content/renderer/media_recorder/vea_encoder.cc
index 0a81de0..faedb367 100644
--- a/content/renderer/media_recorder/vea_encoder.cc
+++ b/content/renderer/media_recorder/vea_encoder.cc
@@ -133,7 +133,7 @@
   base::SharedMemory* output_buffer =
       output_buffers_[bitstream_buffer_id].get();
   std::unique_ptr<std::string> data(new std::string);
-  data->append(reinterpret_cast<char*>(output_buffer->memory()),
+  data->append(static_cast<char*>(output_buffer->memory()),
                metadata.payload_size_bytes);
 
   const auto front_frame = frames_in_encode_.front();
@@ -231,7 +231,7 @@
     video_frame = media::VideoFrame::WrapExternalSharedMemory(
         media::PIXEL_FORMAT_I420, vea_requested_input_coded_size_,
         gfx::Rect(input_visible_size_), input_visible_size_,
-        reinterpret_cast<uint8_t*>(input_buffer->memory()),
+        static_cast<uint8_t*>(input_buffer->memory()),
         input_buffer->mapped_size(), input_buffer->handle(), 0,
         frame->timestamp());
     if (!video_frame) {
diff --git a/content/renderer/mus/BUILD.gn b/content/renderer/mus/BUILD.gn
index 3fbd631..cd47da6 100644
--- a/content/renderer/mus/BUILD.gn
+++ b/content/renderer/mus/BUILD.gn
@@ -32,7 +32,7 @@
     "//media/mojo/interfaces:remoting",
     "//services/service_manager/public/cpp",
     "//services/ui/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//third_party/blink/public:blink",
     "//ui/events:events",
     "//ui/events:events_base",
diff --git a/content/renderer/mus/mus_embedded_frame.cc b/content/renderer/mus/mus_embedded_frame.cc
index e0fdd26..ee69bec2 100644
--- a/content/renderer/mus/mus_embedded_frame.cc
+++ b/content/renderer/mus/mus_embedded_frame.cc
@@ -14,7 +14,7 @@
 #include "components/viz/client/local_surface_id_provider.h"
 #include "content/renderer/mus/renderer_window_tree_client.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 
 namespace content {
 namespace {
diff --git a/content/renderer/mus/render_widget_window_tree_client_factory.cc b/content/renderer/mus/render_widget_window_tree_client_factory.cc
index bda28e54..b308559 100644
--- a/content/renderer/mus/render_widget_window_tree_client_factory.cc
+++ b/content/renderer/mus/render_widget_window_tree_client_factory.cc
@@ -18,7 +18,7 @@
 #include "content/renderer/mus/renderer_window_tree_client.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "services/service_manager/public/cpp/service.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
diff --git a/content/renderer/mus/renderer_window_tree_client.h b/content/renderer/mus/renderer_window_tree_client.h
index bbc8c14..d8e50389 100644
--- a/content/renderer/mus/renderer_window_tree_client.h
+++ b/content/renderer/mus/renderer_window_tree_client.h
@@ -15,7 +15,7 @@
 #include "content/common/render_widget_window_tree_client_factory.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/ui/common/types.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace base {
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 5cd06054..e3a4a41 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -146,7 +146,7 @@
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
 #include "services/ui/public/cpp/gpu/gpu.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "skia/ext/skia_memory_dump_provider.h"
 #include "third_party/blink/public/platform/scheduler/child/webthread_base.h"
 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 48125ba59..e6367ff 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -65,6 +65,7 @@
 #include "content/renderer/media_capture_from_element/html_video_element_capturer_source.h"
 #include "content/renderer/media_recorder/media_recorder_handler.h"
 #include "content/renderer/mojo/blink_interface_provider_impl.h"
+#include "content/renderer/p2p/port_allocator.h"
 #include "content/renderer/push_messaging/push_provider.h"
 #include "content/renderer/render_thread_impl.h"
 #include "content/renderer/storage_util.h"
@@ -842,6 +843,38 @@
 
 //------------------------------------------------------------------------------
 
+scoped_refptr<base::SingleThreadTaskRunner>
+RendererBlinkPlatformImpl::GetWebRtcWorkerThread() {
+  RenderThreadImpl* render_thread = RenderThreadImpl::current();
+  DCHECK(render_thread);
+  PeerConnectionDependencyFactory* rtc_dependency_factory =
+      render_thread->GetPeerConnectionDependencyFactory();
+  rtc_dependency_factory->EnsureInitialized();
+  return rtc_dependency_factory->GetWebRtcWorkerThread();
+}
+
+rtc::Thread* RendererBlinkPlatformImpl::GetWebRtcWorkerThreadRtcThread() {
+  RenderThreadImpl* render_thread = RenderThreadImpl::current();
+  DCHECK(render_thread);
+  PeerConnectionDependencyFactory* rtc_dependency_factory =
+      render_thread->GetPeerConnectionDependencyFactory();
+  rtc_dependency_factory->EnsureInitialized();
+  return rtc_dependency_factory->GetWebRtcWorkerThreadRtcThread();
+}
+
+std::unique_ptr<cricket::PortAllocator>
+RendererBlinkPlatformImpl::CreateWebRtcPortAllocator(
+    blink::WebLocalFrame* frame) {
+  RenderThreadImpl* render_thread = RenderThreadImpl::current();
+  DCHECK(render_thread);
+  PeerConnectionDependencyFactory* rtc_dependency_factory =
+      render_thread->GetPeerConnectionDependencyFactory();
+  rtc_dependency_factory->EnsureInitialized();
+  return rtc_dependency_factory->CreatePortAllocator(frame);
+}
+
+//------------------------------------------------------------------------------
+
 std::unique_ptr<WebCanvasCaptureHandler>
 RendererBlinkPlatformImpl::CreateCanvasCaptureHandler(
     const WebSize& size,
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index afdd949..0e7ddd9 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -162,6 +162,10 @@
       scoped_refptr<base::SingleThreadTaskRunner> task_runner) override;
   std::unique_ptr<blink::WebMediaStreamCenter> CreateMediaStreamCenter()
       override;
+  scoped_refptr<base::SingleThreadTaskRunner> GetWebRtcWorkerThread() override;
+  rtc::Thread* GetWebRtcWorkerThreadRtcThread() override;
+  std::unique_ptr<cricket::PortAllocator> CreateWebRtcPortAllocator(
+      blink::WebLocalFrame* frame) override;
   std::unique_ptr<blink::WebCanvasCaptureHandler> CreateCanvasCaptureHandler(
       const blink::WebSize& size,
       double frame_rate,
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index 67a5d18..d81b3bad 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -20,7 +20,6 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "content/common/service_worker/service_worker.mojom.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/referrer.h"
diff --git a/content/renderer/service_worker/service_worker_network_provider.cc b/content/renderer/service_worker/service_worker_network_provider.cc
index c3cec85..64b0d8bc 100644
--- a/content/renderer/service_worker/service_worker_network_provider.cc
+++ b/content/renderer/service_worker/service_worker_network_provider.cc
@@ -7,7 +7,6 @@
 #include "base/atomic_sequence_num.h"
 #include "base/single_thread_task_runner.h"
 #include "content/common/navigation_params.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_provider.mojom.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/common/browser_side_navigation_policy.h"
diff --git a/content/renderer/service_worker/service_worker_provider_context_unittest.cc b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
index 39dad20..0e34a0d 100644
--- a/content/renderer/service_worker/service_worker_provider_context_unittest.cc
+++ b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
@@ -17,7 +17,6 @@
 #include "base/test/scoped_task_environment.h"
 #include "content/child/thread_safe_sender.h"
 #include "content/common/service_worker/service_worker_container.mojom.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/resource_type.h"
diff --git a/content/renderer/service_worker/web_service_worker_impl.cc b/content/renderer/service_worker/web_service_worker_impl.cc
index dba3fc3..75f91c1 100644
--- a/content/renderer/service_worker/web_service_worker_impl.cc
+++ b/content/renderer/service_worker/web_service_worker_impl.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/macros.h"
-#include "content/common/service_worker/service_worker_messages.h"
 #include "content/renderer/service_worker/service_worker_context_client.h"
 #include "content/renderer/service_worker/service_worker_provider_context.h"
 #include "content/renderer/service_worker/web_service_worker_provider_impl.h"
diff --git a/content/shell/browser/layout_test/layout_test_permission_manager.cc b/content/shell/browser/layout_test/layout_test_permission_manager.cc
index a6ad4ea9..cabc6868 100644
--- a/content/shell/browser/layout_test/layout_test_permission_manager.cc
+++ b/content/shell/browser/layout_test/layout_test_permission_manager.cc
@@ -199,15 +199,17 @@
   PermissionDescription description(permission, url.GetOrigin(),
                                     embedding_url.GetOrigin());
 
-  base::AutoLock lock(permissions_lock_);
+  {
+    base::AutoLock lock(permissions_lock_);
 
-  auto it = permissions_.find(description);
-  if (it == permissions_.end()) {
-    permissions_.insert(
-        std::pair<PermissionDescription, blink::mojom::PermissionStatus>(
-            description, status));
-  } else {
-    it->second = status;
+    auto it = permissions_.find(description);
+    if (it == permissions_.end()) {
+      permissions_.insert(
+          std::pair<PermissionDescription, blink::mojom::PermissionStatus>(
+              description, status));
+    } else {
+      it->second = status;
+    }
   }
 
   OnPermissionChanged(description, status);
diff --git a/content/shell/browser/shell_devtools_manager_delegate.cc b/content/shell/browser/shell_devtools_manager_delegate.cc
index d0a3a9a..d7adc1e 100644
--- a/content/shell/browser/shell_devtools_manager_delegate.cc
+++ b/content/shell/browser/shell_devtools_manager_delegate.cc
@@ -180,6 +180,10 @@
 ShellDevToolsManagerDelegate::~ShellDevToolsManagerDelegate() {
 }
 
+BrowserContext* ShellDevToolsManagerDelegate::GetDefaultBrowserContext() {
+  return browser_context_;
+}
+
 scoped_refptr<DevToolsAgentHost>
 ShellDevToolsManagerDelegate::CreateNewTarget(const GURL& url) {
   Shell* shell = Shell::CreateNewWindow(browser_context_,
diff --git a/content/shell/browser/shell_devtools_manager_delegate.h b/content/shell/browser/shell_devtools_manager_delegate.h
index b155fa47..435ea7b 100644
--- a/content/shell/browser/shell_devtools_manager_delegate.h
+++ b/content/shell/browser/shell_devtools_manager_delegate.h
@@ -23,6 +23,7 @@
   ~ShellDevToolsManagerDelegate() override;
 
   // DevToolsManagerDelegate implementation.
+  BrowserContext* GetDefaultBrowserContext() override;
   scoped_refptr<DevToolsAgentHost> CreateNewTarget(const GURL& url) override;
   std::string GetDiscoveryPageHTML() override;
   bool HasBundledFrontendResources() override;
diff --git a/device/fido/fido_request_handler_base.cc b/device/fido/fido_request_handler_base.cc
index 7886f009..ebe07db7 100644
--- a/device/fido/fido_request_handler_base.cc
+++ b/device/fido/fido_request_handler_base.cc
@@ -206,22 +206,23 @@
   // If |observer_| exists, dispatching request to |authenticator_ptr| is
   // delegated to |observer_|. Else, dispatch request to |authenticator_ptr|
   // immediately.
-  bool should_delay_request = false;
-  if (observer_)
-    observer_->FidoAuthenticatorAdded(*authenticator_ptr,
-                                      &should_delay_request);
+  bool embedder_controls_dispatch = false;
+  if (observer_) {
+    embedder_controls_dispatch =
+        observer_->EmbedderControlsAuthenticatorDispatch(*authenticator_ptr);
+    observer_->FidoAuthenticatorAdded(*authenticator_ptr);
+  }
 
-  if (should_delay_request)
-    return;
-
-  // Post |InitializeAuthenticatorAndDispatchRequest| into its own task. This
-  // avoids hairpinning, even if the authenticator immediately invokes the
-  // request callback.
-  base::SequencedTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &FidoRequestHandlerBase::InitializeAuthenticatorAndDispatchRequest,
-          GetWeakPtr(), authenticator_ptr));
+  if (!embedder_controls_dispatch) {
+    // Post |InitializeAuthenticatorAndDispatchRequest| into its own task. This
+    // avoids hairpinning, even if the authenticator immediately invokes the
+    // request callback.
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &FidoRequestHandlerBase::InitializeAuthenticatorAndDispatchRequest,
+            GetWeakPtr(), authenticator_ptr));
+  }
 }
 
 void FidoRequestHandlerBase::SetPlatformAuthenticatorOrMarkUnavailable(
diff --git a/device/fido/fido_request_handler_base.h b/device/fido/fido_request_handler_base.h
index fa1f960..f6d5967 100644
--- a/device/fido/fido_request_handler_base.h
+++ b/device/fido/fido_request_handler_base.h
@@ -92,9 +92,22 @@
     // This method will not be invoked until the observer is set.
     virtual void OnTransportAvailabilityEnumerated(
         TransportAvailabilityInfo data) = 0;
+
+    // If true, the request handler will defer dispatch of its request onto the
+    // given authenticator to the embedder. The embedder needs to call
+    // |StartAuthenticatorRequest| when it wants to initiate request dispatch.
+    //
+    // This method is invoked before |FidoAuthenticatorAdded|, and may be
+    // invoked multiple times for the same authenticator. Depending on the
+    // result, the request handler might decide not to make the authenticator
+    // available, in which case it never gets passed to
+    // |FidoAuthenticatorAdded|.
+    virtual bool EmbedderControlsAuthenticatorDispatch(
+        const FidoAuthenticator& authenticator) = 0;
+
     virtual void BluetoothAdapterPowerChanged(bool is_powered_on) = 0;
-    virtual void FidoAuthenticatorAdded(const FidoAuthenticator& authenticator,
-                                        bool* hold_off_request) = 0;
+    virtual void FidoAuthenticatorAdded(
+        const FidoAuthenticator& authenticator) = 0;
     virtual void FidoAuthenticatorRemoved(base::StringPiece device_id) = 0;
   };
 
@@ -136,7 +149,7 @@
   // Set the platform authenticator for this request, if one is available.
   // |AuthenticatorImpl| must call this method after invoking |set_oberver| even
   // if no platform authenticator is available, in which case it passes nullptr.
-  void SetPlatformAuthenticatorOrMarkUnavailable(
+  virtual void SetPlatformAuthenticatorOrMarkUnavailable(
       base::Optional<PlatformAuthenticatorInfo> platform_authenticator_info);
 
   TransportAvailabilityInfo& transport_availability_info() {
diff --git a/device/fido/fido_request_handler_unittest.cc b/device/fido/fido_request_handler_unittest.cc
index 94948f9..099070f7 100644
--- a/device/fido/fido_request_handler_unittest.cc
+++ b/device/fido/fido_request_handler_unittest.cc
@@ -77,10 +77,14 @@
     transport_availability_notification_receiver_.callback().Run(
         std::move(data));
   }
+  bool EmbedderControlsAuthenticatorDispatch(
+      const FidoAuthenticator&) override {
+    return false;
+  }
 
   void BluetoothAdapterPowerChanged(bool is_powered_on) override {}
-  void FidoAuthenticatorAdded(const FidoAuthenticator& authenticator,
-                              bool* hold_off_request) override {}
+  void FidoAuthenticatorAdded(const FidoAuthenticator& authenticator) override {
+  }
   void FidoAuthenticatorRemoved(base::StringPiece device_id) override {}
 
  private:
diff --git a/device/fido/make_credential_handler_unittest.cc b/device/fido/make_credential_handler_unittest.cc
index 0ffd560f..ae20083e 100644
--- a/device/fido/make_credential_handler_unittest.cc
+++ b/device/fido/make_credential_handler_unittest.cc
@@ -106,11 +106,6 @@
     if (!base::ContainsKey(transports, Transport::kNearFieldCommunication))
       EXPECT_FALSE(nfc_discovery()->is_start_requested());
 
-    // Even with FidoTransportProtocol::kInternal allowed, unless the platform
-    // authenticator factory returns a FidoAuthenticator instance (which it will
-    // not be default), the transport will be marked `unavailable`.
-    transports.erase(Transport::kInternal);
-
     EXPECT_THAT(
         request_handler->transport_availability_info().available_transports,
         ::testing::UnorderedElementsAreArray(transports));
@@ -273,6 +268,11 @@
 // TODO(crbug.com/873710): Platform authenticators are temporarily disabled if
 // AuthenticatorAttachment is unset (kAny).
 TEST_F(FidoMakeCredentialHandlerTest, AnyAttachment) {
+  auto platform_device = MockFidoDevice::MakeCtap(
+      ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice));
+  platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
+  set_mock_platform_device(std::move(platform_device));
+
   auto request_handler =
       CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
           AuthenticatorSelectionCriteria(
@@ -280,6 +280,13 @@
               false /* require_resident_key */,
               UserVerificationRequirement::kPreferred));
 
+  // MakeCredentialHandler will not dispatch the kAny request to the platform
+  // authenticator since the request does not get dispatched through UI. Despite
+  // setting a platform authenticator, the internal transport never becomes
+  // available.
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(callback().was_called());
+
   ExpectAllowedTransportsForRequestAre(
       request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
                               FidoTransportProtocol::kNearFieldCommunication,
@@ -302,6 +309,16 @@
 }
 
 TEST_F(FidoMakeCredentialHandlerTest, PlatformAttachment) {
+  // Add a platform device to trigger the transport actually becoming available.
+  // We don't care about the result of the request.
+  auto platform_device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
+      test_data::kTestGetInfoResponsePlatformDevice);
+  platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
+  platform_device->ExpectCtap2CommandAndDoNotRespond(
+      CtapRequestCommand::kAuthenticatorMakeCredential);
+  EXPECT_CALL(*platform_device, Cancel());
+  set_mock_platform_device(std::move(platform_device));
+
   auto request_handler =
       CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
           AuthenticatorSelectionCriteria(
diff --git a/device/fido/make_credential_request_handler.cc b/device/fido/make_credential_request_handler.cc
index 6586589d..6149654 100644
--- a/device/fido/make_credential_request_handler.cc
+++ b/device/fido/make_credential_request_handler.cc
@@ -35,11 +35,6 @@
        !options.is_platform_device()) ||
       (authenticator_selection_criteria.authenticator_attachement() ==
            AuthenticatorAttachment::kCrossPlatform &&
-       options.is_platform_device()) ||
-      // TODO(crbug.com/873710): Reenable platform authenticators for kAny,
-      // once Touch ID is integrated into the UI.
-      (authenticator_selection_criteria.authenticator_attachement() ==
-           AuthenticatorAttachment::kAny &&
        options.is_platform_device())) {
     return false;
   }
@@ -78,9 +73,8 @@
               FidoTransportProtocol::kNearFieldCommunication,
               FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy};
     case AttachmentType::kAny:
-      // TODO(crbug.com/873710): Re-enable platform authenticators for kAny,
-      // once Touch ID is integrated into the UI.
-      return {FidoTransportProtocol::kNearFieldCommunication,
+      return {FidoTransportProtocol::kInternal,
+              FidoTransportProtocol::kNearFieldCommunication,
               FidoTransportProtocol::kUsbHumanInterfaceDevice,
               FidoTransportProtocol::kBluetoothLowEnergy,
               FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy};
@@ -153,4 +147,25 @@
   OnAuthenticatorResponse(authenticator, response_code, std::move(response));
 }
 
+void MakeCredentialRequestHandler::SetPlatformAuthenticatorOrMarkUnavailable(
+    base::Optional<PlatformAuthenticatorInfo> platform_authenticator_info) {
+  if (platform_authenticator_info) {
+    // TODO(crbug.com/873710): In the case of a request with
+    // AuthenticatorAttachment::kAny and when there is no embedder-provided
+    // transport selection UI, disable the platform authenticator to avoid the
+    // Touch ID fingerprint prompt competing with external devices.
+    const bool has_transport_selection_ui =
+        observer() && observer()->EmbedderControlsAuthenticatorDispatch(
+                          *platform_authenticator_info->authenticator);
+    if (authenticator_selection_criteria_.authenticator_attachement() ==
+            AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny &&
+        !has_transport_selection_ui) {
+      platform_authenticator_info = base::nullopt;
+    }
+  }
+
+  FidoRequestHandlerBase::SetPlatformAuthenticatorOrMarkUnavailable(
+      std::move(platform_authenticator_info));
+}
+
 }  // namespace device
diff --git a/device/fido/make_credential_request_handler.h b/device/fido/make_credential_request_handler.h
index d27637d..a5e799f 100644
--- a/device/fido/make_credential_request_handler.h
+++ b/device/fido/make_credential_request_handler.h
@@ -43,9 +43,14 @@
       RegisterResponseCallback completion_callback);
   ~MakeCredentialRequestHandler() override;
 
+  // FidoRequestHandlerBase:
+  void SetPlatformAuthenticatorOrMarkUnavailable(
+      base::Optional<PlatformAuthenticatorInfo> platform_authenticator_info)
+      override;
+
  private:
   // FidoRequestHandlerBase:
-  void DispatchRequest(FidoAuthenticator* authenticator) final;
+  void DispatchRequest(FidoAuthenticator* authenticator) override;
 
   void HandleResponse(
       FidoAuthenticator* authenticator,
diff --git a/extensions/browser/api/declarative_net_request/ruleset_matcher.cc b/extensions/browser/api/declarative_net_request/ruleset_matcher.cc
index bab9700..4d9e99b 100644
--- a/extensions/browser/api/declarative_net_request/ruleset_matcher.cc
+++ b/extensions/browser/api/declarative_net_request/ruleset_matcher.cc
@@ -55,7 +55,7 @@
           base::make_span(reinterpret_cast<const uint8_t*>(ruleset_data.data()),
                           ruleset_data.size()),
           expected_ruleset_checksum)) {
-    return kLoadErrorRulesetVerification;
+    return kLoadErrorChecksumMismatch;
   }
 
   UMA_HISTOGRAM_TIMES(
diff --git a/extensions/browser/api/declarative_net_request/ruleset_matcher.h b/extensions/browser/api/declarative_net_request/ruleset_matcher.h
index e3b4838..76876a2 100644
--- a/extensions/browser/api/declarative_net_request/ruleset_matcher.h
+++ b/extensions/browser/api/declarative_net_request/ruleset_matcher.h
@@ -49,8 +49,7 @@
     kLoadErrorFileRead = 2,
 
     // Ruleset loading failed due to a checksum mismatch.
-    // TODO(karandeepb): Rename this to kLoadErrorChecksumMismatch.
-    kLoadErrorRulesetVerification = 3,
+    kLoadErrorChecksumMismatch = 3,
 
     // Ruleset loading failed due to version header mismatch.
     kLoadErrorVersionMismatch = 4,
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_video_encoder_vea.cc b/extensions/renderer/api/display_source/wifi_display/wifi_display_video_encoder_vea.cc
index df6fdc4..b7f2c34 100644
--- a/extensions/renderer/api/display_source/wifi_display/wifi_display_video_encoder_vea.cc
+++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_video_encoder_vea.cc
@@ -209,7 +209,7 @@
   if (!encoded_callback_.is_null()) {
     encoded_callback_.Run(
         std::unique_ptr<WiFiDisplayEncodedFrame>(new WiFiDisplayEncodedFrame(
-            std::string(reinterpret_cast<const char*>(output_buffer->memory()),
+            std::string(static_cast<const char*>(output_buffer->memory()),
                         payload_size),
             request.reference_time, base::TimeTicks::Now(), key_frame)));
   }
diff --git a/extensions/renderer/user_script_set.cc b/extensions/renderer/user_script_set.cc
index 6d34525..35657b8 100644
--- a/extensions/renderer/user_script_set.cc
+++ b/extensions/renderer/user_script_set.cc
@@ -108,7 +108,7 @@
   if (!shared_memory_->Map(sizeof(base::Pickle::Header)))
     return false;
   base::Pickle::Header* pickle_header =
-      reinterpret_cast<base::Pickle::Header*>(shared_memory_->memory());
+      static_cast<base::Pickle::Header*>(shared_memory_->memory());
 
   // Now map in the rest of the block.
   int pickle_size = sizeof(base::Pickle::Header) + pickle_header->payload_size;
@@ -118,7 +118,7 @@
 
   // Unpickle scripts.
   uint32_t num_scripts = 0;
-  base::Pickle pickle(reinterpret_cast<char*>(shared_memory_->memory()),
+  base::Pickle pickle(static_cast<char*>(shared_memory_->memory()),
                       pickle_size);
   base::PickleIterator iter(pickle);
   base::debug::Alias(&pickle_size);
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc
index a658754..5d9c7af 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest.cc
+++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc
@@ -311,8 +311,7 @@
 
   bool NoCommandsWritten() {
     scoped_refptr<Buffer> ring_buffer = helper_->get_ring_buffer();
-    const uint8_t* cmds =
-        reinterpret_cast<const uint8_t*>(ring_buffer->memory());
+    const uint8_t* cmds = static_cast<const uint8_t*>(ring_buffer->memory());
     const uint8_t* end = cmds + ring_buffer->size();
     for (; cmds < end; ++cmds) {
       if (*cmds != kInitialValue) {
diff --git a/gpu/command_buffer/client/mapped_memory.h b/gpu/command_buffer/client/mapped_memory.h
index 8a6fb75..7eeee65 100644
--- a/gpu/command_buffer/client/mapped_memory.h
+++ b/gpu/command_buffer/client/mapped_memory.h
@@ -101,8 +101,7 @@
   // Returns true if pointer is in the range of this block.
   bool IsInChunk(void* pointer) const {
     return pointer >= shm_->memory() &&
-           pointer <
-               reinterpret_cast<const int8_t*>(shm_->memory()) + shm_->size();
+           pointer < static_cast<const int8_t*>(shm_->memory()) + shm_->size();
   }
 
   // Returns true of any memory in this chunk is in use or free pending token.
diff --git a/gpu/command_buffer/client/raster_implementation_unittest.cc b/gpu/command_buffer/client/raster_implementation_unittest.cc
index 76df1d97..662a39a 100644
--- a/gpu/command_buffer/client/raster_implementation_unittest.cc
+++ b/gpu/command_buffer/client/raster_implementation_unittest.cc
@@ -183,8 +183,7 @@
 
   bool NoCommandsWritten() {
     scoped_refptr<Buffer> ring_buffer = helper_->get_ring_buffer();
-    const uint8_t* cmds =
-        reinterpret_cast<const uint8_t*>(ring_buffer->memory());
+    const uint8_t* cmds = static_cast<const uint8_t*>(ring_buffer->memory());
     const uint8_t* end = cmds + ring_buffer->size();
     for (; cmds < end; ++cmds) {
       if (*cmds != kInitialValue) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index 2993a83..a717638 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -486,7 +486,7 @@
                                                           &shared_memory_id_);
   shared_memory_offset_ = kSharedMemoryOffset;
   shared_memory_address_ =
-      reinterpret_cast<int8_t*>(buffer->memory()) + shared_memory_offset_;
+      static_cast<int8_t*>(buffer->memory()) + shared_memory_offset_;
   shared_memory_base_ = buffer->memory();
   ClearSharedMemory();
 
@@ -792,12 +792,11 @@
   for (GLsizei ii = 0; ii < count; ++ii) {
     if (str && str[ii]) {
       size_t str_len = strlen(str[ii]);
-      memcpy(reinterpret_cast<char*>(shared_memory_address_) + offset,
-             str[ii], str_len);
+      memcpy(static_cast<char*>(shared_memory_address_) + offset, str[ii],
+             str_len);
       offset += str_len;
     }
-    memcpy(reinterpret_cast<char*>(shared_memory_address_) + offset,
-           &str_end, 1);
+    memcpy(static_cast<char*>(shared_memory_address_) + offset, &str_end, 1);
     offset += 1;
   }
   cmd::SetBucketData cmd2;
@@ -1668,7 +1667,7 @@
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
 }
 
-void GLES2DecoderTestBase::AddExpectationsForGenVertexArraysOES(){
+void GLES2DecoderTestBase::AddExpectationsForGenVertexArraysOES() {
   if (group_->feature_info()->feature_flags().native_vertex_array_object) {
       EXPECT_CALL(*gl_, GenVertexArraysOES(1, _))
           .WillOnce(SetArgPointee<1>(kServiceVertexArrayId))
@@ -1676,7 +1675,7 @@
   }
 }
 
-void GLES2DecoderTestBase::AddExpectationsForDeleteVertexArraysOES(){
+void GLES2DecoderTestBase::AddExpectationsForDeleteVertexArraysOES() {
   if (group_->feature_info()->feature_flags().native_vertex_array_object) {
       EXPECT_CALL(*gl_, DeleteVertexArraysOES(1, _))
           .Times(1)
@@ -2244,11 +2243,11 @@
   DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
   DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                shared_memory_id_, kSharedMemoryOffset);
-};
+}
 
 void GLES2DecoderTestBase::SetupSampler() {
   DoBindSampler(0, client_sampler_id_, kServiceSamplerId);
-};
+}
 
 void GLES2DecoderTestBase::DeleteVertexBuffer() {
   DoDeleteBuffer(client_buffer_id_, kServiceBufferId);
@@ -2462,7 +2461,7 @@
                                                           &shared_memory_id_);
   shared_memory_offset_ = kSharedMemoryOffset;
   shared_memory_address_ =
-      reinterpret_cast<int8_t*>(buffer->memory()) + shared_memory_offset_;
+      static_cast<int8_t*>(buffer->memory()) + shared_memory_offset_;
   shared_memory_base_ = buffer->memory();
   shared_memory_size_ = kSharedBufferSize - shared_memory_offset_;
 
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.cc b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
index fb6ac76..6e1ed19 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
@@ -224,7 +224,7 @@
                                                           &shared_memory_id_);
   shared_memory_offset_ = kSharedMemoryOffset;
   shared_memory_address_ =
-      reinterpret_cast<int8_t*>(buffer->memory()) + shared_memory_offset_;
+      static_cast<int8_t*>(buffer->memory()) + shared_memory_offset_;
   shared_memory_base_ = buffer->memory();
   ClearSharedMemory();
 
@@ -379,12 +379,11 @@
   for (GLsizei ii = 0; ii < count; ++ii) {
     if (str && str[ii]) {
       size_t str_len = strlen(str[ii]);
-      memcpy(reinterpret_cast<char*>(shared_memory_address_) + offset, str[ii],
+      memcpy(static_cast<char*>(shared_memory_address_) + offset, str[ii],
              str_len);
       offset += str_len;
     }
-    memcpy(reinterpret_cast<char*>(shared_memory_address_) + offset, &str_end,
-           1);
+    memcpy(static_cast<char*>(shared_memory_address_) + offset, &str_end, 1);
     offset += 1;
   }
   cmd::SetBucketData cmd2;
diff --git a/gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.cc b/gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.cc
index 13a9fd8..1769a4a 100644
--- a/gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.cc
+++ b/gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.cc
@@ -206,7 +206,7 @@
 void* GpuMemoryBufferImplSharedMemory::memory(size_t plane) {
   DCHECK(mapped_);
   DCHECK_LT(plane, gfx::NumberOfPlanesForBufferFormat(format_));
-  return reinterpret_cast<uint8_t*>(shared_memory_->memory()) + offset_ +
+  return static_cast<uint8_t*>(shared_memory_->memory()) + offset_ +
          gfx::BufferOffsetForBufferFormat(size_, format_, plane);
 }
 
diff --git a/headless/lib/browser/headless_devtools_manager_delegate.cc b/headless/lib/browser/headless_devtools_manager_delegate.cc
index d5050e5f..bd611875 100644
--- a/headless/lib/browser/headless_devtools_manager_delegate.cc
+++ b/headless/lib/browser/headless_devtools_manager_delegate.cc
@@ -81,6 +81,10 @@
   }
   return contexts;
 }
+content::BrowserContext*
+HeadlessDevToolsManagerDelegate::GetDefaultBrowserContext() {
+  return HeadlessBrowserContextImpl::From(browser_->GetDefaultBrowserContext());
+}
 
 content::BrowserContext*
 HeadlessDevToolsManagerDelegate::CreateBrowserContext() {
diff --git a/headless/lib/browser/headless_devtools_manager_delegate.h b/headless/lib/browser/headless_devtools_manager_delegate.h
index 50c2c9d..eb40d404 100644
--- a/headless/lib/browser/headless_devtools_manager_delegate.h
+++ b/headless/lib/browser/headless_devtools_manager_delegate.h
@@ -44,6 +44,7 @@
                       content::DevToolsAgentHostClient* client) override;
 
   std::vector<content::BrowserContext*> GetBrowserContexts() override;
+  content::BrowserContext* GetDefaultBrowserContext() override;
   content::BrowserContext* CreateBrowserContext() override;
   void DisposeBrowserContext(content::BrowserContext* context,
                              DisposeCallback callback) override;
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_observer.h b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_observer.h
index 44503d78..b80d6f0 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_observer.h
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_observer.h
@@ -52,6 +52,10 @@
   virtual void FullscreenModelWasReset(FullscreenController* controller,
                                        FullscreenAnimator* animator) {}
 
+  // Invoked before the FullscreenController service is shut down.
+  virtual void FullscreenControllerWillShutDown(
+      FullscreenController* controller) {}
+
  private:
   DISALLOW_COPY_AND_ASSIGN(FullscreenControllerObserver);
 };
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_mediator.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_mediator.mm
index e3cc1cfe..cf0a8ff6 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_mediator.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_mediator.mm
@@ -77,6 +77,9 @@
 }
 
 void FullscreenMediator::Disconnect() {
+  for (auto& observer : observers_) {
+    observer.FullscreenControllerWillShutDown(controller_);
+  }
   [animator_ stopAnimation:YES];
   animator_ = nil;
   model_->RemoveObserver(this);
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_mediator_unittest.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_mediator_unittest.mm
index e393c406e..6e02278d4 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_mediator_unittest.mm
@@ -6,6 +6,7 @@
 
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_model.h"
 #import "ios/chrome/browser/ui/fullscreen/test/fullscreen_model_test_util.h"
+#import "ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.h"
 #import "ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller_observer.h"
 #import "ios/chrome/browser/ui/fullscreen/test/test_fullscreen_mediator.h"
 #include "testing/platform_test.h"
@@ -17,13 +18,14 @@
 // Test fixture for FullscreenMediator.
 class FullscreenMediatorTest : public PlatformTest {
  public:
-  FullscreenMediatorTest() : PlatformTest(), mediator_(controller(), &model_) {
+  FullscreenMediatorTest()
+      : PlatformTest(), controller_(&model_), mediator_(&controller_, &model_) {
     SetUpFullscreenModelForTesting(&model_, 100);
     mediator_.AddObserver(&observer_);
   }
   ~FullscreenMediatorTest() override {
-    mediator_.RemoveObserver(&observer_);
     mediator_.Disconnect();
+    EXPECT_TRUE(observer_.is_shut_down());
   }
 
   FullscreenController* controller() {
@@ -37,6 +39,7 @@
 
  private:
   FullscreenModel model_;
+  TestFullscreenController controller_;
   TestFullscreenMediator mediator_;
   TestFullscreenControllerObserver observer_;
 };
diff --git a/ios/chrome/browser/ui/fullscreen/test/BUILD.gn b/ios/chrome/browser/ui/fullscreen/test/BUILD.gn
index 1b7fa455..ccc90f69 100644
--- a/ios/chrome/browser/ui/fullscreen/test/BUILD.gn
+++ b/ios/chrome/browser/ui/fullscreen/test/BUILD.gn
@@ -20,6 +20,8 @@
   configs += [ "//build/config/compiler:enable_arc" ]
 
   deps = [
+    "//base",
+    "//ios/chrome/browser/ui/broadcaster",
     "//ios/chrome/browser/ui/fullscreen",
     "//ios/chrome/browser/ui/fullscreen:internal",
     "//ios/chrome/browser/ui/fullscreen:ui",
diff --git a/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.h b/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.h
index 8c97db93..f817f32 100644
--- a/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.h
+++ b/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.h
@@ -7,10 +7,14 @@
 
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_controller.h"
 
+#include "base/observer_list.h"
+
 class FullscreenModel;
 
-// Test version of FullscreenController that enables and disabled the
-// FullscreenModel passed on initialization.
+// Test version of FullscreenController with limited functionality:
+// - Enables/disables a FullscreenModel.
+// - Exposes a broadcaster.
+// - Supports FullscreenControllerObserver::FullscreenControllerWillShutDown().
 class TestFullscreenController : public FullscreenController {
  public:
   explicit TestFullscreenController(FullscreenModel* model);
@@ -27,9 +31,16 @@
   CGFloat GetProgress() const override;
   void ResetModel() override;
 
+  // KeyedService:
+  void Shutdown() override;
+
  private:
   // The model.
-  FullscreenModel* model_;
+  FullscreenModel* model_ = nullptr;
+  // The broadcaster.
+  ChromeBroadcaster* broadcaster_ = nil;
+  // The observers.
+  base::ObserverList<FullscreenControllerObserver>::Unchecked observers_;
 };
 
 #endif  // IOS_CHROME_BROWSER_UI_FULLSCREEN_TEST_TEST_FULLSCREEN_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.mm b/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.mm
index 3974fb2e..13b290e5 100644
--- a/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.mm
+++ b/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.h"
 
+#import "ios/chrome/browser/ui/broadcaster/chrome_broadcaster.h"
+#import "ios/chrome/browser/ui/fullscreen/fullscreen_controller_observer.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_model.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -11,38 +13,53 @@
 #endif
 
 TestFullscreenController::TestFullscreenController(FullscreenModel* model)
-    : FullscreenController(), model_(model) {}
+    : FullscreenController(),
+      model_(model),
+      broadcaster_([[ChromeBroadcaster alloc] init]) {}
 
 TestFullscreenController::~TestFullscreenController() = default;
 
 ChromeBroadcaster* TestFullscreenController::broadcaster() {
-  return nil;
+  return broadcaster_;
 }
 
 void TestFullscreenController::SetWebStateList(WebStateList* web_state_list) {}
 
 void TestFullscreenController::AddObserver(
-    FullscreenControllerObserver* observer) {}
+    FullscreenControllerObserver* observer) {
+  observers_.AddObserver(observer);
+}
 
 void TestFullscreenController::RemoveObserver(
-    FullscreenControllerObserver* observer) {}
+    FullscreenControllerObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
 
 bool TestFullscreenController::IsEnabled() const {
-  return model_->enabled();
+  return model_ && model_->enabled();
 }
 
 void TestFullscreenController::IncrementDisabledCounter() {
-  model_->IncrementDisabledCounter();
+  if (model_)
+    model_->IncrementDisabledCounter();
 }
 
 void TestFullscreenController::DecrementDisabledCounter() {
-  model_->DecrementDisabledCounter();
+  if (model_)
+    model_->DecrementDisabledCounter();
 }
 
 CGFloat TestFullscreenController::GetProgress() const {
-  return model_->progress();
+  return model_ ? model_->progress() : 0.0;
 }
 
 void TestFullscreenController::ResetModel() {
-  model_->ResetForNavigation();
+  if (model_)
+    model_->ResetForNavigation();
+}
+
+void TestFullscreenController::Shutdown() {
+  for (auto& observer : observers_) {
+    observer.FullscreenControllerWillShutDown(this);
+  }
 }
diff --git a/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller_observer.h b/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller_observer.h
index b5f30af..07209a7 100644
--- a/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller_observer.h
+++ b/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller_observer.h
@@ -13,6 +13,7 @@
   CGFloat progress() const { return progress_; }
   bool enabled() const { return enabled_; }
   FullscreenAnimator* animator() const { return animator_; }
+  bool is_shut_down() const { return is_shut_down_; }
 
  private:
   // FullscreenControllerObserver:
@@ -26,10 +27,13 @@
                                      FullscreenAnimator* animator) override;
   void FullscreenModelWasReset(FullscreenController* controller,
                                FullscreenAnimator* aniamtor) override;
+  void FullscreenControllerWillShutDown(
+      FullscreenController* controller) override;
 
   CGFloat progress_ = 0.0;
   bool enabled_ = true;
   __weak FullscreenAnimator* animator_ = nil;
+  bool is_shut_down_ = false;
 };
 
 #endif  // IOS_CHROME_BROWSER_UI_FULLSCREEN_TEST_TEST_FULLSCREEN_CONTROLLER_OBSERVER_H_
diff --git a/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller_observer.mm b/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller_observer.mm
index 50f13fb..fd228649 100644
--- a/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller_observer.mm
+++ b/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller_observer.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller_observer.h"
 
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_animator.h"
+#import "ios/chrome/browser/ui/fullscreen/fullscreen_controller.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -39,3 +40,9 @@
     FullscreenAnimator* animator) {
   animator_ = animator;
 }
+
+void TestFullscreenControllerObserver::FullscreenControllerWillShutDown(
+    FullscreenController* controller) {
+  is_shut_down_ = true;
+  controller->RemoveObserver(this);
+}
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
index ad9f559..aa23a8d 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -63,6 +63,8 @@
   // Observer that updates |viewController| for fullscreen events.
   std::unique_ptr<FullscreenControllerObserver> _fullscreenObserver;
 }
+// Whether the coordinator is started.
+@property(nonatomic, assign, getter=isStarted) BOOL started;
 // Coordinator for the omnibox popup.
 @property(nonatomic, strong) OmniboxPopupCoordinator* omniboxPopupCoordinator;
 // Coordinator for the omnibox.
@@ -75,6 +77,7 @@
 @implementation LocationBarCoordinator
 @synthesize commandDispatcher = _commandDispatcher;
 @synthesize viewController = _viewController;
+@synthesize started = _started;
 @synthesize mediator = _mediator;
 @synthesize browserState = _browserState;
 @synthesize dispatcher = _dispatcher;
@@ -94,6 +97,9 @@
 - (void)start {
   DCHECK(self.commandDispatcher);
 
+  if (self.started)
+    return;
+
   [self.commandDispatcher startDispatchingToTarget:self
                                        forProtocol:@protocol(OmniboxFocuser)];
   [self.commandDispatcher
@@ -146,9 +152,13 @@
   FullscreenControllerFactory::GetInstance()
       ->GetForBrowserState(self.browserState)
       ->AddObserver(_fullscreenObserver.get());
+
+  self.started = YES;
 }
 
 - (void)stop {
+  if (!self.started)
+    return;
   [self.commandDispatcher stopDispatchingToTarget:self];
   // The popup has to be destroyed before the location bar.
   [self.omniboxPopupCoordinator stop];
@@ -158,6 +168,11 @@
   self.viewController = nil;
   [self.mediator disconnect];
   self.mediator = nil;
+
+  FullscreenControllerFactory::GetInstance()
+      ->GetForBrowserState(self.browserState)
+      ->RemoveObserver(_fullscreenObserver.get());
+  self.started = NO;
 }
 
 - (BOOL)omniboxPopupHasAutocompleteResults {
diff --git a/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_egtest.mm b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_egtest.mm
index c811c13a..60f8330 100644
--- a/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_egtest.mm
+++ b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_egtest.mm
@@ -200,10 +200,7 @@
   NSString* otherDevicesLabel =
       l10n_util::GetNSString(IDS_IOS_RECENT_TABS_OTHER_DEVICES);
   id<GREYMatcher> otherDevicesMatcher =
-      IsUIRefreshPhase1Enabled()
-          ? grey_allOf(grey_accessibilityLabel(otherDevicesLabel),
-                       grey_sufficientlyVisible(), nil)
-          : chrome_test_util::ButtonWithAccessibilityLabel(otherDevicesLabel);
+      chrome_test_util::ButtonWithAccessibilityLabel(otherDevicesLabel);
   [[EarlGrey selectElementWithMatcher:otherDevicesMatcher]
       performAction:grey_tap()];
   [SigninEarlGreyUI checkSigninPromoNotVisible];
diff --git a/ios/chrome/browser/ui/table_view/cells/BUILD.gn b/ios/chrome/browser/ui/table_view/cells/BUILD.gn
index cb18582..53b1a30 100644
--- a/ios/chrome/browser/ui/table_view/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/table_view/cells/BUILD.gn
@@ -37,6 +37,7 @@
     "resources:table_view_cell_favicon_background",
     "//base",
     "//base:i18n",
+    "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui:ui_util",
     "//ios/chrome/browser/ui/authentication:authentication_ui",
     "//ios/chrome/browser/ui/colors:colors",
@@ -47,6 +48,7 @@
     "//ios/chrome/common/favicon",
     "//ios/chrome/common/ui_util",
     "//ios/third_party/material_components_ios:material_components_ios",
+    "//ui/base",
     "//url:url",
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm
index 83c82aa..770e55d 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm
@@ -9,6 +9,8 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ui/base/l10n/l10n_util_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -228,6 +230,15 @@
     self.disclosureDirection = direction;
     CGFloat angle = direction == DisclosureDirectionDown ? kRotationNinetyCW
                                                          : kRotationNinetyCCW;
+
+    // Update the accessibility hint to match the new direction.
+    self.accessibilityHint =
+        direction == DisclosureDirectionDown
+            ? l10n_util::GetNSString(
+                  IDS_IOS_RECENT_TABS_DISCLOSURE_VIEW_EXPANDED_HINT)
+            : l10n_util::GetNSString(
+                  IDS_IOS_RECENT_TABS_DISCLOSURE_VIEW_COLLAPSED_HINT);
+
     if (animate) {
       __weak TableViewDisclosureHeaderFooterView* weakSelf = self;
       [self.cellAnimator addAnimations:^{
@@ -244,6 +255,10 @@
 #pragma mark - Accessibility
 
 - (NSString*)accessibilityLabel {
+  // If no subtitleLabel text has been set only use the titleLabel text.
+  if (![self.subtitleLabel.text length])
+    return self.titleLabel.text;
+
   return [NSString stringWithFormat:@"%@, %@", self.titleLabel.text,
                                     self.subtitleLabel.text];
 }
diff --git a/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_coordinator.mm
index a35817b5..59a9389f 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_coordinator.mm
+++ b/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_coordinator.mm
@@ -42,6 +42,8 @@
   std::unique_ptr<FullscreenControllerObserver> _fullscreenObserver;
 }
 
+// Whether the coordinator is started.
+@property(nonatomic, assign, getter=isStarted) BOOL started;
 // Redefined as PrimaryToolbarViewController.
 @property(nonatomic, strong) PrimaryToolbarViewController* viewController;
 // The coordinator for the location bar in the toolbar.
@@ -55,6 +57,7 @@
 @implementation PrimaryToolbarCoordinator
 
 @dynamic viewController;
+@synthesize started = _started;
 @synthesize commandDispatcher = _commandDispatcher;
 @synthesize delegate = _delegate;
 @synthesize locationBarCoordinator = _locationBarCoordinator;
@@ -65,6 +68,8 @@
 
 - (void)start {
   DCHECK(self.commandDispatcher);
+  if (self.started)
+    return;
 
   [self.commandDispatcher startDispatchingToTarget:self
                                        forProtocol:@protocol(FakeboxFocuser)];
@@ -91,19 +96,27 @@
         [self.locationBarCoordinator editViewAnimatee];
   }
 
-  [super start];
-
   _fullscreenObserver =
       std::make_unique<FullscreenUIUpdater>(self.viewController);
   FullscreenControllerFactory::GetInstance()
       ->GetForBrowserState(self.browserState)
       ->AddObserver(_fullscreenObserver.get());
+
+  [super start];
+  self.started = YES;
 }
 
 - (void)stop {
+  if (!self.started)
+    return;
   [super stop];
   [self.commandDispatcher stopDispatchingToTarget:self];
   [self.locationBarCoordinator stop];
+  FullscreenControllerFactory::GetInstance()
+      ->GetForBrowserState(self.browserState)
+      ->RemoveObserver(_fullscreenObserver.get());
+  _fullscreenObserver = nullptr;
+  self.started = NO;
 }
 
 #pragma mark - PrimaryToolbarCoordinator
diff --git a/ipc/ipc_message_start.h b/ipc/ipc_message_start.h
index 9c829d3cf..4367019 100644
--- a/ipc/ipc_message_start.h
+++ b/ipc/ipc_message_start.h
@@ -49,7 +49,6 @@
   TtsMsgStart,
   NaClHostMsgStart,
   EncryptedMediaMsgStart,
-  ServiceWorkerMsgStart,
   CastMsgStart,
   ChromeExtensionMsgStart,
   GinJavaBridgeMsgStart,
diff --git a/mash/BUILD.gn b/mash/BUILD.gn
index 3d6f3f4b..24c788f8 100644
--- a/mash/BUILD.gn
+++ b/mash/BUILD.gn
@@ -24,10 +24,7 @@
 
   # Build on platforms that support ash.
   if (is_chromeos) {
-    deps += [
-      "//ash/components/autoclick:autoclick_app",
-      "//ash/components/quick_launch:quick_launch_app",
-    ]
+    deps += [ "//ash/components/quick_launch:quick_launch_app" ]
   }
 }
 
diff --git a/mash/DEPS b/mash/DEPS
index 1da21f86..e0db878 100644
--- a/mash/DEPS
+++ b/mash/DEPS
@@ -8,6 +8,7 @@
   "+services/service_manager",
   "+services/ui/common",
   "+services/ui/public",
+  "+services/ws/public",
   "+third_party/skia/include",
   "+ui",
 ]
diff --git a/mash/example/views_examples/BUILD.gn b/mash/example/views_examples/BUILD.gn
index f14452e..9ad44d2 100644
--- a/mash/example/views_examples/BUILD.gn
+++ b/mash/example/views_examples/BUILD.gn
@@ -20,7 +20,7 @@
     "//mash/public/mojom",
     "//mojo/public/cpp/bindings",
     "//services/service_manager/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//ui/gfx",
     "//ui/gfx/geometry",
diff --git a/mash/example/window_type_launcher/BUILD.gn b/mash/example/window_type_launcher/BUILD.gn
index 07f39b9f..d04104a6 100644
--- a/mash/example/window_type_launcher/BUILD.gn
+++ b/mash/example/window_type_launcher/BUILD.gn
@@ -23,7 +23,7 @@
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/mojom",
     "//services/ui/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//ui/aura",
     "//ui/gfx",
diff --git a/mash/example/window_type_launcher/window_type_launcher.cc b/mash/example/window_type_launcher/window_type_launcher.cc
index 50ba6234..547b9efe 100644
--- a/mash/example/window_type_launcher/window_type_launcher.cc
+++ b/mash/example/window_type_launcher/window_type_launcher.cc
@@ -18,7 +18,7 @@
 #include "services/service_manager/public/cpp/service_context.h"
 #include "services/service_manager/public/cpp/service_runner.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/compositor/layer.h"
diff --git a/media/audio/audio_sync_reader.cc b/media/audio/audio_sync_reader.cc
index 6d297b36..bba78e8 100644
--- a/media/audio/audio_sync_reader.cc
+++ b/media/audio/audio_sync_reader.cc
@@ -70,7 +70,7 @@
   DCHECK_EQ(base::checked_cast<uint32_t>(shared_memory_mapping_.size()),
             ComputeAudioOutputBufferSize(params));
   AudioOutputBuffer* buffer =
-      reinterpret_cast<AudioOutputBuffer*>(shared_memory_mapping_.memory());
+      static_cast<AudioOutputBuffer*>(shared_memory_mapping_.memory());
   output_bus_ = AudioBus::WrapMemory(params, buffer->audio);
   output_bus_->Zero();
   output_bus_->set_is_bitstream_format(params.IsBitstreamFormat());
@@ -157,7 +157,7 @@
   // bytes might lead to being descheduled. The reading side will zero
   // them when consumed.
   AudioOutputBuffer* buffer =
-      reinterpret_cast<AudioOutputBuffer*>(shared_memory_mapping_.memory());
+      static_cast<AudioOutputBuffer*>(shared_memory_mapping_.memory());
   // Increase the number of skipped frames stored in shared memory.
   buffer->params.frames_skipped += prior_frames_skipped;
   buffer->params.delay_us = delay.InMicroseconds();
@@ -223,7 +223,7 @@
   if (output_bus_->is_bitstream_format()) {
     // For bitstream formats, we need the real data size and PCM frame count.
     AudioOutputBuffer* buffer =
-        reinterpret_cast<AudioOutputBuffer*>(shared_memory_mapping_.memory());
+        static_cast<AudioOutputBuffer*>(shared_memory_mapping_.memory());
     uint32_t data_size = buffer->params.bitstream_data_size;
     uint32_t bitstream_frames = buffer->params.bitstream_frames;
     // |bitstream_frames| is cast to int below, so it must fit.
diff --git a/media/audio/audio_sync_reader_unittest.cc b/media/audio/audio_sync_reader_unittest.cc
index 7735a5d..5b901776 100644
--- a/media/audio/audio_sync_reader_unittest.cc
+++ b/media/audio/audio_sync_reader_unittest.cc
@@ -63,8 +63,7 @@
       base::BindRepeating(&NoLog), params, socket.get());
   const base::WritableSharedMemoryMapping shmem =
       reader->TakeSharedMemoryRegion().Map();
-  AudioOutputBuffer* buffer =
-      reinterpret_cast<AudioOutputBuffer*>(shmem.memory());
+  AudioOutputBuffer* buffer = static_cast<AudioOutputBuffer*>(shmem.memory());
   reader->RequestMoreData(base::TimeDelta(), base::TimeTicks(), 0);
 
   uint32_t signal;
diff --git a/media/audio/fuchsia/audio_output_stream_fuchsia.cc b/media/audio/fuchsia/audio_output_stream_fuchsia.cc
index 03adca55e..63d7cd2 100644
--- a/media/audio/fuchsia/audio_output_stream_fuchsia.cc
+++ b/media/audio/fuchsia/audio_output_stream_fuchsia.cc
@@ -192,9 +192,8 @@
 
   audio_bus_->ToInterleaved<media::Float32SampleTypeTraits>(
       audio_bus_->frames(),
-      reinterpret_cast<float*>(
-          reinterpret_cast<uint8_t*>(payload_buffer_.memory()) +
-          payload_buffer_pos_));
+      reinterpret_cast<float*>(static_cast<uint8_t*>(payload_buffer_.memory()) +
+                               payload_buffer_pos_));
 
   fuchsia::media::StreamPacket packet;
   packet.pts = stream_position_samples_;
diff --git a/media/base/mime_util_internal.cc b/media/base/mime_util_internal.cc
index 9ff20e6..91bfc8a 100644
--- a/media/base/mime_util_internal.cc
+++ b/media/base/mime_util_internal.cc
@@ -278,9 +278,7 @@
 
   const CodecSet mp3_codecs{MP3};
 
-  CodecSet mp4_audio_codecs;
-  mp4_audio_codecs.emplace(MP3);
-  mp4_audio_codecs.emplace(FLAC);
+  CodecSet mp4_audio_codecs{FLAC, MP3, OPUS};
 
   // Only VP9 with valid codec string vp09.xx.xx.xx.xx.xx.xx.xx is supported.
   // See ParseVp9CodecID for details.
diff --git a/media/base/user_input_monitor.cc b/media/base/user_input_monitor.cc
index 4664abd..c0b6558 100644
--- a/media/base/user_input_monitor.cc
+++ b/media/base/user_input_monitor.cc
@@ -33,8 +33,7 @@
   // No ordering constraints between Load/Store operations, a temporary
   // inconsistent value is fine.
   base::subtle::NoBarrier_Store(
-      reinterpret_cast<base::subtle::Atomic32*>(writable_mapping.memory()),
-      count);
+      static_cast<base::subtle::Atomic32*>(writable_mapping.memory()), count);
 }
 
 #ifdef DISABLE_USER_INPUT_MONITOR
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCapture.java b/media/capture/video/android/java/src/org/chromium/media/VideoCapture.java
index 7e9c9081..7f593e7 100644
--- a/media/capture/video/android/java/src/org/chromium/media/VideoCapture.java
+++ b/media/capture/video/android/java/src/org/chromium/media/VideoCapture.java
@@ -62,14 +62,22 @@
     @CalledByNative
     public abstract boolean allocate(int width, int height, int frameRate);
 
+    // Success is indicated by returning true and a callback to
+    // nativeOnStarted(), which may occur synchronously or asynchronously.
+    // Failure can be indicated by one of the following:
+    // * Returning false. In this case no callback to nativeOnStarted() is made.
+    // * Returning true, and asynchronously invoking nativeOnError. In this case
+    //   also no callback to nativeOnStarted() is made.
     @CalledByNative
-    public abstract boolean startCapture();
+    public abstract boolean startCaptureMaybeAsync();
 
+    // Blocks until it is guaranteed that no more frames are sent.
     @CalledByNative
-    public abstract boolean stopCapture();
+    public abstract boolean stopCaptureAndBlockUntilStopped();
 
+    // Replies by calling nativeOnGetPhotoCapabilitiesReply().
     @CalledByNative
-    public abstract PhotoCapabilities getPhotoCapabilities();
+    public abstract void getPhotoCapabilitiesAsync(long callbackId);
 
     /**
      * @param zoom Zoom level, should be ignored if 0.
@@ -95,8 +103,9 @@
             boolean hasRedEyeReduction, boolean redEyeReduction, int fillLightMode,
             boolean hasTorch, boolean torch, double colorTemperature);
 
+    // Replies by calling nativeOnPhotoTaken().
     @CalledByNative
-    public abstract boolean takePhoto(final long callbackId);
+    public abstract void takePhotoAsync(long callbackId);
 
     @CalledByNative
     public abstract void deallocate();
@@ -164,6 +173,12 @@
         return orientation;
     }
 
+    // {@link nativeOnPhotoTaken()} needs to be called back if there's any
+    // problem after {@link takePhotoAsync()} has returned true.
+    protected void notifyTakePhotoError(long callbackId) {
+        nativeOnPhotoTaken(mNativeVideoCaptureDeviceAndroid, callbackId, null);
+    }
+
     /**
      * Finds the framerate range matching |targetFramerate|. Tries to find a range with as low of a
      * minimum value as possible to allow the camera adjust based on the lighting conditions.
@@ -231,10 +246,17 @@
     public native void nativeOnError(
             long nativeVideoCaptureDeviceAndroid, int androidVideoCaptureError, String message);
 
-    // Method for VideoCapture implementations to send Photos back to.
+    public native void nativeOnGetPhotoCapabilitiesReply(
+            long nativeVideoCaptureDeviceAndroid, long callbackId, PhotoCapabilities result);
+
+    // Callback for calls to takePhoto(). This can indicate both success and
+    // failure. Failure is indicated by |data| being null.
     public native void nativeOnPhotoTaken(
             long nativeVideoCaptureDeviceAndroid, long callbackId, byte[] data);
 
     // Method for VideoCapture implementations to report device started event.
     public native void nativeOnStarted(long nativeVideoCaptureDeviceAndroid);
+
+    public native void nativeDCheckCurrentlyOnIncomingTaskRunner(
+            long nativeVideoCaptureDeviceAndroid);
 }
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java
index db2be319..9433eba8 100644
--- a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java
+++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java
@@ -148,8 +148,7 @@
 
             synchronized (mPhotoTakenCallbackLock) {
                 if (mPhotoTakenCallbackId == 0) return;
-                nativeOnPhotoTaken(
-                        mNativeVideoCaptureDeviceAndroid, mPhotoTakenCallbackId, new byte[0]);
+                notifyTakePhotoError(mPhotoTakenCallbackId);
                 mPhotoTakenCallbackId = 0;
             }
         }
@@ -427,9 +426,9 @@
     }
 
     @Override
-    public boolean startCapture() {
+    public boolean startCaptureMaybeAsync() {
         if (mCamera == null) {
-            Log.e(TAG, "startCapture: mCamera is null");
+            Log.e(TAG, "startCaptureAsync: mCamera is null");
             return false;
         }
 
@@ -446,7 +445,7 @@
         try {
             mCamera.startPreview();
         } catch (RuntimeException ex) {
-            Log.e(TAG, "startCapture: Camera.startPreview: " + ex);
+            Log.e(TAG, "startCaptureAsync: Camera.startPreview: " + ex);
             return false;
         }
 
@@ -461,9 +460,9 @@
     }
 
     @Override
-    public boolean stopCapture() {
+    public boolean stopCaptureAndBlockUntilStopped() {
         if (mCamera == null) {
-            Log.e(TAG, "stopCapture: mCamera is null");
+            Log.e(TAG, "stopCaptureAndBlockUntilStopped: mCamera is null");
             return true;
         }
 
@@ -483,7 +482,7 @@
     }
 
     @Override
-    public PhotoCapabilities getPhotoCapabilities() {
+    public void getPhotoCapabilitiesAsync(long callbackId) {
         final android.hardware.Camera.Parameters parameters = getCameraParameters(mCamera);
         PhotoCapabilities.Builder builder = new PhotoCapabilities.Builder();
         Log.i(TAG, " CAM params: %s", parameters.flatten());
@@ -636,7 +635,8 @@
             builder.setFillLightModes(integerArrayListToArray(modes));
         }
 
-        return builder.build();
+        nativeOnGetPhotoCapabilitiesReply(
+                mNativeVideoCaptureDeviceAndroid, callbackId, builder.build());
     }
 
     @Override
@@ -778,15 +778,19 @@
     }
 
     @Override
-    public boolean takePhoto(final long callbackId) {
+    public void takePhotoAsync(final long callbackId) {
         if (mCamera == null || !mIsRunning) {
-            Log.e(TAG, "takePhoto: mCamera is null or is not running");
-            return false;
+            Log.e(TAG, "takePhotoAsync: mCamera is null or is not running");
+            notifyTakePhotoError(callbackId);
+            return;
         }
 
         // Only one picture can be taken at once.
         synchronized (mPhotoTakenCallbackLock) {
-            if (mPhotoTakenCallbackId != 0) return false;
+            if (mPhotoTakenCallbackId != 0) {
+                notifyTakePhotoError(callbackId);
+                return;
+            }
             mPhotoTakenCallbackId = callbackId;
         }
         mPreviewParameters = getCameraParameters(mCamera);
@@ -819,18 +823,18 @@
             mCamera.setParameters(photoParameters);
         } catch (RuntimeException ex) {
             Log.e(TAG, "setParameters " + ex);
-            return false;
+            notifyTakePhotoError(callbackId);
+            return;
         }
 
         mCamera.takePicture(null, null, null, new CrPictureCallback());
-        return true;
     }
 
     @Override
     public void deallocate() {
         if (mCamera == null) return;
 
-        stopCapture();
+        stopCaptureAndBlockUntilStopped();
         try {
             mCamera.setPreviewTexture(null);
             if (mGlTextures != null) GLES20.glDeleteTextures(1, mGlTextures, 0);
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
index dbe5761..f1066130 100644
--- a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
+++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
@@ -22,6 +22,7 @@
 import android.media.Image;
 import android.media.ImageReader;
 import android.os.Build;
+import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -32,7 +33,6 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
-import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.JNINamespace;
 
 import java.nio.ByteBuffer;
@@ -54,19 +54,21 @@
     private class CrStateListener extends CameraDevice.StateCallback {
         @Override
         public void onOpened(CameraDevice cameraDevice) {
-            mCameraDevice = cameraDevice;
-            changeCameraStateAndNotify(CameraState.CONFIGURING);
-            if (createPreviewObjectsAndStartPreview()) return;
+            assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
 
-            changeCameraStateAndNotify(CameraState.STOPPED);
-            nativeOnError(mNativeVideoCaptureDeviceAndroid,
-                    AndroidVideoCaptureError.ANDROID_API_2_ERROR_CONFIGURING_CAMERA,
-                    "Error configuring camera");
+            Log.e(TAG, "CameraDevice.StateCallback onOpened");
+            mCameraDevice = cameraDevice;
+            mWaitForDeviceClosedConditionVariable.close();
+            changeCameraStateAndNotify(CameraState.CONFIGURING);
+            createPreviewObjectsAndStartPreviewOrFailWith(
+                    AndroidVideoCaptureError.ANDROID_API_2_ERROR_CONFIGURING_CAMERA);
         }
 
         @Override
         public void onDisconnected(CameraDevice cameraDevice) {
+            assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
             Log.e(TAG, "cameraDevice was closed unexpectedly");
+
             cameraDevice.close();
             mCameraDevice = null;
             changeCameraStateAndNotify(CameraState.STOPPED);
@@ -74,7 +76,9 @@
 
         @Override
         public void onError(CameraDevice cameraDevice, int error) {
+            assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
             Log.e(TAG, "cameraDevice encountered an error");
+
             cameraDevice.close();
             mCameraDevice = null;
             changeCameraStateAndNotify(CameraState.STOPPED);
@@ -82,6 +86,20 @@
                     AndroidVideoCaptureError.ANDROID_API_2_CAMERA_DEVICE_ERROR_RECEIVED,
                     "Camera device error " + Integer.toString(error));
         }
+
+        @Override
+        public void onClosed(CameraDevice camera) {
+            Log.d(TAG, "cameraDevice closed");
+            // If we called mCameraDevice.close() while mPreviewSession was running,
+            // mPreviewSession will get closed, but the corresponding CrPreviewSessionListener
+            // will not receive a callback to onClosed(). Therefore we have to clean up
+            // the reference to mPreviewSession here.
+            if (mPreviewSession != null) {
+                mPreviewSession = null;
+            }
+
+            mWaitForDeviceClosedConditionVariable.open();
+        }
     };
 
     // Inner class to extend a Capture Session state change listener.
@@ -93,6 +111,8 @@
 
         @Override
         public void onConfigured(CameraCaptureSession cameraCaptureSession) {
+            assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
+
             Log.d(TAG, "CrPreviewSessionListener.onConfigured");
             mPreviewSession = cameraCaptureSession;
             try {
@@ -119,20 +139,37 @@
                 Log.e(TAG, "setRepeatingRequest: ", ex);
                 return;
             }
-            // Now wait for trigger on CrPreviewReaderListener.onImageAvailable();
-            nativeOnStarted(mNativeVideoCaptureDeviceAndroid);
+
             changeCameraStateAndNotify(CameraState.STARTED);
+            nativeOnStarted(mNativeVideoCaptureDeviceAndroid);
+
+            // Frames will be arriving at CrPreviewReaderListener.onImageAvailable();
         }
 
         @Override
         public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
+            assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
+            Log.d(TAG, "CrPreviewSessionListener.onConfigureFailed");
+
             // TODO(mcasas): When signalling error, C++ will tear us down. Is there need for
             // cleanup?
             changeCameraStateAndNotify(CameraState.STOPPED);
+            mPreviewSession = null;
             nativeOnError(mNativeVideoCaptureDeviceAndroid,
                     AndroidVideoCaptureError.ANDROID_API_2_CAPTURE_SESSION_CONFIGURE_FAILED,
                     "Camera session configuration error");
         }
+
+        @Override
+        public void onClosed(CameraCaptureSession cameraCaptureSession) {
+            assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
+            Log.d(TAG, "CrPreviewSessionListener.onClosed");
+
+            // The preview session gets closed temporarily when a takePhoto
+            // request is being processed. A new preview session will be
+            // started after that.
+            mPreviewSession = null;
+        }
     };
 
     // Internal class implementing an ImageReader listener for Preview frames. Gets pinged when a
@@ -140,6 +177,8 @@
     private class CrPreviewReaderListener implements ImageReader.OnImageAvailableListener {
         @Override
         public void onImageAvailable(ImageReader reader) {
+            assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
+
             try (Image image = reader.acquireLatestImage()) {
                 if (image == null) return;
 
@@ -190,6 +229,8 @@
 
         @Override
         public void onConfigured(CameraCaptureSession session) {
+            assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
+
             Log.d(TAG, "CrPhotoSessionListener.onConfigured");
             try {
                 // This line triggers a single photo capture. No |listener| is registered, so we
@@ -209,6 +250,8 @@
 
         @Override
         public void onConfigureFailed(CameraCaptureSession session) {
+            assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
+
             Log.e(TAG, "failed configuring capture session");
             notifyTakePhotoError(mCallbackId);
             return;
@@ -244,6 +287,8 @@
 
         @Override
         public void onImageAvailable(ImageReader reader) {
+            assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
+
             try (Image image = reader.acquireLatestImage()) {
                 if (image == null) {
                     throw new IllegalStateException();
@@ -262,34 +307,463 @@
                 return;
             }
 
-            if (createPreviewObjectsAndStartPreview()) return;
-
-            nativeOnError(mNativeVideoCaptureDeviceAndroid,
-                    AndroidVideoCaptureError.ANDROID_API_2_ERROR_RESTARTING_PREVIEW,
-                    "Error restarting preview");
+            createPreviewObjectsAndStartPreviewOrFailWith(
+                    AndroidVideoCaptureError.ANDROID_API_2_ERROR_RESTARTING_PREVIEW);
         }
     };
 
-    // Inner Runnable to reconfigure the preview session, must be run on application context looper.
-    private final Runnable mReconfigureCaptureTask = new Runnable() {
+    private class StopCaptureTask implements Runnable {
         @Override
         public void run() {
-            ThreadUtils.assertOnUiThread();
-            assert mPreviewRequestBuilder != null : "preview request builder";
-            assert mPreviewSession != null : "preview session";
+            assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
 
-            // Reuse most of |mPreviewRequestBuilder| since it has expensive items inside that have
-            // to do with preview, e.g. the ImageReader and its associated Surface.
-            configureCommonCaptureSettings(mPreviewRequestBuilder);
+            if (mCameraDevice == null) return;
 
+            // As per Android API documentation, this will automatically abort captures
+            // pending for mPreviewSession, but it will not lead to callbacks such as
+            // onClosed() to the corresponding CrPreviewSessionListener.
+            // Different from what the Android API documentation says, pending frames
+            // may still get delivered after this call. Therefore, we have to wait for
+            // CrStateListener.onClosed() in order to have a guarantee that no more
+            // frames are delivered.
+            mCameraDevice.close();
+
+            changeCameraStateAndNotify(CameraState.STOPPED);
+            mCropRect = new Rect();
+        }
+    }
+
+    private class GetPhotoCapabilitiesTask implements Runnable {
+        private final long mCallbackId;
+        public GetPhotoCapabilitiesTask(long callbackId) {
+            mCallbackId = callbackId;
+        }
+
+        @Override
+        public void run() {
+            assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
+
+            final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(mId);
+            PhotoCapabilities.Builder builder = new PhotoCapabilities.Builder();
+
+            int minIso = 0;
+            int maxIso = 0;
+            final Range<Integer> iso_range =
+                    cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE);
+            if (iso_range != null) {
+                minIso = iso_range.getLower();
+                maxIso = iso_range.getUpper();
+            }
+            builder.setMinIso(minIso).setMaxIso(maxIso).setStepIso(1);
+            if (mPreviewRequestBuilder.get(CaptureRequest.SENSOR_SENSITIVITY) != null) {
+                builder.setCurrentIso(mPreviewRequest.get(CaptureRequest.SENSOR_SENSITIVITY));
+            }
+
+            final StreamConfigurationMap streamMap = cameraCharacteristics.get(
+                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+            final Size[] supportedSizes = streamMap.getOutputSizes(ImageFormat.JPEG);
+            int minWidth = Integer.MAX_VALUE;
+            int minHeight = Integer.MAX_VALUE;
+            int maxWidth = 0;
+            int maxHeight = 0;
+            for (Size size : supportedSizes) {
+                if (size.getWidth() < minWidth) minWidth = size.getWidth();
+                if (size.getHeight() < minHeight) minHeight = size.getHeight();
+                if (size.getWidth() > maxWidth) maxWidth = size.getWidth();
+                if (size.getHeight() > maxHeight) maxHeight = size.getHeight();
+            }
+            builder.setMinHeight(minHeight).setMaxHeight(maxHeight).setStepHeight(1);
+            builder.setMinWidth(minWidth).setMaxWidth(maxWidth).setStepWidth(1);
+            builder.setCurrentHeight(
+                    (mPhotoHeight > 0) ? mPhotoHeight : mCaptureFormat.getHeight());
+            builder.setCurrentWidth((mPhotoWidth > 0) ? mPhotoWidth : mCaptureFormat.getWidth());
+
+            final float currentZoom =
+                    cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)
+                            .width()
+                    / (float) mPreviewRequest.get(CaptureRequest.SCALER_CROP_REGION).width();
+            // There is no min-zoom per se, so clamp it to always 1.
+            builder.setMinZoom(1.0).setMaxZoom(mMaxZoom);
+            builder.setCurrentZoom(currentZoom).setStepZoom(0.1);
+
+            // Classify the Focus capabilities. In CONTINUOUS and SINGLE_SHOT, we can call
+            // autoFocus(AutoFocusCallback) to configure region(s) to focus onto.
+            final int[] jniFocusModes =
+                    cameraCharacteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
+            ArrayList<Integer> focusModes = new ArrayList<Integer>(3);
+            for (int mode : jniFocusModes) {
+                if (mode == CameraMetadata.CONTROL_AF_MODE_OFF) {
+                    focusModes.add(Integer.valueOf(AndroidMeteringMode.FIXED));
+                } else if (mode == CameraMetadata.CONTROL_AF_MODE_AUTO
+                        || mode == CameraMetadata.CONTROL_AF_MODE_MACRO) {
+                    // CONTROL_AF_MODE_{AUTO,MACRO} do not imply continuously focusing.
+                    if (!focusModes.contains(Integer.valueOf(AndroidMeteringMode.SINGLE_SHOT))) {
+                        focusModes.add(Integer.valueOf(AndroidMeteringMode.SINGLE_SHOT));
+                    }
+                } else if (mode == CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_VIDEO
+                        || mode == CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE
+                        || mode == CameraMetadata.CONTROL_AF_MODE_EDOF) {
+                    if (!focusModes.contains(Integer.valueOf(AndroidMeteringMode.CONTINUOUS))) {
+                        focusModes.add(Integer.valueOf(AndroidMeteringMode.CONTINUOUS));
+                    }
+                }
+            }
+            builder.setFocusModes(integerArrayListToArray(focusModes));
+
+            final int focusMode = mPreviewRequest.get(CaptureRequest.CONTROL_AF_MODE);
+            int jniFocusMode = AndroidMeteringMode.NONE;
+            if (focusMode == CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_VIDEO
+                    || focusMode == CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE) {
+                jniFocusMode = AndroidMeteringMode.CONTINUOUS;
+            } else if (focusMode == CameraMetadata.CONTROL_AF_MODE_AUTO
+                    || focusMode == CameraMetadata.CONTROL_AF_MODE_MACRO) {
+                jniFocusMode = AndroidMeteringMode.SINGLE_SHOT;
+            } else if (focusMode == CameraMetadata.CONTROL_AF_MODE_OFF) {
+                jniFocusMode = AndroidMeteringMode.FIXED;
+            } else {
+                assert jniFocusMode == CameraMetadata.CONTROL_AF_MODE_EDOF;
+            }
+            builder.setFocusMode(jniFocusMode);
+
+            // Auto Exposure is the usual capability and state, unless AE is not available at all,
+            // which is signalled by an empty CONTROL_AE_AVAILABLE_MODES list. Exposure Compensation
+            // can also support or be locked, this is equivalent to AndroidMeteringMode.FIXED.
+            final int[] jniExposureModes =
+                    cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
+            ArrayList<Integer> exposureModes = new ArrayList<Integer>(1);
+            for (int mode : jniExposureModes) {
+                if (mode == CameraMetadata.CONTROL_AE_MODE_ON
+                        || mode == CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH
+                        || mode == CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH
+                        || mode == CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) {
+                    exposureModes.add(Integer.valueOf(AndroidMeteringMode.CONTINUOUS));
+                    break;
+                }
+            }
             try {
-                mPreviewSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, null);
-            } catch (CameraAccessException | SecurityException | IllegalStateException
-                    | IllegalArgumentException ex) {
-                Log.e(TAG, "setRepeatingRequest: ", ex);
+                if (cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE)) {
+                    exposureModes.add(Integer.valueOf(AndroidMeteringMode.FIXED));
+                }
+            } catch (NoSuchFieldError e) {
+                // Ignore this exception, it means CONTROL_AE_LOCK_AVAILABLE is not known.
+            }
+            builder.setExposureModes(integerArrayListToArray(exposureModes));
+
+            int jniExposureMode = AndroidMeteringMode.CONTINUOUS;
+            if (mPreviewRequest.get(CaptureRequest.CONTROL_AE_MODE)
+                    == CameraMetadata.CONTROL_AE_MODE_OFF) {
+                jniExposureMode = AndroidMeteringMode.NONE;
+            }
+            if (mPreviewRequest.get(CaptureRequest.CONTROL_AE_LOCK)) {
+                jniExposureMode = AndroidMeteringMode.FIXED;
+            }
+            builder.setExposureMode(jniExposureMode);
+
+            final float step =
+                    cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP)
+                            .floatValue();
+            builder.setStepExposureCompensation(step);
+            final Range<Integer> exposureCompensationRange =
+                    cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE);
+            builder.setMinExposureCompensation(exposureCompensationRange.getLower() * step);
+            builder.setMaxExposureCompensation(exposureCompensationRange.getUpper() * step);
+            builder.setCurrentExposureCompensation(
+                    mPreviewRequest.get(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION) * step);
+
+            final int[] jniWhiteBalanceMode =
+                    cameraCharacteristics.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES);
+            ArrayList<Integer> whiteBalanceModes = new ArrayList<Integer>(1);
+            for (int mode : jniWhiteBalanceMode) {
+                if (mode == CameraMetadata.CONTROL_AWB_MODE_AUTO) {
+                    whiteBalanceModes.add(Integer.valueOf(AndroidMeteringMode.CONTINUOUS));
+                    break;
+                }
+            }
+            try {
+                if (cameraCharacteristics.get(CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE)) {
+                    whiteBalanceModes.add(Integer.valueOf(AndroidMeteringMode.FIXED));
+                }
+            } catch (NoSuchFieldError e) {
+                // Ignore this exception, it means CONTROL_AWB_LOCK_AVAILABLE is not known.
+            }
+            builder.setWhiteBalanceModes(integerArrayListToArray(whiteBalanceModes));
+
+            final int whiteBalanceMode = mPreviewRequest.get(CaptureRequest.CONTROL_AWB_MODE);
+            if (whiteBalanceMode == CameraMetadata.CONTROL_AWB_MODE_OFF) {
+                builder.setWhiteBalanceMode(AndroidMeteringMode.NONE);
+            } else if (whiteBalanceMode == CameraMetadata.CONTROL_AWB_MODE_AUTO) {
+                builder.setWhiteBalanceMode(AndroidMeteringMode.CONTINUOUS);
+            } else {
+                builder.setWhiteBalanceMode(AndroidMeteringMode.FIXED);
+            }
+            builder.setMinColorTemperature(COLOR_TEMPERATURES_MAP.keyAt(0));
+            builder.setMaxColorTemperature(
+                    COLOR_TEMPERATURES_MAP.keyAt(COLOR_TEMPERATURES_MAP.size() - 1));
+            final int index = COLOR_TEMPERATURES_MAP.indexOfValue(whiteBalanceMode);
+            if (index >= 0) {
+                builder.setCurrentColorTemperature(COLOR_TEMPERATURES_MAP.keyAt(index));
+            }
+            builder.setStepColorTemperature(50);
+
+            if (!cameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)) {
+                builder.setSupportsTorch(false);
+                builder.setRedEyeReduction(false);
+            } else {
+                // There's no way to query if torch and/or red eye reduction modes are available
+                // using Camera2 API but since there's a Flash unit, we assume so.
+                builder.setSupportsTorch(true);
+                builder.setTorch(mPreviewRequest.get(CaptureRequest.FLASH_MODE)
+                        == CameraMetadata.FLASH_MODE_TORCH);
+
+                builder.setRedEyeReduction(true);
+
+                final int[] flashModes =
+                        cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
+                ArrayList<Integer> modes = new ArrayList<Integer>(0);
+                for (int flashMode : flashModes) {
+                    if (flashMode == CameraMetadata.FLASH_MODE_OFF) {
+                        modes.add(Integer.valueOf(AndroidFillLightMode.OFF));
+                    } else if (flashMode == CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH) {
+                        modes.add(Integer.valueOf(AndroidFillLightMode.AUTO));
+                    } else if (flashMode == CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH) {
+                        modes.add(Integer.valueOf(AndroidFillLightMode.FLASH));
+                    }
+                }
+                builder.setFillLightModes(integerArrayListToArray(modes));
+            }
+
+            nativeOnGetPhotoCapabilitiesReply(
+                    mNativeVideoCaptureDeviceAndroid, mCallbackId, builder.build());
+        }
+    }
+
+    private class PhotoOptions {
+        public final double zoom;
+        public final int focusMode;
+        public final int exposureMode;
+        public final double width;
+        public final double height;
+        public final float[] pointsOfInterest2D;
+        public final boolean hasExposureCompensation;
+        public final double exposureCompensation;
+        public final int whiteBalanceMode;
+        public final double iso;
+        public final boolean hasRedEyeReduction;
+        public final boolean redEyeReduction;
+        public final int fillLightMode;
+        public final boolean hasTorch;
+        public final boolean torch;
+        public final double colorTemperature;
+
+        public PhotoOptions(double zoom, int focusMode, int exposureMode, double width,
+                double height, float[] pointsOfInterest2D, boolean hasExposureCompensation,
+                double exposureCompensation, int whiteBalanceMode, double iso,
+                boolean hasRedEyeReduction, boolean redEyeReduction, int fillLightMode,
+                boolean hasTorch, boolean torch, double colorTemperature) {
+            this.zoom = zoom;
+            this.focusMode = focusMode;
+            this.exposureMode = exposureMode;
+            this.width = width;
+            this.height = height;
+            this.pointsOfInterest2D = pointsOfInterest2D;
+            this.hasExposureCompensation = hasExposureCompensation;
+            this.exposureCompensation = exposureCompensation;
+            this.whiteBalanceMode = whiteBalanceMode;
+            this.iso = iso;
+            this.hasRedEyeReduction = hasRedEyeReduction;
+            this.redEyeReduction = redEyeReduction;
+            this.fillLightMode = fillLightMode;
+            this.hasTorch = hasTorch;
+            this.torch = torch;
+            this.colorTemperature = colorTemperature;
+        }
+    }
+
+    private class SetPhotoOptionsTask implements Runnable {
+        private final PhotoOptions mOptions;
+
+        public SetPhotoOptionsTask(PhotoOptions options) {
+            mOptions = options;
+        }
+
+        @Override
+        public void run() {
+            assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
+
+            final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(mId);
+            final Rect canvas =
+                    cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+
+            if (mOptions.zoom != 0) {
+                final float normalizedZoom =
+                        Math.max(1.0f, Math.min((float) mOptions.zoom, mMaxZoom));
+                final float cropFactor = (normalizedZoom - 1) / (2 * normalizedZoom);
+
+                mCropRect = new Rect(Math.round(canvas.width() * cropFactor),
+                        Math.round(canvas.height() * cropFactor),
+                        Math.round(canvas.width() * (1 - cropFactor)),
+                        Math.round(canvas.height() * (1 - cropFactor)));
+                Log.d(TAG, "zoom level %f, rectangle: %s", normalizedZoom, mCropRect.toString());
+            }
+
+            if (mOptions.focusMode != AndroidMeteringMode.NOT_SET) mFocusMode = mOptions.focusMode;
+            if (mOptions.exposureMode != AndroidMeteringMode.NOT_SET)
+                mExposureMode = mOptions.exposureMode;
+            if (mOptions.whiteBalanceMode != AndroidMeteringMode.NOT_SET)
+                mWhiteBalanceMode = mOptions.whiteBalanceMode;
+
+            if (mOptions.width > 0) mPhotoWidth = (int) Math.round(mOptions.width);
+            if (mOptions.height > 0) mPhotoHeight = (int) Math.round(mOptions.height);
+
+            // Upon new |zoom| configuration, clear up the previous |mAreaOfInterest| if any.
+            if (mAreaOfInterest != null && !mAreaOfInterest.getRect().isEmpty()
+                    && mOptions.zoom > 0) {
+                mAreaOfInterest = null;
+            }
+            // Also clear |mAreaOfInterest| if the user sets it as NONE.
+            if (mFocusMode == AndroidMeteringMode.NONE
+                    || mExposureMode == AndroidMeteringMode.NONE) {
+                mAreaOfInterest = null;
+            }
+            // Update |mAreaOfInterest| if the camera supports and there are |pointsOfInterest2D|.
+            final boolean pointsOfInterestSupported =
+                    cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF) > 0
+                    || cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE) > 0
+                    || cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB) > 0;
+            if (pointsOfInterestSupported && mOptions.pointsOfInterest2D.length > 0) {
+                assert mOptions.pointsOfInterest2D.length
+                        == 2 : "Only 1 point of interest supported";
+                assert mOptions.pointsOfInterest2D[0] <= 1.0
+                        && mOptions.pointsOfInterest2D[0] >= 0.0;
+                assert mOptions.pointsOfInterest2D[1] <= 1.0
+                        && mOptions.pointsOfInterest2D[1] >= 0.0;
+                // Calculate a Rect of 1/8 the |visibleRect| dimensions, and center it w.r.t.
+                // |canvas|.
+                final Rect visibleRect = (mCropRect.isEmpty()) ? canvas : mCropRect;
+                int centerX = Math.round(mOptions.pointsOfInterest2D[0] * visibleRect.width());
+                int centerY = Math.round(mOptions.pointsOfInterest2D[1] * visibleRect.height());
+                if (visibleRect.equals(mCropRect)) {
+                    centerX += (canvas.width() - visibleRect.width()) / 2;
+                    centerY += (canvas.height() - visibleRect.height()) / 2;
+                }
+                final int regionWidth = visibleRect.width() / 8;
+                final int regionHeight = visibleRect.height() / 8;
+
+                mAreaOfInterest = new MeteringRectangle(Math.max(0, centerX - regionWidth / 2),
+                        Math.max(0, centerY - regionHeight / 2), regionWidth, regionHeight,
+                        MeteringRectangle.METERING_WEIGHT_MAX);
+
+                Log.d(TAG, "Calculating (%.2fx%.2f) wrt to %s (canvas being %s)",
+                        mOptions.pointsOfInterest2D[0], mOptions.pointsOfInterest2D[1],
+                        visibleRect.toString(), canvas.toString());
+                Log.d(TAG, "Area of interest %s", mAreaOfInterest.toString());
+            }
+
+            if (mOptions.hasExposureCompensation) {
+                mExposureCompensation = (int) Math.round(mOptions.exposureCompensation
+                        / cameraCharacteristics
+                                  .get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP)
+                                  .floatValue());
+            }
+            if (mOptions.iso > 0) mIso = (int) Math.round(mOptions.iso);
+            if (mOptions.colorTemperature > 0)
+                mColorTemperature = (int) Math.round(mOptions.colorTemperature);
+
+            if (mOptions.hasRedEyeReduction) mRedEyeReduction = mOptions.redEyeReduction;
+            if (mOptions.fillLightMode != AndroidFillLightMode.NOT_SET)
+                mFillLightMode = mOptions.fillLightMode;
+            if (mOptions.hasTorch) mTorch = mOptions.torch;
+
+            if (mPreviewSession != null) {
+                assert mPreviewRequestBuilder != null : "preview request builder";
+
+                // Reuse most of |mPreviewRequestBuilder| since it has expensive items inside that
+                // have to do with preview, e.g. the ImageReader and its associated Surface.
+                configureCommonCaptureSettings(mPreviewRequestBuilder);
+
+                try {
+                    mPreviewSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, null);
+                } catch (CameraAccessException | SecurityException | IllegalStateException
+                        | IllegalArgumentException ex) {
+                    Log.e(TAG, "setRepeatingRequest: ", ex);
+                }
             }
         }
-    };
+    }
+
+    private class TakePhotoTask implements Runnable {
+        private final long mCallbackId;
+        public TakePhotoTask(long callbackId) {
+            mCallbackId = callbackId;
+        }
+
+        @Override
+        public void run() {
+            assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
+
+            if (mCameraDevice == null || mCameraState != CameraState.STARTED) {
+                Log.e(TAG,
+                        "TakePhoto failed because mCameraDevice == null || "
+                                + "mCameraState != CameraState.STARTED");
+                notifyTakePhotoError(mCallbackId);
+                return;
+            }
+
+            final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(mId);
+            final StreamConfigurationMap streamMap = cameraCharacteristics.get(
+                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+            final Size[] supportedSizes = streamMap.getOutputSizes(ImageFormat.JPEG);
+            final Size closestSize =
+                    findClosestSizeInArray(supportedSizes, mPhotoWidth, mPhotoHeight);
+
+            Log.d(TAG, "requested resolution: (%dx%d)", mPhotoWidth, mPhotoHeight);
+            if (closestSize != null) {
+                Log.d(TAG, " matched (%dx%d)", closestSize.getWidth(), closestSize.getHeight());
+            }
+            final ImageReader imageReader = ImageReader.newInstance(
+                    (closestSize != null) ? closestSize.getWidth() : mCaptureFormat.getWidth(),
+                    (closestSize != null) ? closestSize.getHeight() : mCaptureFormat.getHeight(),
+                    ImageFormat.JPEG, 1 /* maxImages */);
+
+            final CrPhotoReaderListener photoReaderListener =
+                    new CrPhotoReaderListener(mCallbackId);
+            imageReader.setOnImageAvailableListener(photoReaderListener, mCameraThreadHandler);
+
+            final List<Surface> surfaceList = new ArrayList<Surface>(1);
+            // TODO(mcasas): release this Surface when not needed, https://crbug.com/643884.
+            surfaceList.add(imageReader.getSurface());
+
+            CaptureRequest.Builder photoRequestBuilder = null;
+            try {
+                photoRequestBuilder =
+                        mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+            } catch (CameraAccessException ex) {
+                Log.e(TAG, "createCaptureRequest() error ", ex);
+                notifyTakePhotoError(mCallbackId);
+                return;
+            }
+            if (photoRequestBuilder == null) {
+                Log.e(TAG, "photoRequestBuilder error");
+                notifyTakePhotoError(mCallbackId);
+                return;
+            }
+            photoRequestBuilder.addTarget(imageReader.getSurface());
+            photoRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, getCameraRotation());
+
+            configureCommonCaptureSettings(photoRequestBuilder);
+
+            final CaptureRequest photoRequest = photoRequestBuilder.build();
+            final CrPhotoSessionListener sessionListener =
+                    new CrPhotoSessionListener(imageReader, photoRequest, mCallbackId);
+            try {
+                mCameraDevice.createCaptureSession(
+                        surfaceList, sessionListener, mCameraThreadHandler);
+            } catch (CameraAccessException | IllegalArgumentException | SecurityException ex) {
+                Log.e(TAG, "createCaptureSession: " + ex);
+                notifyTakePhotoError(mCallbackId);
+            }
+        }
+    }
 
     private static final double kNanosecondsPerSecond = 1000000000;
     private static final String TAG = "VideoCapture";
@@ -317,9 +791,14 @@
     private CameraCaptureSession mPreviewSession;
     private CaptureRequest mPreviewRequest;
     private CaptureRequest.Builder mPreviewRequestBuilder;
-    private Handler mMainHandler;
     private ImageReader mImageReader = null;
-    private final Looper mLooper;
+    // We create a dedicated HandlerThread for operating the camera on. This
+    // is needed, because the camera APIs requires a Looper for posting
+    // asynchronous callbacks to. The native thread that calls the constructor
+    // and public API cannot be used for this, because it does not have a
+    // Looper.
+    private Handler mCameraThreadHandler;
+    private ConditionVariable mWaitForDeviceClosedConditionVariable = new ConditionVariable();
 
     private Range<Integer> mAeFpsRange;
     private CameraState mCameraState = CameraState.STOPPED;
@@ -352,24 +831,26 @@
         return null;
     }
 
-    // {@link nativeOnPhotoTaken()} needs to be called back if there's any
-    // problem after {@link takePhoto()} has returned true.
-    private void notifyTakePhotoError(long callbackId) {
-        nativeOnPhotoTaken(mNativeVideoCaptureDeviceAndroid, callbackId, new byte[0]);
+    private void createPreviewObjectsAndStartPreviewOrFailWith(int androidVideoCaptureError) {
+        assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
+
+        if (createPreviewObjectsAndStartPreview()) return;
+
+        changeCameraStateAndNotify(CameraState.STOPPED);
+        nativeOnError(mNativeVideoCaptureDeviceAndroid, androidVideoCaptureError,
+                "Error starting or restarting preview");
     }
 
     private boolean createPreviewObjectsAndStartPreview() {
+        assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
         if (mCameraDevice == null) return false;
 
         // Create an ImageReader and plug a thread looper into it to have
         // readback take place on its own thread.
         mImageReader = ImageReader.newInstance(mCaptureFormat.getWidth(),
                 mCaptureFormat.getHeight(), mCaptureFormat.getPixelFormat(), 2 /* maxImages */);
-        HandlerThread thread = new HandlerThread("CameraPreview");
-        thread.start();
-        final Handler backgroundHandler = new Handler(thread.getLooper());
         final CrPreviewReaderListener imageReaderListener = new CrPreviewReaderListener();
-        mImageReader.setOnImageAvailableListener(imageReaderListener, backgroundHandler);
+        mImageReader.setOnImageAvailableListener(imageReaderListener, mCameraThreadHandler);
 
         try {
             // TEMPLATE_PREVIEW specifically means "high frame rate is given
@@ -429,6 +910,8 @@
     }
 
     private void configureCommonCaptureSettings(CaptureRequest.Builder requestBuilder) {
+        assert mCameraThreadHandler.getLooper() == Looper.myLooper() : "called on wrong thread";
+
         final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(mId);
 
         // |mFocusMode| indicates if we're in auto/continuous, single-shot or manual mode.
@@ -579,7 +1062,7 @@
         return -1;
     }
 
-    private int getClosestWhiteBalance(int colorTemperature, int[] supportedTemperatures) {
+    private static int getClosestWhiteBalance(int colorTemperature, int[] supportedTemperatures) {
         int minDiff = Integer.MAX_VALUE;
         int matchedTemperature = -1;
 
@@ -595,14 +1078,14 @@
         return matchedTemperature;
     }
 
-    static boolean isLegacyDevice(int id) {
+    public static boolean isLegacyDevice(int id) {
         final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(id);
         return cameraCharacteristics != null
                 && cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
                 == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
     }
 
-    static int getNumberOfCameras() {
+    public static int getNumberOfCameras() {
         CameraManager manager = null;
         try {
             manager = (CameraManager) ContextUtils.getApplicationContext().getSystemService(
@@ -621,7 +1104,7 @@
         }
     }
 
-    static int getCaptureApiType(int id) {
+    public static int getCaptureApiType(int id) {
         final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(id);
         if (cameraCharacteristics == null) {
             return VideoCaptureApi.UNKNOWN;
@@ -641,7 +1124,7 @@
         }
     }
 
-    static int getFacingMode(int id) {
+    public static int getFacingMode(int id) {
         final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(id);
         if (cameraCharacteristics == null) {
             return VideoFacingMode.MEDIA_VIDEO_FACING_NONE;
@@ -658,7 +1141,7 @@
         }
     }
 
-    static String getName(int id) {
+    public static String getName(int id) {
         final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(id);
         if (cameraCharacteristics == null) return null;
         final int facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
@@ -666,7 +1149,7 @@
                 + ((facing == CameraCharacteristics.LENS_FACING_FRONT) ? "front" : "back");
     }
 
-    static VideoCaptureFormat[] getDeviceSupportedFormats(int id) {
+    public static VideoCaptureFormat[] getDeviceSupportedFormats(int id) {
         final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(id);
         if (cameraCharacteristics == null) return null;
 
@@ -711,7 +1194,13 @@
 
     VideoCaptureCamera2(int id, long nativeVideoCaptureDeviceAndroid) {
         super(id, nativeVideoCaptureDeviceAndroid);
-        mLooper = Looper.myLooper();
+
+        nativeDCheckCurrentlyOnIncomingTaskRunner(mNativeVideoCaptureDeviceAndroid);
+
+        HandlerThread thread = new HandlerThread("VideoCaptureCamera2_CameraThread");
+        thread.start();
+        mCameraThreadHandler = new Handler(thread.getLooper());
+
         final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(id);
         if (cameraCharacteristics != null) {
             mMaxZoom = cameraCharacteristics.get(
@@ -720,9 +1209,14 @@
     }
 
     @Override
+    public void finalize() {
+        mCameraThreadHandler.getLooper().quit();
+    }
+
+    @Override
     public boolean allocate(int width, int height, int frameRate) {
         Log.d(TAG, "allocate: requested (%d x %d) @%dfps", width, height, frameRate);
-        assert mLooper == Looper.myLooper() : "called on wrong thread";
+        nativeDCheckCurrentlyOnIncomingTaskRunner(mNativeVideoCaptureDeviceAndroid);
         synchronized (mCameraStateLock) {
             if (mCameraState == CameraState.OPENING || mCameraState == CameraState.CONFIGURING) {
                 Log.e(TAG, "allocate() invoked while Camera is busy opening/configuring.");
@@ -777,26 +1271,17 @@
     }
 
     @Override
-    public boolean startCapture() {
-        assert mLooper == Looper.myLooper() : "called on wrong thread";
+    public boolean startCaptureMaybeAsync() {
+        nativeDCheckCurrentlyOnIncomingTaskRunner(mNativeVideoCaptureDeviceAndroid);
+
         changeCameraStateAndNotify(CameraState.OPENING);
         final CameraManager manager =
                 (CameraManager) ContextUtils.getApplicationContext().getSystemService(
                         Context.CAMERA_SERVICE);
 
-        if (!mUseBackgroundThreadForTesting) {
-            mMainHandler = new Handler(ContextUtils.getApplicationContext().getMainLooper());
-        } else {
-            // Usually we deliver frames on application context thread, but unit tests
-            // occupy its Looper; deliver frames on a background thread instead.
-            HandlerThread thread = new HandlerThread("CameraPicture");
-            thread.start();
-            mMainHandler = new Handler(thread.getLooper());
-        }
-
         final CrStateListener stateListener = new CrStateListener();
         try {
-            manager.openCamera(Integer.toString(mId), stateListener, mMainHandler);
+            manager.openCamera(Integer.toString(mId), stateListener, mCameraThreadHandler);
         } catch (CameraAccessException | IllegalArgumentException | SecurityException ex) {
             Log.e(TAG, "allocate: manager.openCamera: ", ex);
             return false;
@@ -806,8 +1291,8 @@
     }
 
     @Override
-    public boolean stopCapture() {
-        assert mLooper == Looper.myLooper() : "called on wrong thread";
+    public boolean stopCaptureAndBlockUntilStopped() {
+        nativeDCheckCurrentlyOnIncomingTaskRunner(mNativeVideoCaptureDeviceAndroid);
 
         // With Camera2 API, the capture is started asynchronously, which will cause problem if
         // stopCapture comes too quickly. Without stopping the previous capture properly, the next
@@ -823,214 +1308,16 @@
             if (mCameraState == CameraState.STOPPED) return true;
         }
 
-        try {
-            mPreviewSession.abortCaptures();
-        } catch (CameraAccessException | IllegalStateException ex) {
-            // Stopping a device whose CameraCaptureSession is closed is not an error: ignore this.
-            Log.w(TAG, "abortCaptures: ", ex);
-        }
-        if (mCameraDevice == null) return false;
-        mCameraDevice.close();
+        mCameraThreadHandler.post(new StopCaptureTask());
+        mWaitForDeviceClosedConditionVariable.block();
 
-        if (mUseBackgroundThreadForTesting) mMainHandler.getLooper().quit();
-
-        changeCameraStateAndNotify(CameraState.STOPPED);
-        mCropRect = new Rect();
         return true;
     }
 
     @Override
-    public PhotoCapabilities getPhotoCapabilities() {
-        assert mLooper == Looper.myLooper() : "called on wrong thread";
-        final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(mId);
-        PhotoCapabilities.Builder builder = new PhotoCapabilities.Builder();
-
-        int minIso = 0;
-        int maxIso = 0;
-        final Range<Integer> iso_range =
-                cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE);
-        if (iso_range != null) {
-            minIso = iso_range.getLower();
-            maxIso = iso_range.getUpper();
-        }
-        builder.setMinIso(minIso).setMaxIso(maxIso).setStepIso(1);
-        if (mPreviewRequestBuilder.get(CaptureRequest.SENSOR_SENSITIVITY) != null) {
-            builder.setCurrentIso(mPreviewRequest.get(CaptureRequest.SENSOR_SENSITIVITY));
-        }
-
-        final StreamConfigurationMap streamMap =
-                cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
-        final Size[] supportedSizes = streamMap.getOutputSizes(ImageFormat.JPEG);
-        int minWidth = Integer.MAX_VALUE;
-        int minHeight = Integer.MAX_VALUE;
-        int maxWidth = 0;
-        int maxHeight = 0;
-        for (Size size : supportedSizes) {
-            if (size.getWidth() < minWidth) minWidth = size.getWidth();
-            if (size.getHeight() < minHeight) minHeight = size.getHeight();
-            if (size.getWidth() > maxWidth) maxWidth = size.getWidth();
-            if (size.getHeight() > maxHeight) maxHeight = size.getHeight();
-        }
-        builder.setMinHeight(minHeight).setMaxHeight(maxHeight).setStepHeight(1);
-        builder.setMinWidth(minWidth).setMaxWidth(maxWidth).setStepWidth(1);
-        builder.setCurrentHeight((mPhotoHeight > 0) ? mPhotoHeight : mCaptureFormat.getHeight());
-        builder.setCurrentWidth((mPhotoWidth > 0) ? mPhotoWidth : mCaptureFormat.getWidth());
-
-        final float currentZoom =
-                cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)
-                        .width()
-                / (float) mPreviewRequest.get(CaptureRequest.SCALER_CROP_REGION).width();
-        // There is no min-zoom per se, so clamp it to always 1.
-        builder.setMinZoom(1.0).setMaxZoom(mMaxZoom);
-        builder.setCurrentZoom(currentZoom).setStepZoom(0.1);
-
-        // Classify the Focus capabilities. In CONTINUOUS and SINGLE_SHOT, we can call
-        // autoFocus(AutoFocusCallback) to configure region(s) to focus onto.
-        final int[] jniFocusModes =
-                cameraCharacteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
-        ArrayList<Integer> focusModes = new ArrayList<Integer>(3);
-        for (int mode : jniFocusModes) {
-            if (mode == CameraMetadata.CONTROL_AF_MODE_OFF) {
-                focusModes.add(Integer.valueOf(AndroidMeteringMode.FIXED));
-            } else if (mode == CameraMetadata.CONTROL_AF_MODE_AUTO
-                    || mode == CameraMetadata.CONTROL_AF_MODE_MACRO) {
-                // CONTROL_AF_MODE_{AUTO,MACRO} do not imply continuously focusing.
-                if (!focusModes.contains(Integer.valueOf(AndroidMeteringMode.SINGLE_SHOT))) {
-                    focusModes.add(Integer.valueOf(AndroidMeteringMode.SINGLE_SHOT));
-                }
-            } else if (mode == CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_VIDEO
-                    || mode == CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE
-                    || mode == CameraMetadata.CONTROL_AF_MODE_EDOF) {
-                if (!focusModes.contains(Integer.valueOf(AndroidMeteringMode.CONTINUOUS))) {
-                    focusModes.add(Integer.valueOf(AndroidMeteringMode.CONTINUOUS));
-                }
-            }
-        }
-        builder.setFocusModes(integerArrayListToArray(focusModes));
-
-        final int focusMode = mPreviewRequest.get(CaptureRequest.CONTROL_AF_MODE);
-        int jniFocusMode = AndroidMeteringMode.NONE;
-        if (focusMode == CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_VIDEO
-                || focusMode == CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE) {
-            jniFocusMode = AndroidMeteringMode.CONTINUOUS;
-        } else if (focusMode == CameraMetadata.CONTROL_AF_MODE_AUTO
-                || focusMode == CameraMetadata.CONTROL_AF_MODE_MACRO) {
-            jniFocusMode = AndroidMeteringMode.SINGLE_SHOT;
-        } else if (focusMode == CameraMetadata.CONTROL_AF_MODE_OFF) {
-            jniFocusMode = AndroidMeteringMode.FIXED;
-        } else {
-            assert jniFocusMode == CameraMetadata.CONTROL_AF_MODE_EDOF;
-        }
-        builder.setFocusMode(jniFocusMode);
-
-        // Auto Exposure is the usual capability and state, unless AE is not available at all, which
-        // is signalled by an empty CONTROL_AE_AVAILABLE_MODES list. Exposure Compensation can also
-        // support or be locked, this is equivalent to AndroidMeteringMode.FIXED.
-        final int[] jniExposureModes =
-                cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
-        ArrayList<Integer> exposureModes = new ArrayList<Integer>(1);
-        for (int mode : jniExposureModes) {
-            if (mode == CameraMetadata.CONTROL_AE_MODE_ON
-                    || mode == CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH
-                    || mode == CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH
-                    || mode == CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) {
-                exposureModes.add(Integer.valueOf(AndroidMeteringMode.CONTINUOUS));
-                break;
-            }
-        }
-        try {
-            if (cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE)) {
-                exposureModes.add(Integer.valueOf(AndroidMeteringMode.FIXED));
-            }
-        } catch (NoSuchFieldError e) {
-            // Ignore this exception, it means CONTROL_AE_LOCK_AVAILABLE is not known.
-        }
-        builder.setExposureModes(integerArrayListToArray(exposureModes));
-
-        int jniExposureMode = AndroidMeteringMode.CONTINUOUS;
-        if (mPreviewRequest.get(CaptureRequest.CONTROL_AE_MODE)
-                == CameraMetadata.CONTROL_AE_MODE_OFF) {
-            jniExposureMode = AndroidMeteringMode.NONE;
-        }
-        if (mPreviewRequest.get(CaptureRequest.CONTROL_AE_LOCK)) {
-            jniExposureMode = AndroidMeteringMode.FIXED;
-        }
-        builder.setExposureMode(jniExposureMode);
-
-        final float step =
-                cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP)
-                        .floatValue();
-        builder.setStepExposureCompensation(step);
-        final Range<Integer> exposureCompensationRange =
-                cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE);
-        builder.setMinExposureCompensation(exposureCompensationRange.getLower() * step);
-        builder.setMaxExposureCompensation(exposureCompensationRange.getUpper() * step);
-        builder.setCurrentExposureCompensation(
-                mPreviewRequest.get(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION) * step);
-
-        final int[] jniWhiteBalanceMode =
-                cameraCharacteristics.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES);
-        ArrayList<Integer> whiteBalanceModes = new ArrayList<Integer>(1);
-        for (int mode : jniWhiteBalanceMode) {
-            if (mode == CameraMetadata.CONTROL_AWB_MODE_AUTO) {
-                whiteBalanceModes.add(Integer.valueOf(AndroidMeteringMode.CONTINUOUS));
-                break;
-            }
-        }
-        try {
-            if (cameraCharacteristics.get(CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE)) {
-                whiteBalanceModes.add(Integer.valueOf(AndroidMeteringMode.FIXED));
-            }
-        } catch (NoSuchFieldError e) {
-            // Ignore this exception, it means CONTROL_AWB_LOCK_AVAILABLE is not known.
-        }
-        builder.setWhiteBalanceModes(integerArrayListToArray(whiteBalanceModes));
-
-        final int whiteBalanceMode = mPreviewRequest.get(CaptureRequest.CONTROL_AWB_MODE);
-        if (whiteBalanceMode == CameraMetadata.CONTROL_AWB_MODE_OFF) {
-            builder.setWhiteBalanceMode(AndroidMeteringMode.NONE);
-        } else if (whiteBalanceMode == CameraMetadata.CONTROL_AWB_MODE_AUTO) {
-            builder.setWhiteBalanceMode(AndroidMeteringMode.CONTINUOUS);
-        } else {
-            builder.setWhiteBalanceMode(AndroidMeteringMode.FIXED);
-        }
-        builder.setMinColorTemperature(COLOR_TEMPERATURES_MAP.keyAt(0));
-        builder.setMaxColorTemperature(
-                COLOR_TEMPERATURES_MAP.keyAt(COLOR_TEMPERATURES_MAP.size() - 1));
-        final int index = COLOR_TEMPERATURES_MAP.indexOfValue(whiteBalanceMode);
-        if (index >= 0) {
-            builder.setCurrentColorTemperature(COLOR_TEMPERATURES_MAP.keyAt(index));
-        }
-        builder.setStepColorTemperature(50);
-
-        if (!cameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)) {
-            builder.setSupportsTorch(false);
-            builder.setRedEyeReduction(false);
-        } else {
-            // There's no way to query if torch and/or red eye reduction modes are available using
-            // Camera2 API but since there's a Flash unit, we assume so.
-            builder.setSupportsTorch(true);
-            builder.setTorch(mPreviewRequest.get(CaptureRequest.FLASH_MODE)
-                    == CameraMetadata.FLASH_MODE_TORCH);
-
-            builder.setRedEyeReduction(true);
-
-            final int[] flashModes =
-                    cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
-            ArrayList<Integer> modes = new ArrayList<Integer>(0);
-            for (int flashMode : flashModes) {
-                if (flashMode == CameraMetadata.FLASH_MODE_OFF) {
-                    modes.add(Integer.valueOf(AndroidFillLightMode.OFF));
-                } else if (flashMode == CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH) {
-                    modes.add(Integer.valueOf(AndroidFillLightMode.AUTO));
-                } else if (flashMode == CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH) {
-                    modes.add(Integer.valueOf(AndroidFillLightMode.FLASH));
-                }
-            }
-            builder.setFillLightModes(integerArrayListToArray(modes));
-        }
-
-        return builder.build();
+    public void getPhotoCapabilitiesAsync(long callbackId) {
+        nativeDCheckCurrentlyOnIncomingTaskRunner(mNativeVideoCaptureDeviceAndroid);
+        mCameraThreadHandler.post(new GetPhotoCapabilitiesTask(callbackId));
     }
 
     @Override
@@ -1039,142 +1326,17 @@
             double exposureCompensation, int whiteBalanceMode, double iso,
             boolean hasRedEyeReduction, boolean redEyeReduction, int fillLightMode,
             boolean hasTorch, boolean torch, double colorTemperature) {
-        assert mLooper == Looper.myLooper() : "called on wrong thread";
-        final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(mId);
-        final Rect canvas =
-                cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
-
-        if (zoom != 0) {
-            final float normalizedZoom = Math.max(1.0f, Math.min((float) zoom, mMaxZoom));
-            final float cropFactor = (normalizedZoom - 1) / (2 * normalizedZoom);
-
-            mCropRect = new Rect(Math.round(canvas.width() * cropFactor),
-                    Math.round(canvas.height() * cropFactor),
-                    Math.round(canvas.width() * (1 - cropFactor)),
-                    Math.round(canvas.height() * (1 - cropFactor)));
-            Log.d(TAG, "zoom level %f, rectangle: %s", normalizedZoom, mCropRect.toString());
-        }
-
-        if (focusMode != AndroidMeteringMode.NOT_SET) mFocusMode = focusMode;
-        if (exposureMode != AndroidMeteringMode.NOT_SET) mExposureMode = exposureMode;
-        if (whiteBalanceMode != AndroidMeteringMode.NOT_SET) mWhiteBalanceMode = whiteBalanceMode;
-
-        if (width > 0) mPhotoWidth = (int) Math.round(width);
-        if (height > 0) mPhotoHeight = (int) Math.round(height);
-
-        // Upon new |zoom| configuration, clear up the previous |mAreaOfInterest| if any.
-        if (mAreaOfInterest != null && !mAreaOfInterest.getRect().isEmpty() && zoom > 0) {
-            mAreaOfInterest = null;
-        }
-        // Also clear |mAreaOfInterest| if the user sets it as NONE.
-        if (mFocusMode == AndroidMeteringMode.NONE || mExposureMode == AndroidMeteringMode.NONE) {
-            mAreaOfInterest = null;
-        }
-        // Update |mAreaOfInterest| if the camera supports and there are |pointsOfInterest2D|.
-        final boolean pointsOfInterestSupported =
-                cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF) > 0
-                || cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE) > 0
-                || cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB) > 0;
-        if (pointsOfInterestSupported && pointsOfInterest2D.length > 0) {
-            assert pointsOfInterest2D.length == 2 : "Only 1 point of interest supported";
-            assert pointsOfInterest2D[0] <= 1.0 && pointsOfInterest2D[0] >= 0.0;
-            assert pointsOfInterest2D[1] <= 1.0 && pointsOfInterest2D[1] >= 0.0;
-            // Calculate a Rect of 1/8 the |visibleRect| dimensions, and center it w.r.t. |canvas|.
-            final Rect visibleRect = (mCropRect.isEmpty()) ? canvas : mCropRect;
-            int centerX = Math.round(pointsOfInterest2D[0] * visibleRect.width());
-            int centerY = Math.round(pointsOfInterest2D[1] * visibleRect.height());
-            if (visibleRect.equals(mCropRect)) {
-                centerX += (canvas.width() - visibleRect.width()) / 2;
-                centerY += (canvas.height() - visibleRect.height()) / 2;
-            }
-            final int regionWidth = visibleRect.width() / 8;
-            final int regionHeight = visibleRect.height() / 8;
-
-            mAreaOfInterest = new MeteringRectangle(Math.max(0, centerX - regionWidth / 2),
-                    Math.max(0, centerY - regionHeight / 2), regionWidth, regionHeight,
-                    MeteringRectangle.METERING_WEIGHT_MAX);
-
-            Log.d(TAG, "Calculating (%.2fx%.2f) wrt to %s (canvas being %s)", pointsOfInterest2D[0],
-                    pointsOfInterest2D[1], visibleRect.toString(), canvas.toString());
-            Log.d(TAG, "Area of interest %s", mAreaOfInterest.toString());
-        }
-
-        if (hasExposureCompensation) {
-            mExposureCompensation = (int) Math.round(exposureCompensation
-                    / cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP)
-                              .floatValue());
-        }
-        if (iso > 0) mIso = (int) Math.round(iso);
-        if (colorTemperature > 0) mColorTemperature = (int) Math.round(colorTemperature);
-
-        if (hasRedEyeReduction) mRedEyeReduction = redEyeReduction;
-        if (fillLightMode != AndroidFillLightMode.NOT_SET) mFillLightMode = fillLightMode;
-        if (hasTorch) mTorch = torch;
-
-        final Handler mainHandler =
-                new Handler(ContextUtils.getApplicationContext().getMainLooper());
-        mainHandler.removeCallbacks(mReconfigureCaptureTask);
-        mainHandler.post(mReconfigureCaptureTask);
+        nativeDCheckCurrentlyOnIncomingTaskRunner(mNativeVideoCaptureDeviceAndroid);
+        mCameraThreadHandler.post(new SetPhotoOptionsTask(new PhotoOptions(zoom, focusMode,
+                exposureMode, width, height, pointsOfInterest2D, hasExposureCompensation,
+                exposureCompensation, whiteBalanceMode, iso, hasRedEyeReduction, redEyeReduction,
+                fillLightMode, hasTorch, torch, colorTemperature)));
     }
 
     @Override
-    public boolean takePhoto(final long callbackId) {
-        assert mLooper == Looper.myLooper() : "called on wrong thread";
-        if (mCameraDevice == null || mCameraState != CameraState.STARTED) return false;
-
-        final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(mId);
-        final StreamConfigurationMap streamMap =
-                cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
-        final Size[] supportedSizes = streamMap.getOutputSizes(ImageFormat.JPEG);
-        final Size closestSize = findClosestSizeInArray(supportedSizes, mPhotoWidth, mPhotoHeight);
-
-        Log.d(TAG, "requested resolution: (%dx%d)", mPhotoWidth, mPhotoHeight);
-        if (closestSize != null) {
-            Log.d(TAG, " matched (%dx%d)", closestSize.getWidth(), closestSize.getHeight());
-        }
-        final ImageReader imageReader = ImageReader.newInstance(
-                (closestSize != null) ? closestSize.getWidth() : mCaptureFormat.getWidth(),
-                (closestSize != null) ? closestSize.getHeight() : mCaptureFormat.getHeight(),
-                ImageFormat.JPEG, 1 /* maxImages */);
-
-        HandlerThread thread = new HandlerThread("CameraPicture");
-        thread.start();
-        final Handler backgroundHandler = new Handler(thread.getLooper());
-
-        final CrPhotoReaderListener photoReaderListener = new CrPhotoReaderListener(callbackId);
-        imageReader.setOnImageAvailableListener(photoReaderListener, backgroundHandler);
-
-        final List<Surface> surfaceList = new ArrayList<Surface>(1);
-        // TODO(mcasas): release this Surface when not needed, https://crbug.com/643884.
-        surfaceList.add(imageReader.getSurface());
-
-        CaptureRequest.Builder photoRequestBuilder = null;
-        try {
-            photoRequestBuilder =
-                    mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
-        } catch (CameraAccessException ex) {
-            Log.e(TAG, "createCaptureRequest() error ", ex);
-            return false;
-        }
-        if (photoRequestBuilder == null) {
-            Log.e(TAG, "photoRequestBuilder error");
-            return false;
-        }
-        photoRequestBuilder.addTarget(imageReader.getSurface());
-        photoRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, getCameraRotation());
-
-        configureCommonCaptureSettings(photoRequestBuilder);
-
-        final CaptureRequest photoRequest = photoRequestBuilder.build();
-        final CrPhotoSessionListener sessionListener =
-                new CrPhotoSessionListener(imageReader, photoRequest, callbackId);
-        try {
-            mCameraDevice.createCaptureSession(surfaceList, sessionListener, backgroundHandler);
-        } catch (CameraAccessException | IllegalArgumentException | SecurityException ex) {
-            Log.e(TAG, "createCaptureSession: " + ex);
-            return false;
-        }
-        return true;
+    public void takePhotoAsync(long callbackId) {
+        nativeDCheckCurrentlyOnIncomingTaskRunner(mNativeVideoCaptureDeviceAndroid);
+        mCameraThreadHandler.post(new TakePhotoTask(callbackId));
     }
 
     @Override
diff --git a/media/capture/video/android/video_capture_device_android.cc b/media/capture/video/android/video_capture_device_android.cc
index 5d496c50..fdcd066 100644
--- a/media/capture/video/android/video_capture_device_android.cc
+++ b/media/capture/video/android/video_capture_device_android.cc
@@ -162,7 +162,7 @@
            << capture_format_.frame_size.ToString() << ")@ "
            << capture_format_.frame_rate << "fps";
 
-  ret = Java_VideoCapture_startCapture(env, j_capture_);
+  ret = Java_VideoCapture_startCaptureMaybeAsync(env, j_capture_);
   if (!ret) {
     SetErrorState(media::VideoCaptureError::kAndroidFailedToStartCapture,
                   FROM_HERE, "failed to start capture");
@@ -185,7 +185,8 @@
 
   JNIEnv* env = AttachCurrentThread();
 
-  const jboolean ret = Java_VideoCapture_stopCapture(env, j_capture_);
+  const jboolean ret =
+      Java_VideoCapture_stopCaptureAndBlockUntilStopped(env, j_capture_);
   if (!ret) {
     SetErrorState(media::VideoCaptureError::kAndroidFailedToStopCapture,
                   FROM_HERE, "failed to stop capture");
@@ -349,6 +350,115 @@
       FROM_HERE, base::android::ConvertJavaStringToUTF8(env, message));
 }
 
+void VideoCaptureDeviceAndroid::OnGetPhotoCapabilitiesReply(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj,
+    jlong callback_id,
+    jobject result) {
+  base::AutoLock lock(photo_callbacks_lock_);
+  GetPhotoStateCallback* const cb =
+      reinterpret_cast<GetPhotoStateCallback*>(callback_id);
+  // Search for the pointer |cb| in the list of |take_photo_callbacks_|.
+  const auto reference_it = std::find_if(
+      get_photo_state_callbacks_.begin(), get_photo_state_callbacks_.end(),
+      [cb](const std::unique_ptr<GetPhotoStateCallback>& callback) {
+        return callback.get() == cb;
+      });
+  if (reference_it == get_photo_state_callbacks_.end()) {
+    NOTREACHED() << "|callback_id| not found.";
+    return;
+  }
+
+  base::android::ScopedJavaLocalRef<jobject> scoped_photo_capabilities(env,
+                                                                       result);
+  PhotoCapabilities caps(scoped_photo_capabilities);
+
+  // TODO(mcasas): Manual member copying sucks, consider adding typemapping from
+  // PhotoCapabilities to mojom::PhotoStatePtr, https://crbug.com/622002.
+  mojom::PhotoStatePtr photo_capabilities = mojom::PhotoState::New();
+
+  const auto jni_white_balance_modes = caps.getWhiteBalanceModes();
+  std::vector<mojom::MeteringMode> white_balance_modes;
+  for (const auto& white_balance_mode : jni_white_balance_modes)
+    white_balance_modes.push_back(ToMojomMeteringMode(white_balance_mode));
+  photo_capabilities->supported_white_balance_modes = white_balance_modes;
+  photo_capabilities->current_white_balance_mode =
+      ToMojomMeteringMode(caps.getWhiteBalanceMode());
+
+  const auto jni_exposure_modes = caps.getExposureModes();
+  std::vector<mojom::MeteringMode> exposure_modes;
+  for (const auto& exposure_mode : jni_exposure_modes)
+    exposure_modes.push_back(ToMojomMeteringMode(exposure_mode));
+  photo_capabilities->supported_exposure_modes = exposure_modes;
+  photo_capabilities->current_exposure_mode =
+      ToMojomMeteringMode(caps.getExposureMode());
+
+  const auto jni_focus_modes = caps.getFocusModes();
+  std::vector<mojom::MeteringMode> focus_modes;
+  for (const auto& focus_mode : jni_focus_modes)
+    focus_modes.push_back(ToMojomMeteringMode(focus_mode));
+  photo_capabilities->supported_focus_modes = focus_modes;
+  photo_capabilities->current_focus_mode =
+      ToMojomMeteringMode(caps.getFocusMode());
+
+  photo_capabilities->exposure_compensation = mojom::Range::New();
+  photo_capabilities->exposure_compensation->current =
+      caps.getCurrentExposureCompensation();
+  photo_capabilities->exposure_compensation->max =
+      caps.getMaxExposureCompensation();
+  photo_capabilities->exposure_compensation->min =
+      caps.getMinExposureCompensation();
+  photo_capabilities->exposure_compensation->step =
+      caps.getStepExposureCompensation();
+  photo_capabilities->color_temperature = mojom::Range::New();
+  photo_capabilities->color_temperature->current =
+      caps.getCurrentColorTemperature();
+  photo_capabilities->color_temperature->max = caps.getMaxColorTemperature();
+  photo_capabilities->color_temperature->min = caps.getMinColorTemperature();
+  photo_capabilities->color_temperature->step = caps.getStepColorTemperature();
+  photo_capabilities->iso = mojom::Range::New();
+  photo_capabilities->iso->current = caps.getCurrentIso();
+  photo_capabilities->iso->max = caps.getMaxIso();
+  photo_capabilities->iso->min = caps.getMinIso();
+  photo_capabilities->iso->step = caps.getStepIso();
+
+  photo_capabilities->brightness = mojom::Range::New();
+  photo_capabilities->contrast = mojom::Range::New();
+  photo_capabilities->saturation = mojom::Range::New();
+  photo_capabilities->sharpness = mojom::Range::New();
+
+  photo_capabilities->zoom = mojom::Range::New();
+  photo_capabilities->zoom->current = caps.getCurrentZoom();
+  photo_capabilities->zoom->max = caps.getMaxZoom();
+  photo_capabilities->zoom->min = caps.getMinZoom();
+  photo_capabilities->zoom->step = caps.getStepZoom();
+
+  photo_capabilities->supports_torch = caps.getSupportsTorch();
+  photo_capabilities->torch = caps.getTorch();
+
+  photo_capabilities->red_eye_reduction =
+      caps.getRedEyeReduction() ? mojom::RedEyeReduction::CONTROLLABLE
+                                : mojom::RedEyeReduction::NEVER;
+  photo_capabilities->height = mojom::Range::New();
+  photo_capabilities->height->current = caps.getCurrentHeight();
+  photo_capabilities->height->max = caps.getMaxHeight();
+  photo_capabilities->height->min = caps.getMinHeight();
+  photo_capabilities->height->step = caps.getStepHeight();
+  photo_capabilities->width = mojom::Range::New();
+  photo_capabilities->width->current = caps.getCurrentWidth();
+  photo_capabilities->width->max = caps.getMaxWidth();
+  photo_capabilities->width->min = caps.getMinWidth();
+  photo_capabilities->width->step = caps.getStepWidth();
+  const auto fill_light_modes = caps.getFillLightModes();
+  std::vector<mojom::FillLightMode> modes;
+  for (const auto& fill_light_mode : fill_light_modes)
+    modes.push_back(ToMojomFillLightMode(fill_light_mode));
+  photo_capabilities->fill_light_mode = modes;
+
+  std::move(*cb).Run(std::move(photo_capabilities));
+  get_photo_state_callbacks_.erase(reference_it);
+}
+
 void VideoCaptureDeviceAndroid::OnPhotoTaken(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj,
@@ -360,23 +470,25 @@
 
   TakePhotoCallback* const cb =
       reinterpret_cast<TakePhotoCallback*>(callback_id);
-  // Search for the pointer |cb| in the list of |photo_callbacks_|.
+  // Search for the pointer |cb| in the list of |take_photo_callbacks_|.
   const auto reference_it =
-      std::find_if(photo_callbacks_.begin(), photo_callbacks_.end(),
+      std::find_if(take_photo_callbacks_.begin(), take_photo_callbacks_.end(),
                    [cb](const std::unique_ptr<TakePhotoCallback>& callback) {
                      return callback.get() == cb;
                    });
-  if (reference_it == photo_callbacks_.end()) {
+  if (reference_it == take_photo_callbacks_.end()) {
     NOTREACHED() << "|callback_id| not found.";
     return;
   }
 
-  mojom::BlobPtr blob = mojom::Blob::New();
-  base::android::JavaByteArrayToByteVector(env, data.obj(), &blob->data);
-  blob->mime_type = blob->data.empty() ? "" : "image/jpeg";
-  std::move(*cb).Run(std::move(blob));
+  if (data != nullptr) {
+    mojom::BlobPtr blob = mojom::Blob::New();
+    base::android::JavaByteArrayToByteVector(env, data.obj(), &blob->data);
+    blob->mime_type = blob->data.empty() ? "" : "image/jpeg";
+    std::move(*cb).Run(std::move(blob));
+  }
 
-  photo_callbacks_.erase(reference_it);
+  take_photo_callbacks_.erase(reference_it);
 }
 
 void VideoCaptureDeviceAndroid::OnStarted(JNIEnv* env,
@@ -385,6 +497,12 @@
     client_->OnStarted();
 }
 
+void VideoCaptureDeviceAndroid::DCheckCurrentlyOnIncomingTaskRunner(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj) {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+}
+
 void VideoCaptureDeviceAndroid::ConfigureForTesting() {
   Java_VideoCapture_setTestMode(AttachCurrentThread(), j_capture_);
 }
@@ -472,15 +590,11 @@
   std::unique_ptr<TakePhotoCallback> heap_callback(
       new TakePhotoCallback(std::move(callback)));
   const intptr_t callback_id = reinterpret_cast<intptr_t>(heap_callback.get());
-
-  // We need lock here because asynchronous response to
-  // Java_VideoCapture_takePhoto(), i.e. a call to OnPhotoTaken, arrives from a
-  // separate thread, and it can arrive before |photo_callbacks_.push_back()|
-  // has executed.
-  base::AutoLock lock(photo_callbacks_lock_);
-  if (Java_VideoCapture_takePhoto(env, j_capture_, callback_id)) {
-    photo_callbacks_.push_back(std::move(heap_callback));
+  {
+    base::AutoLock lock(photo_callbacks_lock_);
+    take_photo_callbacks_.push_back(std::move(heap_callback));
   }
+  Java_VideoCapture_takePhotoAsync(env, j_capture_, callback_id);
 }
 
 void VideoCaptureDeviceAndroid::DoGetPhotoState(
@@ -495,92 +609,15 @@
 #endif
   JNIEnv* env = AttachCurrentThread();
 
-  PhotoCapabilities caps(
-      Java_VideoCapture_getPhotoCapabilities(env, j_capture_));
-
-  // TODO(mcasas): Manual member copying sucks, consider adding typemapping from
-  // PhotoCapabilities to mojom::PhotoStatePtr, https://crbug.com/622002.
-  mojom::PhotoStatePtr photo_capabilities = mojom::PhotoState::New();
-
-  const auto jni_white_balance_modes = caps.getWhiteBalanceModes();
-  std::vector<mojom::MeteringMode> white_balance_modes;
-  for (const auto& white_balance_mode : jni_white_balance_modes)
-    white_balance_modes.push_back(ToMojomMeteringMode(white_balance_mode));
-  photo_capabilities->supported_white_balance_modes = white_balance_modes;
-  photo_capabilities->current_white_balance_mode =
-      ToMojomMeteringMode(caps.getWhiteBalanceMode());
-
-  const auto jni_exposure_modes = caps.getExposureModes();
-  std::vector<mojom::MeteringMode> exposure_modes;
-  for (const auto& exposure_mode : jni_exposure_modes)
-    exposure_modes.push_back(ToMojomMeteringMode(exposure_mode));
-  photo_capabilities->supported_exposure_modes = exposure_modes;
-  photo_capabilities->current_exposure_mode =
-      ToMojomMeteringMode(caps.getExposureMode());
-
-  const auto jni_focus_modes = caps.getFocusModes();
-  std::vector<mojom::MeteringMode> focus_modes;
-  for (const auto& focus_mode : jni_focus_modes)
-    focus_modes.push_back(ToMojomMeteringMode(focus_mode));
-  photo_capabilities->supported_focus_modes = focus_modes;
-  photo_capabilities->current_focus_mode =
-      ToMojomMeteringMode(caps.getFocusMode());
-
-  photo_capabilities->exposure_compensation = mojom::Range::New();
-  photo_capabilities->exposure_compensation->current =
-      caps.getCurrentExposureCompensation();
-  photo_capabilities->exposure_compensation->max =
-      caps.getMaxExposureCompensation();
-  photo_capabilities->exposure_compensation->min =
-      caps.getMinExposureCompensation();
-  photo_capabilities->exposure_compensation->step =
-      caps.getStepExposureCompensation();
-  photo_capabilities->color_temperature = mojom::Range::New();
-  photo_capabilities->color_temperature->current =
-      caps.getCurrentColorTemperature();
-  photo_capabilities->color_temperature->max = caps.getMaxColorTemperature();
-  photo_capabilities->color_temperature->min = caps.getMinColorTemperature();
-  photo_capabilities->color_temperature->step = caps.getStepColorTemperature();
-  photo_capabilities->iso = mojom::Range::New();
-  photo_capabilities->iso->current = caps.getCurrentIso();
-  photo_capabilities->iso->max = caps.getMaxIso();
-  photo_capabilities->iso->min = caps.getMinIso();
-  photo_capabilities->iso->step = caps.getStepIso();
-
-  photo_capabilities->brightness = mojom::Range::New();
-  photo_capabilities->contrast = mojom::Range::New();
-  photo_capabilities->saturation = mojom::Range::New();
-  photo_capabilities->sharpness = mojom::Range::New();
-
-  photo_capabilities->zoom = mojom::Range::New();
-  photo_capabilities->zoom->current = caps.getCurrentZoom();
-  photo_capabilities->zoom->max = caps.getMaxZoom();
-  photo_capabilities->zoom->min = caps.getMinZoom();
-  photo_capabilities->zoom->step = caps.getStepZoom();
-
-  photo_capabilities->supports_torch = caps.getSupportsTorch();
-  photo_capabilities->torch = caps.getTorch();
-
-  photo_capabilities->red_eye_reduction =
-      caps.getRedEyeReduction() ? mojom::RedEyeReduction::CONTROLLABLE
-                                : mojom::RedEyeReduction::NEVER;
-  photo_capabilities->height = mojom::Range::New();
-  photo_capabilities->height->current = caps.getCurrentHeight();
-  photo_capabilities->height->max = caps.getMaxHeight();
-  photo_capabilities->height->min = caps.getMinHeight();
-  photo_capabilities->height->step = caps.getStepHeight();
-  photo_capabilities->width = mojom::Range::New();
-  photo_capabilities->width->current = caps.getCurrentWidth();
-  photo_capabilities->width->max = caps.getMaxWidth();
-  photo_capabilities->width->min = caps.getMinWidth();
-  photo_capabilities->width->step = caps.getStepWidth();
-  const auto fill_light_modes = caps.getFillLightModes();
-  std::vector<mojom::FillLightMode> modes;
-  for (const auto& fill_light_mode : fill_light_modes)
-    modes.push_back(ToMojomFillLightMode(fill_light_mode));
-  photo_capabilities->fill_light_mode = modes;
-
-  std::move(callback).Run(std::move(photo_capabilities));
+  // Make copy on the heap so we can pass the pointer through JNI.
+  std::unique_ptr<GetPhotoStateCallback> heap_callback(
+      new GetPhotoStateCallback(std::move(callback)));
+  const intptr_t callback_id = reinterpret_cast<intptr_t>(heap_callback.get());
+  {
+    base::AutoLock lock(photo_callbacks_lock_);
+    get_photo_state_callbacks_.push_back(std::move(heap_callback));
+  }
+  Java_VideoCapture_getPhotoCapabilitiesAsync(env, j_capture_, callback_id);
 }
 
 void VideoCaptureDeviceAndroid::DoSetPhotoOptions(
diff --git a/media/capture/video/android/video_capture_device_android.h b/media/capture/video/android/video_capture_device_android.h
index 500dea4c..eb1c619 100644
--- a/media/capture/video/android/video_capture_device_android.h
+++ b/media/capture/video/android/video_capture_device_android.h
@@ -21,7 +21,7 @@
 namespace base {
 class Location;
 class SingleThreadTaskRunner;
-}
+}  // namespace base
 
 namespace media {
 
@@ -106,6 +106,12 @@
                int android_video_capture_error,
                const base::android::JavaParamRef<jstring>& message);
 
+  void OnGetPhotoCapabilitiesReply(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj,
+      jlong callback_id,
+      jobject photo_capabilities);
+
   // Implement org.chromium.media.VideoCapture.nativeOnPhotoTaken.
   void OnPhotoTaken(JNIEnv* env,
                     const base::android::JavaParamRef<jobject>& obj,
@@ -115,6 +121,12 @@
   // Implement org.chromium.media.VideoCapture.nativeOnStarted.
   void OnStarted(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
 
+  // Implement
+  // org.chromium.media.VideoCapture.nativeDCheckCurrentlyOnIncomingTaskRunner.
+  void DCheckCurrentlyOnIncomingTaskRunner(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj);
+
   void ConfigureForTesting();
 
  protected:
@@ -168,9 +180,10 @@
   base::TimeTicks expected_next_frame_time_;
   base::TimeDelta frame_interval_;
 
-  // List of |photo_callbacks_| in flight, being served in Java side.
+  // List of callbacks for photo API in flight, being served in Java side.
   base::Lock photo_callbacks_lock_;
-  std::list<std::unique_ptr<TakePhotoCallback>> photo_callbacks_;
+  std::list<std::unique_ptr<GetPhotoStateCallback>> get_photo_state_callbacks_;
+  std::list<std::unique_ptr<TakePhotoCallback>> take_photo_callbacks_;
 
   const VideoCaptureDeviceDescriptor device_descriptor_;
   VideoCaptureFormat capture_format_;
diff --git a/media/filters/BUILD.gn b/media/filters/BUILD.gn
index e5802a0..eb1ba88 100644
--- a/media/filters/BUILD.gn
+++ b/media/filters/BUILD.gn
@@ -12,7 +12,6 @@
   # should be using //media and not directly DEP this roll-up target.
   visibility = [
     "//media",
-    "//media/muxers",
     "//media/renderers",
   ]
 
@@ -52,8 +51,6 @@
     "memory_data_source.h",
     "offloading_video_decoder.cc",
     "offloading_video_decoder.h",
-    "opus_constants.cc",
-    "opus_constants.h",
     "pipeline_controller.cc",
     "pipeline_controller.h",
     "source_buffer_parse_warnings.h",
diff --git a/media/filters/opus_constants.cc b/media/filters/opus_constants.cc
deleted file mode 100644
index 438367d..0000000
--- a/media/filters/opus_constants.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2015 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 <stdint.h>
-#include "media/filters/opus_constants.h"
-
-namespace media {
-
-const uint8_t kDefaultOpusChannelLayout[OPUS_MAX_CHANNELS_WITH_DEFAULT_LAYOUT] =
-    {0, 1};
-
-const uint8_t kFFmpegChannelDecodingLayouts
-    [OPUS_MAX_VORBIS_CHANNELS][OPUS_MAX_VORBIS_CHANNELS] = {
-        {0},
-
-        // Stereo: No reorder.
-        {0, 1},
-
-        // 3 Channels, from Vorbis order to:
-        //  L, R, Center
-        {0, 2, 1},
-
-        // 4 Channels: No reorder.
-        {0, 1, 2, 3},
-
-        // 5 Channels, from Vorbis order to:
-        //  Front L, Front R, Center, Back L, Back R
-        {0, 2, 1, 3, 4},
-
-        // 6 Channels (5.1), from Vorbis order to:
-        //  Front L, Front R, Center, LFE, Back L, Back R
-        {0, 2, 1, 5, 3, 4},
-
-        // 7 Channels (6.1), from Vorbis order to:
-        //  Front L, Front R, Front Center, LFE, Side L, Side R, Back Center
-        {0, 2, 1, 6, 3, 4, 5},
-
-        // 8 Channels (7.1), from Vorbis order to:
-        //  Front L, Front R, Center, LFE, Back L, Back R, Side L, Side R
-        {0, 2, 1, 7, 5, 6, 3, 4},
-};
-
-const uint8_t
-    kOpusVorbisChannelMap[OPUS_MAX_VORBIS_CHANNELS][OPUS_MAX_VORBIS_CHANNELS] =
-        {
-            {0},
-            {0, 1},
-            {0, 2, 1},
-            {0, 1, 2, 3},
-            {0, 4, 1, 2, 3},
-            {0, 4, 1, 2, 3, 5},
-            {0, 4, 1, 2, 3, 5, 6},
-            {0, 6, 1, 2, 3, 4, 5, 7},
-};
-
-}  // namespace media
diff --git a/media/filters/stream_parser_factory.cc b/media/filters/stream_parser_factory.cc
index d515e2b..276164c 100644
--- a/media/filters/stream_parser_factory.cc
+++ b/media/filters/stream_parser_factory.cc
@@ -258,6 +258,7 @@
                                                    nullptr};
 
 static const CodecInfo* const kAudioMP4Codecs[] = {&kMPEG4FLACCodecInfo,
+                                                   &kOpusCodecInfo,
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
                                                    &kMPEG4AACCodecInfo,
                                                    &kMPEG2AACLCCodecInfo,
diff --git a/media/formats/BUILD.gn b/media/formats/BUILD.gn
index 1ed89cb..bedeafe 100644
--- a/media/formats/BUILD.gn
+++ b/media/formats/BUILD.gn
@@ -13,6 +13,7 @@
     "//media/base/android/",
     "//media/cdm",
     "//media/filters",
+    "//media/muxers",
   ]
 
   sources = [
@@ -20,6 +21,8 @@
     "ac3/ac3_util.h",
     "common/offset_byte_queue.cc",
     "common/offset_byte_queue.h",
+    "common/opus_constants.cc",
+    "common/opus_constants.h",
     "mp4/bitstream_converter.cc",
     "mp4/bitstream_converter.h",
     "mp4/box_definitions.cc",
diff --git a/media/formats/common/opus_constants.cc b/media/formats/common/opus_constants.cc
new file mode 100644
index 0000000..3b6c4e6
--- /dev/null
+++ b/media/formats/common/opus_constants.cc
@@ -0,0 +1,35 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/formats/common/opus_constants.h"
+
+namespace media {
+
+// Ensure offset values are properly ordered.
+static_assert(OPUS_EXTRADATA_VERSION_OFFSET < OPUS_EXTRADATA_SIZE,
+              "Invalid opus version constant.");
+static_assert(OPUS_EXTRADATA_CHANNELS_OFFSET < OPUS_EXTRADATA_SIZE,
+              "Invalid opus channels constant.");
+static_assert(OPUS_EXTRADATA_SKIP_SAMPLES_OFFSET < OPUS_EXTRADATA_SIZE,
+              "Invalid opus skip samples constant.");
+static_assert(OPUS_EXTRADATA_SAMPLE_RATE_OFFSET < OPUS_EXTRADATA_SIZE,
+              "Invalid opus sample rate constant.");
+static_assert(OPUS_EXTRADATA_GAIN_OFFSET < OPUS_EXTRADATA_SIZE,
+              "Invalid opus gain constant.");
+static_assert(OPUS_EXTRADATA_CHANNEL_MAPPING_OFFSET < OPUS_EXTRADATA_SIZE,
+              "Invalid opus channel mapping offset");
+
+const uint8_t kOpusVorbisChannelMap[OPUS_MAX_VORBIS_CHANNELS]
+                                   [OPUS_MAX_VORBIS_CHANNELS] = {
+                                       {0},
+                                       {0, 1},
+                                       {0, 2, 1},
+                                       {0, 1, 2, 3},
+                                       {0, 4, 1, 2, 3},
+                                       {0, 4, 1, 2, 3, 5},
+                                       {0, 4, 1, 2, 3, 5, 6},
+                                       {0, 6, 1, 2, 3, 4, 5, 7},
+};
+
+}  // namespace media
diff --git a/media/filters/opus_constants.h b/media/formats/common/opus_constants.h
similarity index 70%
rename from media/filters/opus_constants.h
rename to media/formats/common/opus_constants.h
index efad0092..a8769c7 100644
--- a/media/filters/opus_constants.h
+++ b/media/formats/common/opus_constants.h
@@ -2,13 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MEDIA_FILTERS_OPUS_CONSTANTS_H_
-#define MEDIA_FILTERS_OPUS_CONSTANTS_H_
+#ifndef MEDIA_FORMATS_COMMON_OPUS_CONSTANTS_H_
+#define MEDIA_FORMATS_COMMON_OPUS_CONSTANTS_H_
 
 #include <stdint.h>
 
-#include "media/base/media_export.h"
-
 namespace media {
 
 // The Opus specification is part of IETF RFC 6716:
@@ -80,38 +78,10 @@
   OPUS_EXTRADATA_STREAM_MAP_OFFSET = OPUS_EXTRADATA_NUM_STREAMS_OFFSET + 2,
 };
 
-// Vorbis channel ordering for streams with >= 2 channels:
-// 2 Channels
-//   L, R
-// 3 Channels
-//   L, Center, R
-// 4 Channels
-//   Front L, Front R, Back L, Back R
-// 5 Channels
-//   Front L, Center, Front R, Back L, Back R
-// 6 Channels (5.1)
-//   Front L, Center, Front R, Back L, Back R, LFE
-// 7 channels (6.1)
-//   Front L, Front Center, Front R, Side L, Side R, Back Center, LFE
-// 8 Channels (7.1)
-//   Front L, Center, Front R, Side L, Side R, Back L, Back R, LFE
-//
-// Channel ordering information is taken from section 4.3.9 of the Vorbis I
-// Specification:
-// http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9
-MEDIA_EXPORT extern const uint8_t
-    kDefaultOpusChannelLayout[OPUS_MAX_CHANNELS_WITH_DEFAULT_LAYOUT];
-
-// These are the FFmpeg channel layouts expressed using the position of each
-// channel in the output stream from libopus.
-MEDIA_EXPORT extern const uint8_t
-    kFFmpegChannelDecodingLayouts[OPUS_MAX_VORBIS_CHANNELS]
-                                 [OPUS_MAX_VORBIS_CHANNELS];
-
 // Opus internal to Vorbis channel order mapping written in the header.
-extern const uint8_t
-    kOpusVorbisChannelMap[OPUS_MAX_VORBIS_CHANNELS][OPUS_MAX_VORBIS_CHANNELS];
+extern const uint8_t kOpusVorbisChannelMap[OPUS_MAX_VORBIS_CHANNELS]
+                                          [OPUS_MAX_VORBIS_CHANNELS];
 
 }  // namespace media
 
-#endif  // MEDIA_FILTERS_OPUS_CONSTANTS_H_
+#endif  // MEDIA_FORMATS_COMMON_OPUS_CONSTANTS_H_
diff --git a/media/formats/mp4/box_definitions.cc b/media/formats/mp4/box_definitions.cc
index f3761045..45a324a 100644
--- a/media/formats/mp4/box_definitions.cc
+++ b/media/formats/mp4/box_definitions.cc
@@ -10,10 +10,13 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/numerics/safe_math.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
 #include "media/base/media_switches.h"
 #include "media/base/video_types.h"
 #include "media/base/video_util.h"
+#include "media/formats/common/opus_constants.h"
 #include "media/formats/mp4/es_descriptor.h"
 #include "media/formats/mp4/rcheck.h"
 #include "media/media_buildflags.h"
@@ -1072,6 +1075,60 @@
   return true;
 }
 
+OpusSpecificBox::OpusSpecificBox()
+    : seek_preroll(base::TimeDelta::FromMilliseconds(80)),
+      codec_delay_in_frames(0) {}
+
+OpusSpecificBox::OpusSpecificBox(const OpusSpecificBox& other) = default;
+
+OpusSpecificBox::~OpusSpecificBox() = default;
+
+FourCC OpusSpecificBox::BoxType() const {
+  return FOURCC_DOPS;
+}
+
+bool OpusSpecificBox::Parse(BoxReader* reader) {
+  // Extradata must start with "OpusHead" magic.
+  extradata.insert(extradata.end(),
+                   {0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64});
+
+  // The opus specific box must be present and at least OPUS_EXTRADATA_SIZE - 8
+  // bytes in length. The -8 is for the missing "OpusHead" magic signature that
+  // is required at the start of the extradata we give to the codec.
+  const size_t headerless_extradata_size = reader->box_size() - reader->pos();
+  RCHECK(headerless_extradata_size >= OPUS_EXTRADATA_SIZE - extradata.size());
+  extradata.resize(extradata.size() + headerless_extradata_size);
+
+  int16_t gain_db;
+
+  RCHECK(reader->Read1(&extradata[OPUS_EXTRADATA_VERSION_OFFSET]));
+  RCHECK(reader->Read1(&extradata[OPUS_EXTRADATA_CHANNELS_OFFSET]));
+  RCHECK(reader->Read2(&codec_delay_in_frames /* PreSkip */));
+  RCHECK(reader->Read4(&sample_rate));
+  RCHECK(reader->Read2s(&gain_db));
+
+#if !defined(ARCH_CPU_LITTLE_ENDIAN)
+#error The code below assumes little-endianness.
+#endif
+
+  memcpy(&extradata[OPUS_EXTRADATA_SKIP_SAMPLES_OFFSET], &codec_delay_in_frames,
+         sizeof(codec_delay_in_frames));
+  memcpy(&extradata[OPUS_EXTRADATA_SAMPLE_RATE_OFFSET], &sample_rate,
+         sizeof(sample_rate));
+  memcpy(&extradata[OPUS_EXTRADATA_GAIN_OFFSET], &gain_db, sizeof(gain_db));
+
+  channel_count = extradata[OPUS_EXTRADATA_CHANNELS_OFFSET];
+
+  // Any remaining data is 1-byte data, so copy it over as is, there should
+  // only be a handful of these entries, so reading byte by byte is okay.
+  for (size_t i = OPUS_EXTRADATA_CHANNEL_MAPPING_OFFSET; i < extradata.size();
+       ++i) {
+    RCHECK(reader->Read1(&extradata[i]));
+  }
+
+  return true;
+}
+
 AudioSampleEntry::AudioSampleEntry()
     : format(FOURCC_NULL),
       data_reference_index(0),
@@ -1111,6 +1168,18 @@
     }
   }
 
+  if (format == FOURCC_OPUS ||
+      (format == FOURCC_ENCA && sinf.format.format == FOURCC_OPUS)) {
+    RCHECK_MEDIA_LOGGED(reader->ReadChild(&dops), reader->media_log(),
+                        "Failure parsing OpusSpecificBox (dOps)");
+    RCHECK_MEDIA_LOGGED(channelcount == dops.channel_count, reader->media_log(),
+                        "Opus AudioSampleEntry channel count mismatches "
+                        "OpusSpecificBox STREAMINFO channel count");
+    RCHECK_MEDIA_LOGGED(samplerate == dops.sample_rate, reader->media_log(),
+                        "Opus AudioSampleEntry sample rate mismatches "
+                        "OpusSpecificBox STREAMINFO channel count");
+  }
+
   // Read the FLACSpecificBox, even if CENC is signalled.
   if (format == FOURCC_FLAC ||
       (format == FOURCC_ENCA && sinf.format.format == FOURCC_FLAC)) {
diff --git a/media/formats/mp4/box_definitions.h b/media/formats/mp4/box_definitions.h
index d07ebbc..5c9d0442 100644
--- a/media/formats/mp4/box_definitions.h
+++ b/media/formats/mp4/box_definitions.h
@@ -315,6 +315,16 @@
   uint8_t bits_per_sample;
 };
 
+struct MEDIA_EXPORT OpusSpecificBox : Box {
+  DECLARE_BOX_METHODS(OpusSpecificBox);
+  std::vector<uint8_t> extradata;
+
+  base::TimeDelta seek_preroll;
+  uint16_t codec_delay_in_frames;
+  uint8_t channel_count;
+  uint32_t sample_rate;
+};
+
 struct MEDIA_EXPORT AudioSampleEntry : Box {
   DECLARE_BOX_METHODS(AudioSampleEntry);
 
@@ -327,6 +337,7 @@
   ProtectionSchemeInfo sinf;
   ElementaryStreamDescriptor esds;
   FlacSpecificBox dfla;
+  OpusSpecificBox dops;
 };
 
 struct MEDIA_EXPORT SampleDescription : Box {
diff --git a/media/formats/mp4/fourccs.h b/media/formats/mp4/fourccs.h
index 1b73d00c..37d8845 100644
--- a/media/formats/mp4/fourccs.h
+++ b/media/formats/mp4/fourccs.h
@@ -33,6 +33,7 @@
   FOURCC_CTTS = 0x63747473,
   FOURCC_DFLA = 0x64664c61,  // "dfLa"
   FOURCC_DINF = 0x64696e66,
+  FOURCC_DOPS = 0x644f7073,  // "dOps"
 #if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
   FOURCC_DVA1 = 0x64766131,
   FOURCC_DVAV = 0x64766176,
@@ -78,6 +79,7 @@
   FOURCC_MP4V = 0x6d703476,
   FOURCC_MVEX = 0x6d766578,
   FOURCC_MVHD = 0x6d766864,
+  FOURCC_OPUS = 0x4f707573,  // "Opus"
   FOURCC_PASP = 0x70617370,
   FOURCC_PDIN = 0x7064696e,
   FOURCC_PRFT = 0x70726674,
diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc
index 3517906..ac470a6 100644
--- a/media/formats/mp4/mp4_stream_parser.cc
+++ b/media/formats/mp4/mp4_stream_parser.cc
@@ -362,7 +362,7 @@
                                 ? entry.sinf.format.format
                                 : entry.format;
 
-      if (audio_format != FOURCC_FLAC &&
+      if (audio_format != FOURCC_OPUS && audio_format != FOURCC_FLAC &&
 #if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
           audio_format != FOURCC_AC3 && audio_format != FOURCC_EAC3 &&
 #endif
@@ -370,18 +370,26 @@
           audio_format != FOURCC_MHM1 &&
 #endif
           audio_format != FOURCC_MP4A) {
-        MEDIA_LOG(ERROR, media_log_) << "Unsupported audio format 0x"
-                                     << std::hex << entry.format
-                                     << " in stsd box.";
+        MEDIA_LOG(ERROR, media_log_)
+            << "Unsupported audio format 0x" << std::hex << entry.format
+            << " in stsd box.";
         return false;
       }
 
       AudioCodec codec = kUnknownAudioCodec;
       ChannelLayout channel_layout = CHANNEL_LAYOUT_NONE;
       int sample_per_second = 0;
+      int codec_delay_in_frames = 0;
+      base::TimeDelta seek_preroll;
       std::vector<uint8_t> extra_data;
-
-      if (audio_format == FOURCC_FLAC) {
+      if (audio_format == FOURCC_OPUS) {
+        codec = kCodecOpus;
+        channel_layout = GuessChannelLayout(entry.dops.channel_count);
+        sample_per_second = entry.dops.sample_rate;
+        codec_delay_in_frames = entry.dops.codec_delay_in_frames;
+        seek_preroll = entry.dops.seek_preroll;
+        extra_data = entry.dops.extradata;
+      } else if (audio_format == FOURCC_FLAC) {
         // FLAC-in-ISOBMFF does not use object type indication. |audio_format|
         // is sufficient for identifying FLAC codec.
         if (!has_flac_) {
@@ -481,7 +489,7 @@
       }
       audio_config.Initialize(codec, sample_format, channel_layout,
                               sample_per_second, extra_data, scheme,
-                              base::TimeDelta(), 0);
+                              seek_preroll, codec_delay_in_frames);
       if (codec == kCodecAAC)
         audio_config.disable_discard_decoder_delay();
 
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
index a89015a0..5308e95e 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
@@ -876,9 +876,8 @@
     schedule_task = true;
   } else {
     // This is a buffer queued from the client, with actual contents.  Decode.
-    const uint8_t* const data =
-        reinterpret_cast<const uint8_t*>(shm->memory()) +
-        decoder_current_bitstream_buffer_->bytes_used;
+    const uint8_t* const data = static_cast<const uint8_t*>(shm->memory()) +
+                                decoder_current_bitstream_buffer_->bytes_used;
     const size_t data_size =
         shm->size() - decoder_current_bitstream_buffer_->bytes_used;
     if (!AdvanceFrameFragment(data, data_size, &decoded_size)) {
diff --git a/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.cc b/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.cc
index 693a8cf..04e9ef5 100644
--- a/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.cc
@@ -395,8 +395,8 @@
   TRACE_EVENT0("jpeg", "DecodeTask");
 
   JpegParseResult parse_result;
-  if (!ParseJpegPicture(reinterpret_cast<const uint8_t*>(shm->memory()),
-                        shm->size(), &parse_result)) {
+  if (!ParseJpegPicture(static_cast<const uint8_t*>(shm->memory()), shm->size(),
+                        &parse_result)) {
     VLOGF(1) << "ParseJpegPicture failed";
     NotifyError(bitstream_buffer_id, PARSE_JPEG_FAILED);
     return;
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
index f87a7510..59ef085 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -416,7 +416,7 @@
     std::unique_ptr<BitstreamBufferRef> buffer) {
   DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
 
-  uint8_t* target_data = reinterpret_cast<uint8_t*>(buffer->shm->memory());
+  uint8_t* target_data = static_cast<uint8_t*>(buffer->shm->memory());
   size_t data_size = 0;
 
   if (!vaapi_wrapper_->DownloadAndDestroyCodedBuffer(
diff --git a/media/gpu/video_encode_accelerator_unittest.cc b/media/gpu/video_encode_accelerator_unittest.cc
index 86d61403..a544744 100644
--- a/media/gpu/video_encode_accelerator_unittest.cc
+++ b/media/gpu/video_encode_accelerator_unittest.cc
@@ -1687,7 +1687,7 @@
 
     if (quality_validator_) {
       scoped_refptr<DecoderBuffer> buffer(DecoderBuffer::CopyFrom(
-          reinterpret_cast<const uint8_t*>(shm->memory()),
+          static_cast<const uint8_t*>(shm->memory()),
           static_cast<int>(metadata.payload_size_bytes)));
       quality_validator_->AddDecodeBuffer(buffer);
     }
diff --git a/media/gpu/vt_video_encode_accelerator_mac.cc b/media/gpu/vt_video_encode_accelerator_mac.cc
index 3713efc..cac631d9 100644
--- a/media/gpu/vt_video_encode_accelerator_mac.cc
+++ b/media/gpu/vt_video_encode_accelerator_mac.cc
@@ -450,7 +450,7 @@
   size_t used_buffer_size = 0;
   const bool copy_rv = video_toolbox::CopySampleBufferToAnnexBBuffer(
       encode_output->sample_buffer.get(), keyframe, buffer_ref->size,
-      reinterpret_cast<char*>(buffer_ref->shm->memory()), &used_buffer_size);
+      static_cast<char*>(buffer_ref->shm->memory()), &used_buffer_size);
   if (!copy_rv) {
     DLOG(ERROR) << "Cannot copy output from SampleBuffer to AnnexBBuffer.";
     used_buffer_size = 0;
diff --git a/media/muxers/BUILD.gn b/media/muxers/BUILD.gn
index db70d6c..0e1b918 100644
--- a/media/muxers/BUILD.gn
+++ b/media/muxers/BUILD.gn
@@ -18,7 +18,7 @@
   deps = [
     "//base",
     "//media/base",
-    "//media/filters",
+    "//media/formats",
     "//skia",
     "//third_party/libwebm",
     "//ui/gfx/geometry",
diff --git a/media/muxers/webm_muxer.cc b/media/muxers/webm_muxer.cc
index e281d6c3..79b67039 100644
--- a/media/muxers/webm_muxer.cc
+++ b/media/muxers/webm_muxer.cc
@@ -11,7 +11,7 @@
 #include "media/base/audio_parameters.h"
 #include "media/base/limits.h"
 #include "media/base/video_frame.h"
-#include "media/filters/opus_constants.h"
+#include "media/formats/common/opus_constants.h"
 
 namespace media {
 
diff --git a/media/test/BUILD.gn b/media/test/BUILD.gn
index 5b541e59..d19d17f 100644
--- a/media/test/BUILD.gn
+++ b/media/test/BUILD.gn
@@ -163,6 +163,7 @@
   "WEBM_VP9",
   "WEBM_OPUS_VP9",
   "MP4_FLAC",
+  "MP4_OPUS",
   "MP3",
 
   # See below for additional variants depending on build configuration.
diff --git a/media/test/data/README b/media/test/data/README
index 4347682..88b7d8874 100644
--- a/media/test/data/README
+++ b/media/test/data/README
@@ -32,10 +32,6 @@
 bear-vp8a.webm - WebM VP8 video with alpha channel.
 bear-vp8a-odd-dimensions.webm - WebM VP8 video with alpha channel and odd dimensions.
 bear-opus.webm - Opus Audio only WebM file.
-bear-opus-end-trimming.webm - File to test end trimming. It has one byte
-                              artificially added so that there is maximum
-                              padding at the end. It is an Opus Audio only WebM
-                              file.
 no_streams.webm - Header, Info, & Tracks element from bear-320x240.webm slightly corrupted so it looks
                   like there are no tracks.
 nonzero-start-time.webm - Has the same headers as bear-320x240.webm but the first cluster of this file
@@ -409,3 +405,9 @@
     ffmpeg -f lavfi -i "sine=frequency=300:sample_rate=48000" -t 10 -c:v libvpx a300hz.webm
     ffmpeg -f lavfi -i "sine=frequency=500:sample_rate=48000" -t 5 -c:v libvpx a500hz.webm
     ffmpeg -i red.webm -i green.webm -i blue.webm -i a300hz.webm -i a500hz.webm -map 0 -map 1 -map 2 -map 3 -map 4  multitrack-3video-2audio.webm
+
+// Opus pre-skip and end-trimming test clips.
+// https://people.xiph.org/~greg/opus_testvectors/
+opus-trimming-test.mp4
+opus-trimming-test.ogg
+opus-trimming-test.webm
diff --git a/media/test/data/bear-opus-end-trimming.webm b/media/test/data/bear-opus-end-trimming.webm
deleted file mode 100644
index c198148..0000000
--- a/media/test/data/bear-opus-end-trimming.webm
+++ /dev/null
Binary files differ
diff --git a/media/test/data/bear-opus.mp4 b/media/test/data/bear-opus.mp4
new file mode 100644
index 0000000..c10b2ca
--- /dev/null
+++ b/media/test/data/bear-opus.mp4
Binary files differ
diff --git a/media/test/data/opus-trimming-test.mp4 b/media/test/data/opus-trimming-test.mp4
new file mode 100644
index 0000000..7fd30fa
--- /dev/null
+++ b/media/test/data/opus-trimming-test.mp4
Binary files differ
diff --git a/media/test/data/sfx-opus_frag.mp4 b/media/test/data/sfx-opus_frag.mp4
new file mode 100644
index 0000000..902a090
--- /dev/null
+++ b/media/test/data/sfx-opus_frag.mp4
Binary files differ
diff --git a/media/test/mock_media_source.cc b/media/test/mock_media_source.cc
index 5244ce7..e225db6 100644
--- a/media/test/mock_media_source.cc
+++ b/media/test/mock_media_source.cc
@@ -97,6 +97,14 @@
   return std::move(owned_chunk_demuxer_);
 }
 
+void MockMediaSource::SetAppendWindow(base::TimeDelta timestamp_offset,
+                                      base::TimeDelta append_window_start,
+                                      base::TimeDelta append_window_end) {
+  last_timestamp_offset_ = timestamp_offset;
+  append_window_start_ = append_window_start;
+  append_window_end_ = append_window_end;
+}
+
 void MockMediaSource::Seek(base::TimeDelta seek_time,
                            size_t new_position,
                            size_t seek_append_size) {
@@ -127,7 +135,7 @@
 
   bool success = chunk_demuxer_->AppendData(
       kSourceId, file_data_->data() + current_position_, size,
-      base::TimeDelta(), kInfiniteDuration, &last_timestamp_offset_);
+      append_window_start_, append_window_end_, &last_timestamp_offset_);
   current_position_ += size;
 
   ASSERT_EQ(expect_append_success_, success);
@@ -144,8 +152,8 @@
                                    int size) {
   CHECK(!chunk_demuxer_->IsParsingMediaSegment(kSourceId));
   bool success =
-      chunk_demuxer_->AppendData(kSourceId, pData, size, base::TimeDelta(),
-                                 kInfiniteDuration, &timestamp_offset);
+      chunk_demuxer_->AppendData(kSourceId, pData, size, append_window_start_,
+                                 append_window_end_, &timestamp_offset);
   last_timestamp_offset_ = timestamp_offset;
   return success;
 }
diff --git a/media/test/mock_media_source.h b/media/test/mock_media_source.h
index e7ea563a..3ff7361c 100644
--- a/media/test/mock_media_source.h
+++ b/media/test/mock_media_source.h
@@ -47,6 +47,10 @@
     do_eos_after_next_append_ = flag;
   }
 
+  void SetAppendWindow(base::TimeDelta timestamp_offset,
+                       base::TimeDelta append_window_start,
+                       base::TimeDelta append_window_end);
+
   void Seek(base::TimeDelta seek_time,
             size_t new_position,
             size_t seek_append_size);
@@ -99,6 +103,8 @@
   PipelineStatusCB demuxer_failure_cb_;
   Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb_;
   base::TimeDelta last_timestamp_offset_;
+  base::TimeDelta append_window_start_;
+  base::TimeDelta append_window_end_ = kInfiniteDuration;
   bool do_eos_after_next_append_ = false;
   bool expect_append_success_ = true;
 
diff --git a/media/test/pipeline_integration_fuzzertest.cc b/media/test/pipeline_integration_fuzzertest.cc
index 673a292..9ea4e897ae 100644
--- a/media/test/pipeline_integration_fuzzertest.cc
+++ b/media/test/pipeline_integration_fuzzertest.cc
@@ -36,6 +36,7 @@
   MP4_AV1,
 #endif
   MP4_FLAC,
+  MP4_OPUS,
   MP3,
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
   ADTS,
@@ -71,6 +72,8 @@
 #endif  // BUILDFLAG(ENABLE_AV1_DECODER)
     case MP4_FLAC:
       return "audio/mp4; codecs=\"flac\"";
+    case MP4_OPUS:
+      return "audio/mp4; codecs=\"opus\"";
     case MP3:
       return "audio/mpeg";
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index 1d7d445..ba23def 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -111,6 +111,7 @@
 const char kMP4VideoVP9[] =
     "video/mp4; codecs=\"vp09.00.10.08.01.02.02.02.00\"";
 const char kMP4AudioFlac[] = "audio/mp4; codecs=\"flac\"";
+const char kMP4AudioOpus[] = "audio/mp4; codecs=\"opus\"";
 const char kMP3[] = "audio/mpeg";
 #if BUILDFLAG(ENABLE_AV1_DECODER)
 const char kMP4AV1[] = "video/mp4; codecs=\"av01.0.04M.08\"";
@@ -132,7 +133,6 @@
 const int kAppendTimeMs = kAppendTimeSec * 1000;
 const int k320WebMFileDurationMs = 2736;
 const int k640WebMFileDurationMs = 2762;
-const int kOpusEndTrimmingWebMFileDurationMs = 2741;
 const int kVP9WebMFileDurationMs = 2736;
 const int kVP8AWebMFileDurationMs = 2734;
 
@@ -737,6 +737,9 @@
     // FLAC in MP4
     {"sfx-flac_frag.mp4", kMP4AudioFlac, kAppendWholeFile, 288},
 
+    // Opus in MP4
+    {"sfx-opus_frag.mp4", kMP4AudioOpus, kAppendWholeFile, 301},
+
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
     // AAC in ADTS
     {"bear-audio-main-aac.aac", kADTS, kAppendWholeFile, 2773},
@@ -1122,7 +1125,7 @@
   ASSERT_TRUE(WaitUntilOnEnded());
   EXPECT_HASH_EQ(kOpusEndTrimmingHash_2, GetAudioHash());
 
-  // Seek somewhere outside of the pre-skip / end-trim section, demxuer should
+  // Seek somewhere outside of the pre-skip / end-trim section, demuxer should
   // correctly preroll enough to accurately decode this segment.
   ASSERT_TRUE(Seek(base::TimeDelta::FromMilliseconds(6360)));
   Play();
@@ -1144,7 +1147,7 @@
   ASSERT_TRUE(WaitUntilOnEnded());
   EXPECT_HASH_EQ(kOpusEndTrimmingHash_2, GetAudioHash());
 
-  // Seek somewhere outside of the pre-skip / end-trim section, demxuer should
+  // Seek somewhere outside of the pre-skip / end-trim section, demuxer should
   // correctly preroll enough to accurately decode this segment.
   ASSERT_TRUE(Seek(base::TimeDelta::FromMilliseconds(6360)));
   Play();
@@ -1152,6 +1155,32 @@
   EXPECT_HASH_EQ(kOpusEndTrimmingHash_3, GetAudioHash());
 }
 
+TEST_F(PipelineIntegrationTest, BasicPlaybackOpusMp4TrimmingHashed) {
+  ASSERT_EQ(PIPELINE_OK, Start("opus-trimming-test.mp4", kHashed));
+
+  Play();
+
+  // TODO(dalecurtis): The test clip currently does not have the edit list
+  // entries required to achieve correctness here. Delete this comment and
+  // uncomment the EXPECT_HASH_EQ lines when https://crbug.com/876544 is fixed.
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+  // EXPECT_HASH_EQ(kOpusEndTrimmingHash_1, GetAudioHash());
+
+  // Seek within the pre-skip section, this should not cause a beep.
+  ASSERT_TRUE(Seek(base::TimeDelta::FromSeconds(1)));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  // EXPECT_HASH_EQ(kOpusEndTrimmingHash_2, GetAudioHash());
+
+  // Seek somewhere outside of the pre-skip / end-trim section, demuxer should
+  // correctly preroll enough to accurately decode this segment.
+  ASSERT_TRUE(Seek(base::TimeDelta::FromMilliseconds(6360)));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  // EXPECT_HASH_EQ(kOpusEndTrimmingHash_3, GetAudioHash());
+}
+
 TEST_P(MSEPipelineIntegrationTest, BasicPlaybackOpusWebmTrimmingHashed) {
   MockMediaSource source("opus-trimming-test.webm", kOpusAudioOnlyWebM,
                          kAppendWholeFile);
@@ -1182,14 +1211,55 @@
   EXPECT_HASH_EQ(kOpusEndTrimmingHash_3, GetAudioHash());
 }
 
+TEST_P(MSEPipelineIntegrationTest, BasicPlaybackOpusMp4TrimmingHashed) {
+  MockMediaSource source("opus-trimming-test.mp4", kMP4AudioOpus,
+                         kAppendWholeFile);
+
+  // TODO(dalecurtis): The test clip currently does not have the edit list
+  // entries required to achieve correctness here, so we're manually specifying
+  // the edits using append window trimming.
+  //
+  // It's unclear if MSE actually supports edit list features required to
+  // achieve correctness either. Delete this comment and remove the manual
+  // SetAppendWindow() if/when https://crbug.com/876544 is fixed.
+  source.SetAppendWindow(base::TimeDelta(), base::TimeDelta(),
+                         base::TimeDelta::FromMicroseconds(12720021));
+
+  EXPECT_EQ(PIPELINE_OK,
+            StartPipelineWithMediaSource(&source, kHashed, nullptr));
+  source.EndOfStream();
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusEndTrimmingHash_1, GetAudioHash());
+
+  // Seek within the pre-skip section, this should not cause a beep.
+  base::TimeDelta seek_time = base::TimeDelta::FromSeconds(1);
+  source.Seek(seek_time);
+  ASSERT_TRUE(Seek(seek_time));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusEndTrimmingHash_2, GetAudioHash());
+
+  // Seek somewhere outside of the pre-skip / end-trim section, demuxer should
+  // correctly preroll enough to accurately decode this segment.
+  seek_time = base::TimeDelta::FromMilliseconds(6360);
+  source.Seek(seek_time);
+  ASSERT_TRUE(Seek(seek_time));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusEndTrimmingHash_3, GetAudioHash());
+}
+
 TEST_F(PipelineIntegrationTest, BasicPlaybackOpusWebmHashed_MonoOutput) {
   ASSERT_EQ(PIPELINE_OK,
             Start("bunny-opus-intensity-stereo.webm", kHashed | kMonoOutput));
 
-  // File should have stereo output, which we know to be encoded using
-  // "phase intensity". Downmixing such files to MONO produces artifcats unless
-  // the decoder performs the downmix, which disables "phase inversion".
-  // See http://crbug.com/806219
+  // File should have stereo output, which we know to be encoded using "phase
+  // intensity". Downmixing such files to MONO produces artifacts unless the
+  // decoder performs the downmix, which disables "phase inversion". See
+  // http://crbug.com/806219
   AudioDecoderConfig config =
       demuxer_->GetFirstStream(DemuxerStream::AUDIO)->audio_decoder_config();
   ASSERT_EQ(config.channel_layout(), CHANNEL_LAYOUT_STEREO);
@@ -1225,6 +1295,33 @@
   EXPECT_HASH_EQ(kOpusSmallCodecDelayHash_2, GetAudioHash());
 }
 
+TEST_F(PipelineIntegrationTest, BasicPlaybackOpusMp4PrerollExceedsCodecDelay) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-opus.mp4", kHashed));
+
+  AudioDecoderConfig config =
+      demuxer_->GetFirstStream(DemuxerStream::AUDIO)->audio_decoder_config();
+
+  // Verify that this file's preroll is not eclipsed by the codec delay so we
+  // can detect when preroll is not properly performed.
+  base::TimeDelta codec_delay = base::TimeDelta::FromSecondsD(
+      static_cast<double>(config.codec_delay()) / config.samples_per_second());
+  ASSERT_GT(config.seek_preroll(), codec_delay);
+
+  // TODO(dalecurtis): The test clip currently does not have the edit list
+  // entries required to achieve correctness here. Delete this comment and
+  // uncomment the EXPECT_HASH_EQ lines when https://crbug.com/876544 is fixed.
+
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  // EXPECT_HASH_EQ(kOpusSmallCodecDelayHash_1, GetAudioHash());
+
+  // Seek halfway through the file to invoke seek preroll.
+  ASSERT_TRUE(Seek(base::TimeDelta::FromSecondsD(1.414)));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  // EXPECT_HASH_EQ(kOpusSmallCodecDelayHash_2, GetAudioHash());
+}
+
 TEST_P(MSEPipelineIntegrationTest, BasicPlaybackOpusPrerollExceedsCodecDelay) {
   MockMediaSource source("bear-opus.webm", kOpusAudioOnlyWebM,
                          kAppendWholeFile);
@@ -1254,6 +1351,46 @@
   EXPECT_HASH_EQ(kOpusSmallCodecDelayHash_2, GetAudioHash());
 }
 
+TEST_P(MSEPipelineIntegrationTest,
+       BasicPlaybackOpusMp4PrerollExceedsCodecDelay) {
+  MockMediaSource source("bear-opus.mp4", kMP4AudioOpus, kAppendWholeFile);
+
+  // TODO(dalecurtis): The test clip currently does not have the edit list
+  // entries required to achieve correctness here, so we're manually specifying
+  // the edits using append window trimming.
+  //
+  // It's unclear if MSE actually supports edit list features required to
+  // achieve correctness either. Delete this comment and remove the manual
+  // SetAppendWindow() if/when https://crbug.com/876544 is fixed.
+  source.SetAppendWindow(base::TimeDelta(), base::TimeDelta(),
+                         base::TimeDelta::FromMicroseconds(2740834));
+
+  EXPECT_EQ(PIPELINE_OK,
+            StartPipelineWithMediaSource(&source, kHashed, nullptr));
+  source.EndOfStream();
+
+  AudioDecoderConfig config =
+      demuxer_->GetFirstStream(DemuxerStream::AUDIO)->audio_decoder_config();
+
+  // Verify that this file's preroll is not eclipsed by the codec delay so we
+  // can detect when preroll is not properly performed.
+  base::TimeDelta codec_delay = base::TimeDelta::FromSecondsD(
+      static_cast<double>(config.codec_delay()) / config.samples_per_second());
+  ASSERT_GT(config.seek_preroll(), codec_delay);
+
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusSmallCodecDelayHash_1, GetAudioHash());
+
+  // Seek halfway through the file to invoke seek preroll.
+  base::TimeDelta seek_time = base::TimeDelta::FromSecondsD(1.414);
+  source.Seek(seek_time);
+  ASSERT_TRUE(Seek(seek_time));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusSmallCodecDelayHash_2, GetAudioHash());
+}
+
 TEST_F(PipelineIntegrationTest, BasicPlaybackLive) {
   ASSERT_EQ(PIPELINE_OK, Start("bear-320x240-live.webm", kHashed));
 
@@ -1443,52 +1580,6 @@
   Stop();
 }
 
-TEST_P(MSEPipelineIntegrationTest, BasicPlayback_Opus_WebM) {
-  MockMediaSource source("bear-opus-end-trimming.webm", kOpusAudioOnlyWebM,
-                         kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(kOpusEndTrimmingWebMFileDurationMs,
-            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-  source.Shutdown();
-  Stop();
-}
-
-// Flaky. http://crbug.com/304776
-TEST_P(MSEPipelineIntegrationTest, DISABLED_Opus_Seeking_WebM) {
-  MockMediaSource source("bear-opus-end-trimming.webm", kOpusAudioOnlyWebM,
-                         kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK,
-            StartPipelineWithMediaSource(&source, kHashed, nullptr));
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(kOpusEndTrimmingWebMFileDurationMs,
-            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  base::TimeDelta start_seek_time = base::TimeDelta::FromMilliseconds(1000);
-  base::TimeDelta seek_time = base::TimeDelta::FromMilliseconds(2000);
-
-  Play();
-  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(start_seek_time));
-  source.Seek(seek_time, 0x1D5, 34017);
-  source.EndOfStream();
-  ASSERT_TRUE(Seek(seek_time));
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-
-  EXPECT_HASH_EQ("0.76,0.20,-0.82,-0.58,-1.29,-0.29,", GetAudioHash());
-
-  source.Shutdown();
-  Stop();
-}
-
 TEST_P(MSEPipelineIntegrationTest, ConfigChange_WebM) {
   MockMediaSource source("bear-320x240-16x9-aspect.webm", kWebM,
                          kAppendWholeFile);
@@ -2726,13 +2817,6 @@
                                  0x1C896, 65536));
 }
 
-// Verify that Opus audio in WebM containers can be played back.
-TEST_F(PipelineIntegrationTest, BasicPlayback_AudioOnly_Opus_WebM) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-opus-end-trimming.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
 TEST_F(PipelineIntegrationTest,
        BasicPlayback_AudioOnly_Opus_4ch_ChannelMapping2_WebM) {
   ASSERT_EQ(
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
index 4a55c2d..1b3fd95 100644
--- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -39,8 +39,8 @@
   "//services/proxy_resolver/public/cpp/typemaps.gni",
   "//services/resource_coordinator/public/cpp/typemaps.gni",
   "//services/service_manager/public/cpp/typemaps.gni",
-  "//services/ui/public/interfaces/cursor/typemaps.gni",
-  "//services/ui/public/interfaces/ime/typemaps.gni",
+  "//services/ws/public/mojom/cursor/typemaps.gni",
+  "//services/ws/public/mojom/ime/typemaps.gni",
   "//services/viz/privileged/cpp/typemaps.gni",
   "//services/viz/privileged/interfaces/compositing/typemaps.gni",
   "//services/viz/public/cpp/compositing/typemaps.gni",
diff --git a/net/dns/BUILD.gn b/net/dns/BUILD.gn
index 45d1755..f5c369d 100644
--- a/net/dns/BUILD.gn
+++ b/net/dns/BUILD.gn
@@ -53,6 +53,7 @@
       "host_resolver_impl.cc",
       "host_resolver_proc.cc",
       "host_resolver_proc.h",
+      "host_resolver_source.h",
       "mapped_host_resolver.cc",
       "notify_watcher_mac.cc",
       "notify_watcher_mac.h",
diff --git a/net/dns/host_cache.h b/net/dns/host_cache.h
index ce2cb68..45a3ee2e 100644
--- a/net/dns/host_cache.h
+++ b/net/dns/host_cache.h
@@ -22,11 +22,12 @@
 #include "net/base/expiring_cache.h"
 #include "net/base/net_export.h"
 #include "net/dns/dns_util.h"
+#include "net/dns/host_resolver_source.h"
 
 namespace base {
 class ListValue;
 class TickClock;
-}
+}  // namespace base
 
 namespace net {
 
@@ -34,28 +35,35 @@
 class NET_EXPORT HostCache {
  public:
   struct Key {
-    Key(const std::string& hostname, AddressFamily address_family,
-        HostResolverFlags host_resolver_flags)
+    Key(const std::string& hostname,
+        AddressFamily address_family,
+        HostResolverFlags host_resolver_flags,
+        HostResolverSource host_resolver_source = HostResolverSource::ANY)
         : hostname(hostname),
           address_family(address_family),
-          host_resolver_flags(host_resolver_flags) {}
+          host_resolver_flags(host_resolver_flags),
+          host_resolver_source(host_resolver_source) {}
 
     Key()
-        : address_family(ADDRESS_FAMILY_UNSPECIFIED), host_resolver_flags(0) {}
+        : address_family(ADDRESS_FAMILY_UNSPECIFIED),
+          host_resolver_flags(0),
+          host_resolver_source(HostResolverSource::ANY) {}
 
     bool operator<(const Key& other) const {
       // The order of comparisons of |Key| fields is arbitrary, thus
       // |address_family| and |host_resolver_flags| are compared before
       // |hostname| under assumption that integer comparisons are faster than
       // string comparisons.
-      return std::tie(address_family, host_resolver_flags, hostname) <
+      return std::tie(address_family, host_resolver_flags, hostname,
+                      host_resolver_source) <
              std::tie(other.address_family, other.host_resolver_flags,
-                      other.hostname);
+                      other.hostname, other.host_resolver_source);
     }
 
     std::string hostname;
     AddressFamily address_family;
     HostResolverFlags host_resolver_flags;
+    HostResolverSource host_resolver_source;
   };
 
   struct NET_EXPORT EntryStaleness {
diff --git a/net/dns/host_resolver.cc b/net/dns/host_resolver.cc
index 9211661..3880c7c 100644
--- a/net/dns/host_resolver.cc
+++ b/net/dns/host_resolver.cc
@@ -198,26 +198,44 @@
 HostResolver::RequestInfoToResolveHostParameters(
     const HostResolver::RequestInfo& request_info,
     RequestPriority priority) {
+  // Flag that should only be set internally, not used in input.
+  DCHECK(!(request_info.host_resolver_flags() &
+           HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6));
+
   ResolveHostParameters parameters;
 
   parameters.dns_query_type =
       AddressFamilyToDnsQueryType(request_info.address_family());
   parameters.initial_priority = priority;
+  parameters.source = FlagsToSource(request_info.host_resolver_flags());
+  parameters.allow_cached_response = request_info.allow_cached_response();
+  parameters.include_canonical_name =
+      request_info.host_resolver_flags() & HOST_RESOLVER_CANONNAME;
+  parameters.loopback_only =
+      request_info.host_resolver_flags() & HOST_RESOLVER_LOOPBACK_ONLY;
   parameters.is_speculative = request_info.is_speculative();
 
   return parameters;
 }
 
 // static
+HostResolverSource HostResolver::FlagsToSource(HostResolverFlags flags) {
+  if (flags & HOST_RESOLVER_SYSTEM_ONLY)
+    return HostResolverSource::SYSTEM;
+
+  return HostResolverSource::ANY;
+}
+
+// static
 HostResolverFlags HostResolver::ParametersToHostResolverFlags(
     const ResolveHostParameters& parameters) {
   HostResolverFlags flags = 0;
-  if (parameters.include_canonical_name) {
+  if (parameters.source == HostResolverSource::SYSTEM)
+    flags |= HOST_RESOLVER_SYSTEM_ONLY;
+  if (parameters.include_canonical_name)
     flags |= HOST_RESOLVER_CANONNAME;
-  }
-  if (parameters.loopback_only) {
+  if (parameters.loopback_only)
     flags |= HOST_RESOLVER_LOOPBACK_ONLY;
-  }
   return flags;
 }
 
diff --git a/net/dns/host_resolver.h b/net/dns/host_resolver.h
index bcbd11a..0f9a85b 100644
--- a/net/dns/host_resolver.h
+++ b/net/dns/host_resolver.h
@@ -20,6 +20,7 @@
 #include "net/base/request_priority.h"
 #include "net/dns/dns_config_service.h"
 #include "net/dns/host_cache.h"
+#include "net/dns/host_resolver_source.h"
 
 namespace base {
 class Value;
@@ -197,6 +198,16 @@
     // The initial net priority for the host resolution request.
     RequestPriority initial_priority = RequestPriority::DEFAULT_PRIORITY;
 
+    // The source to use for resolved addresses. Default allows the resolver to
+    // pick an appropriate source. Only affects use of big external sources (eg
+    // calling the system for resolution or using DNS). Even if a source is
+    // specified, results can still come from cache, resolving "localhost" or
+    // IP literals, etc.
+    HostResolverSource source = HostResolverSource::ANY;
+
+    // If |false|, results will not come from the host cache.
+    bool allow_cached_response = true;
+
     // If |true|, requests that the resolver include AddressList::canonical_name
     // in the results. If the resolver can do so without significant
     // performance impact, canonical_name may still be included even if
@@ -358,6 +369,7 @@
   static ResolveHostParameters RequestInfoToResolveHostParameters(
       const RequestInfo& request_info,
       RequestPriority priority);
+  static HostResolverSource FlagsToSource(HostResolverFlags flags);
   static HostResolverFlags ParametersToHostResolverFlags(
       const ResolveHostParameters& parameters);
 
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index 2e51608..679d2e7b 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -619,28 +619,11 @@
               const HostPortPair& request_host,
               const base::Optional<ResolveHostParameters>& optional_parameters,
               base::WeakPtr<HostResolverImpl> resolver)
-      : RequestImpl(source_net_log,
-                    request_host,
-                    optional_parameters,
-                    0 /* host_resolver_flags */,
-                    true /* allow_cached_response */,
-                    resolver) {}
-
-  // Overload for use by the legacy Resolve() API. Has more advanced parameters
-  // not yet supported by the CreateRequest() API.
-  RequestImpl(const NetLogWithSource& source_net_log,
-              const HostPortPair& request_host,
-              const base::Optional<ResolveHostParameters>& optional_parameters,
-              HostResolverFlags host_resolver_flags,
-              bool allow_cached_response,
-              base::WeakPtr<HostResolverImpl> resolver)
       : source_net_log_(source_net_log),
         request_host_(request_host),
-        allow_cached_response_(allow_cached_response),
         parameters_(optional_parameters ? optional_parameters.value()
                                         : ResolveHostParameters()),
-        host_resolver_flags_(host_resolver_flags |
-                             ParametersToHostResolverFlags(parameters_)),
+        host_resolver_flags_(ParametersToHostResolverFlags(parameters_)),
         priority_(parameters_.initial_priority),
         job_(nullptr),
         resolver_(resolver),
@@ -727,8 +710,6 @@
 
   const HostPortPair& request_host() const { return request_host_; }
 
-  bool allow_cached_response() const { return allow_cached_response_; }
-
   const ResolveHostParameters& parameters() const { return parameters_; }
 
   HostResolverFlags host_resolver_flags() const { return host_resolver_flags_; }
@@ -752,7 +733,6 @@
   const NetLogWithSource source_net_log_;
 
   const HostPortPair request_host_;
-  const bool allow_cached_response_;
   const ResolveHostParameters parameters_;
   const HostResolverFlags host_resolver_flags_;
 
@@ -1374,7 +1354,6 @@
         priority_tracker_(priority),
         proc_task_runner_(std::move(proc_task_runner)),
         had_non_speculative_request_(false),
-        had_dns_config_(false),
         num_occupied_job_slots_(0),
         dns_task_error_(OK),
         tick_clock_(tick_clock),
@@ -1627,8 +1606,6 @@
 
     net_log_.AddEvent(NetLogEventType::HOST_RESOLVER_IMPL_JOB_STARTED);
 
-    had_dns_config_ = resolver_->HaveDnsConfig();
-
     start_time_ = tick_clock_->NowTicks();
     base::TimeDelta queue_time = start_time_ - creation_time_;
     base::TimeDelta queue_time_after_change =
@@ -1638,16 +1615,28 @@
     DNS_HISTOGRAM_BY_PRIORITY("Net.DNS.JobQueueTimeAfterChange", priority(),
                               queue_time_after_change);
 
-    bool system_only =
-        (key_.host_resolver_flags & HOST_RESOLVER_SYSTEM_ONLY) != 0;
+    switch (key_.host_resolver_source) {
+      case HostResolverSource::ANY:
+        if (resolver_->HaveDnsConfig() &&
+            !ResemblesMulticastDNSName(key_.hostname)) {
+          StartDnsTask();
+        } else {
+          StartProcTask();
+        }
+        break;
+      case HostResolverSource::SYSTEM:
+        StartProcTask();
+        break;
+      case HostResolverSource::DNS:
+        // DNS source should not be requested unless the resolver is configured
+        // to handle it.
+        DCHECK(resolver_->HaveDnsConfig());
+
+        StartDnsTask();
+        break;
+    }
 
     // Caution: Job::Start must not complete synchronously.
-    if (!system_only && had_dns_config_ &&
-        !ResemblesMulticastDNSName(key_.hostname)) {
-      StartDnsTask();
-    } else {
-      StartProcTask();
-    }
   }
 
   // TODO(szym): Since DnsTransaction does not consume threads, we can increase
@@ -1998,9 +1987,6 @@
 
   bool had_non_speculative_request_;
 
-  // Distinguishes measurements taken while DnsClient was fully configured.
-  bool had_dns_config_;
-
   // Number of slots occupied by this Job in resolver's PrioritizedDispatcher.
   unsigned num_occupied_job_slots_;
 
@@ -2158,7 +2144,6 @@
   auto request = std::make_unique<RequestImpl>(
       source_net_log, info.host_port_pair(),
       RequestInfoToResolveHostParameters(info, priority),
-      info.host_resolver_flags(), info.allow_cached_response(),
       weak_ptr_factory_.GetWeakPtr());
   auto wrapped_request =
       std::make_unique<LegacyRequestImpl>(std::move(request));
@@ -2188,9 +2173,9 @@
   Key key;
   int rv = ResolveLocally(
       info.host_port_pair(), AddressFamilyToDnsQueryType(info.address_family()),
-      info.host_resolver_flags(), info.allow_cached_response(),
-      false /* allow_stale */, nullptr /* stale_info */, source_net_log,
-      addresses, &key);
+      FlagsToSource(info.host_resolver_flags()), info.host_resolver_flags(),
+      info.allow_cached_response(), false /* allow_stale */,
+      nullptr /* stale_info */, source_net_log, addresses, &key);
 
   LogFinishRequest(source_net_log, rv);
   return rv;
@@ -2211,8 +2196,9 @@
   Key key;
   int rv = ResolveLocally(
       info.host_port_pair(), AddressFamilyToDnsQueryType(info.address_family()),
-      info.host_resolver_flags(), info.allow_cached_response(),
-      true /* allow_stale */, stale_info, source_net_log, addresses, &key);
+      FlagsToSource(info.host_resolver_flags()), info.host_resolver_flags(),
+      info.allow_cached_response(), true /* allow_stale */, stale_info,
+      source_net_log, addresses, &key);
   LogFinishRequest(source_net_log, rv);
   return rv;
 }
@@ -2349,9 +2335,9 @@
   Key key;
   int rv = ResolveLocally(
       request->request_host(), request->parameters().dns_query_type,
-      request->host_resolver_flags(), request->allow_cached_response(),
-      false /* allow_stale */, nullptr /* stale_info */,
-      request->source_net_log(), &addresses, &key);
+      request->parameters().source, request->host_resolver_flags(),
+      request->parameters().allow_cached_response, false /* allow_stale */,
+      nullptr /* stale_info */, request->source_net_log(), &addresses, &key);
   if (rv == OK && !request->parameters().is_speculative) {
     request->set_address_results(
         EnsurePortOnAddressList(addresses, request->request_host().port()));
@@ -2372,6 +2358,7 @@
 
 int HostResolverImpl::ResolveLocally(const HostPortPair& host,
                                      DnsQueryType dns_query_type,
+                                     HostResolverSource source,
                                      HostResolverFlags flags,
                                      bool allow_cache,
                                      bool allow_stale,
@@ -2391,7 +2378,7 @@
 
   // Build a key that identifies the request in the cache and in the
   // outstanding jobs map.
-  *key = GetEffectiveKeyForRequest(host.host(), dns_query_type, flags,
+  *key = GetEffectiveKeyForRequest(host.host(), dns_query_type, source, flags,
                                    ip_address_ptr, source_net_log);
 
   DCHECK(allow_stale == !!stale_info);
@@ -2619,6 +2606,7 @@
 HostResolverImpl::Key HostResolverImpl::GetEffectiveKeyForRequest(
     const std::string& hostname,
     DnsQueryType dns_query_type,
+    HostResolverSource source,
     HostResolverFlags flags,
     const IPAddress* ip_address,
     const NetLogWithSource& net_log) {
@@ -2639,7 +2627,7 @@
     effective_flags |= HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
   }
 
-  return Key(hostname, effective_address_family, effective_flags);
+  return Key(hostname, effective_address_family, effective_flags, source);
 }
 
 bool HostResolverImpl::IsIPv6Reachable(const NetLogWithSource& net_log) {
diff --git a/net/dns/host_resolver_impl.h b/net/dns/host_resolver_impl.h
index 41482fa..199a9911 100644
--- a/net/dns/host_resolver_impl.h
+++ b/net/dns/host_resolver_impl.h
@@ -231,6 +231,7 @@
   // and |stale_info| must be null.
   int ResolveLocally(const HostPortPair& host,
                      DnsQueryType requested_address_family,
+                     HostResolverSource source,
                      HostResolverFlags flags,
                      bool allow_cache,
                      bool allow_stale,
@@ -286,6 +287,7 @@
   // family when the request leaves it unspecified.
   Key GetEffectiveKeyForRequest(const std::string& hostname,
                                 DnsQueryType dns_query_type,
+                                HostResolverSource source,
                                 HostResolverFlags flags,
                                 const IPAddress* ip_address,
                                 const NetLogWithSource& net_log);
diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc
index 7473caf70..530f9f2 100644
--- a/net/dns/host_resolver_impl_unittest.cc
+++ b/net/dns/host_resolver_impl_unittest.cc
@@ -877,6 +877,26 @@
   EXPECT_TRUE(req->HasAddress(kIpLiteral, 80));
 }
 
+TEST_F(HostResolverImplTest,
+       ResolveIPLiteralWithHostResolverSystemOnly_ResolveHost) {
+  const char kIpLiteral[] = "178.78.32.1";
+  // Add a mapping to tell if the resolver proc was called (if it was called,
+  // then the result will be the remapped value. Otherwise it will be the IP
+  // literal).
+  proc_->AddRuleForAllFamilies(kIpLiteral, "183.45.32.1");
+
+  HostResolver::ResolveHostParameters parameters;
+  parameters.source = HostResolverSource::SYSTEM;
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair(kIpLiteral, 80), NetLogWithSource(), parameters));
+
+  // IP literal resolution is expected to take precedence over source, so the
+  // result is expected to be the input IP, not the result IP from the proc rule
+  EXPECT_THAT(response.result_error(), IsOk());
+  EXPECT_THAT(response.request()->GetAddressResults().value().endpoints(),
+              testing::ElementsAre(CreateExpected(kIpLiteral, 80)));
+}
+
 TEST_F(HostResolverImplTest, EmptyListMeansNameNotResolved) {
   proc_->AddRuleForAllFamilies("just.testing", "");
   proc_->SignalMultiple(1u);
@@ -1575,8 +1595,6 @@
   EXPECT_THAT(new_response->result_error(), IsOk());
 }
 
-// TODO(crbug.com/821021): Create a ResolveHost test once bypassing the cache is
-// supported.
 TEST_F(HostResolverImplTest, BypassCache) {
   struct MyHandler : public Handler {
     void Handle(Request* req) override {
@@ -1611,6 +1629,29 @@
   EXPECT_EQ(2u, proc_->GetCaptureList().size());
 }
 
+TEST_F(HostResolverImplTest, BypassCache_ResolveHost) {
+  proc_->SignalMultiple(2u);
+
+  ResolveHostResponseHelper initial_response(resolver_->CreateRequest(
+      HostPortPair("a", 80), NetLogWithSource(), base::nullopt));
+  EXPECT_THAT(initial_response.result_error(), IsOk());
+  EXPECT_EQ(1u, proc_->GetCaptureList().size());
+
+  ResolveHostResponseHelper cached_response(resolver_->CreateRequest(
+      HostPortPair("a", 80), NetLogWithSource(), base::nullopt));
+  EXPECT_THAT(cached_response.result_error(), IsOk());
+  // Expect no increase to calls to |proc_| because result was cached.
+  EXPECT_EQ(1u, proc_->GetCaptureList().size());
+
+  HostResolver::ResolveHostParameters parameters;
+  parameters.allow_cached_response = false;
+  ResolveHostResponseHelper cache_bypassed_response(resolver_->CreateRequest(
+      HostPortPair("a", 80), NetLogWithSource(), parameters));
+  EXPECT_THAT(cache_bypassed_response.result_error(), IsOk());
+  // Expect call to |proc_| because cache was bypassed.
+  EXPECT_EQ(2u, proc_->GetCaptureList().size());
+}
+
 // Test that IP address changes flush the cache but initial DNS config reads do
 // not.
 TEST_F(HostResolverImplTest, FlushCacheOnIPAddressChange) {
@@ -3801,6 +3842,8 @@
     EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i;
 }
 
+// Test that hosts ending in ".local" or ".local." are resolved using the system
+// resolver.
 TEST_F(HostResolverImplDnsTest, BypassDnsTask_ResolveHost) {
   ChangeDnsConfig(CreateValidDnsConfig());
 
@@ -3834,8 +3877,31 @@
     EXPECT_THAT(responses[i]->result_error(), IsOk());
 }
 
-// TODO(crbug.com/821021): Create a ResolveHost version of this test once
-// system-only resolves are supported.
+// Test that DNS task is always used when explicitly requested as the source,
+// even with a case that would normally bypass it eg hosts ending in ".local".
+TEST_F(HostResolverImplDnsTest, DnsNotBypassedWhenDnsSource) {
+  // Ensure DNS task requests will succeed and system (proc) requests will fail.
+  ChangeDnsConfig(CreateValidDnsConfig());
+  proc_->AddRuleForAllFamilies(std::string(), std::string());
+
+  HostResolver::ResolveHostParameters dns_parameters;
+  dns_parameters.source = HostResolverSource::DNS;
+
+  ResolveHostResponseHelper dns_response(resolver_->CreateRequest(
+      HostPortPair("ok", 80), NetLogWithSource(), dns_parameters));
+  ResolveHostResponseHelper dns_local_response(resolver_->CreateRequest(
+      HostPortPair("ok.local", 80), NetLogWithSource(), dns_parameters));
+  ResolveHostResponseHelper normal_local_response(resolver_->CreateRequest(
+      HostPortPair("ok.local", 80), NetLogWithSource(), base::nullopt));
+
+  proc_->SignalMultiple(3u);
+
+  EXPECT_THAT(dns_response.result_error(), IsOk());
+  EXPECT_THAT(dns_local_response.result_error(), IsOk());
+  EXPECT_THAT(normal_local_response.result_error(),
+              IsError(ERR_NAME_NOT_RESOLVED));
+}
+
 TEST_F(HostResolverImplDnsTest, SystemOnlyBypassesDnsTask) {
   ChangeDnsConfig(CreateValidDnsConfig());
 
@@ -3855,6 +3921,25 @@
   EXPECT_THAT(requests_[1]->WaitForResult(), IsOk());
 }
 
+TEST_F(HostResolverImplDnsTest, SystemOnlyBypassesDnsTask_ResolveHost) {
+  // Ensure DNS task requests will succeed and system (proc) requests will fail.
+  ChangeDnsConfig(CreateValidDnsConfig());
+  proc_->AddRuleForAllFamilies(std::string(), std::string());
+
+  ResolveHostResponseHelper dns_response(resolver_->CreateRequest(
+      HostPortPair("ok", 80), NetLogWithSource(), base::nullopt));
+
+  HostResolver::ResolveHostParameters parameters;
+  parameters.source = HostResolverSource::SYSTEM;
+  ResolveHostResponseHelper system_response(resolver_->CreateRequest(
+      HostPortPair("ok", 80), NetLogWithSource(), parameters));
+
+  proc_->SignalMultiple(2u);
+
+  EXPECT_THAT(dns_response.result_error(), IsOk());
+  EXPECT_THAT(system_response.result_error(), IsError(ERR_NAME_NOT_RESOLVED));
+}
+
 TEST_F(HostResolverImplDnsTest, DisableDnsClientOnPersistentFailure) {
   ChangeDnsConfig(CreateValidDnsConfig());
 
diff --git a/net/dns/host_resolver_source.h b/net/dns/host_resolver_source.h
new file mode 100644
index 0000000..4985dfc
--- /dev/null
+++ b/net/dns/host_resolver_source.h
@@ -0,0 +1,25 @@
+// 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 NET_DNS_HOST_RESOLVER_SOURCE_H_
+#define NET_DNS_HOST_RESOLVER_SOURCE_H_
+
+// Enumeration to specify the allowed results source for HostResolver
+// requests.
+enum class HostResolverSource {
+  // Resolver will pick an appropriate source. Results could come from DNS,
+  // MulticastDNS, HOSTS file, etc.
+  ANY,
+
+  // Results will only be retrieved from the system or OS, eg via the
+  // getaddrinfo() system call.
+  SYSTEM,
+
+  // Results will only come from DNS queries.
+  DNS,
+
+  // TODO(crbug.com/846423): Add MDNS support.
+};
+
+#endif  // NET_DNS_HOST_RESOLVER_SOURCE_H_
diff --git a/remoting/client/gesture_interpreter.cc b/remoting/client/gesture_interpreter.cc
index 2d5498ae..ec84a99 100644
--- a/remoting/client/gesture_interpreter.cc
+++ b/remoting/client/gesture_interpreter.cc
@@ -182,6 +182,19 @@
 
 void GestureInterpreter::OnDesktopSizeChanged(int width, int height) {
   viewport_.SetDesktopSize(width, height);
+  if (viewport_.IsViewportReady()) {
+    input_strategy_->FocusViewportOnCursor(&viewport_);
+  }
+}
+
+void GestureInterpreter::OnSafeInsetsChanged(int left,
+                                             int top,
+                                             int right,
+                                             int bottom) {
+  viewport_.SetSafeInsets(left, top, right, bottom);
+  if (viewport_.IsViewportReady()) {
+    input_strategy_->FocusViewportOnCursor(&viewport_);
+  }
 }
 
 base::WeakPtr<GestureInterpreter> GestureInterpreter::GetWeakPtr() {
diff --git a/remoting/client/gesture_interpreter.h b/remoting/client/gesture_interpreter.h
index fdbc57e3e..872fcac 100644
--- a/remoting/client/gesture_interpreter.h
+++ b/remoting/client/gesture_interpreter.h
@@ -82,6 +82,7 @@
 
   void OnSurfaceSizeChanged(int width, int height);
   void OnDesktopSizeChanged(int width, int height);
+  void OnSafeInsetsChanged(int left, int top, int right, int bottom);
 
   base::WeakPtr<GestureInterpreter> GetWeakPtr();
 
diff --git a/remoting/client/ui/desktop_viewport.cc b/remoting/client/ui/desktop_viewport.cc
index 7f6de72..6d2f530 100644
--- a/remoting/client/ui/desktop_viewport.cc
+++ b/remoting/client/ui/desktop_viewport.cc
@@ -35,25 +35,16 @@
     return;
   }
 
-  // Only reset the viewport if both dimensions have changed, otherwise keep
-  // the offset and scale and just change the constraint. This is to cover these
-  // use cases:
-  // * Rotation => Reset
-  // * Keyboard => No reset
-  // * Settings menu => No reset
-  //
-  // TODO(yuweih): This is probably too much inferring. Let the caller to decide
-  // when to call ResizeToFit() if things don't work right.
-  bool need_to_reset =
-      surface_width != surface_size_.x && surface_height != surface_size_.y;
-
   surface_size_.x = surface_width;
   surface_size_.y = surface_height;
+  ResizeToFit();
+}
 
-  if (need_to_reset) {
-    ResizeToFit();
-    return;
-  }
+void DesktopViewport::SetSafeInsets(int left, int top, int right, int bottom) {
+  safe_insets_.left = left;
+  safe_insets_.top = top;
+  safe_insets_.right = right;
+  safe_insets_.bottom = bottom;
 
   UpdateViewport();
 }
@@ -83,8 +74,12 @@
     LOG(WARNING) << "Viewport is not ready before getting the viewport center";
     return {0.f, 0.f};
   }
+  float safe_area_center_x =
+      (surface_size_.x + safe_insets_.left - safe_insets_.right) / 2.f;
+  float safe_area_center_y =
+      (surface_size_.y + safe_insets_.top - safe_insets_.bottom) / 2.f;
   return desktop_to_surface_transform_.Invert().MapPoint(
-      {surface_size_.x / 2.f, surface_size_.y / 2.f});
+      {safe_area_center_x, safe_area_center_y});
 }
 
 bool DesktopViewport::IsPointWithinDesktopBounds(
@@ -109,7 +104,7 @@
     return point;
   }
 
-  return ConstrainPointToBounds({0.f, desktop_size_.x, 0.f, desktop_size_.y},
+  return ConstrainPointToBounds({0.f, 0.f, desktop_size_.x, desktop_size_.y},
                                 point);
 }
 
@@ -149,10 +144,12 @@
   // +----------+ v
   // resize the desktop such that it fits the viewport in one dimension.
 
-  float scale = std::max(surface_size_.x / desktop_size_.x,
-                         surface_size_.y / desktop_size_.y);
+  ViewMatrix::Vector2D safe_area_size = GetSurfaceSafeAreaSize();
+  float scale = std::max(safe_area_size.x / desktop_size_.x,
+                         safe_area_size.y / desktop_size_.y);
   desktop_to_surface_transform_.SetScale(scale);
-  desktop_to_surface_transform_.SetOffset({0.f, 0.f});
+  desktop_to_surface_transform_.SetOffset(
+      {safe_insets_.left, safe_insets_.top});
   UpdateViewport();
 }
 
@@ -173,10 +170,12 @@
     desktop_to_surface_transform_.SetScale(MAX_ZOOM_LEVEL);
   }
 
+  ViewMatrix::Vector2D safe_area_size = GetSurfaceSafeAreaSize();
+
   ViewMatrix::Vector2D desktop_size_on_surface_ =
       desktop_to_surface_transform_.MapVector(desktop_size_);
-  if (desktop_size_on_surface_.x < surface_size_.x &&
-      desktop_size_on_surface_.y < surface_size_.y) {
+  if (desktop_size_on_surface_.x < safe_area_size.x &&
+      desktop_size_on_surface_.y < safe_area_size.y) {
     //    +==============+
     //    |      VP      |      +==========+
     //    |              |      |    VP    |
@@ -188,8 +187,8 @@
     //    +==============+
     // Displayed desktop is too small in both directions, so apply the minimum
     // zoom level needed to fit either the width or height.
-    float scale = std::min(surface_size_.x / desktop_size_.x,
-                           surface_size_.y / desktop_size_.y);
+    float scale = std::min(safe_area_size.x / desktop_size_.x,
+                           safe_area_size.y / desktop_size_.y);
     desktop_to_surface_transform_.SetScale(scale);
   }
 
@@ -239,13 +238,14 @@
 
   // Viewport size on the desktop space.
   ViewMatrix::Vector2D viewport_size =
-      desktop_to_surface_transform_.Invert().MapVector(surface_size_);
+      desktop_to_surface_transform_.Invert().MapVector(
+          GetSurfaceSafeAreaSize());
 
   // Scenario 1: If VP can fully fit inside the desktop, then VP's center can be
   // anywhere inside the desktop as long as VP doesn't overlap with the border.
   bounds.left = viewport_size.x / 2.f;
-  bounds.right = desktop_size_.x - viewport_size.x / 2.f;
   bounds.top = viewport_size.y / 2.f;
+  bounds.right = desktop_size_.x - viewport_size.x / 2.f;
   bounds.bottom = desktop_size_.y - viewport_size.y / 2.f;
 
   // Scenario 2: If VP can't fully fit inside the desktop in dimension D, then
@@ -266,6 +266,11 @@
   return bounds;
 }
 
+ViewMatrix::Vector2D DesktopViewport::GetSurfaceSafeAreaSize() const {
+  return {surface_size_.x - safe_insets_.left - safe_insets_.right,
+          surface_size_.y - safe_insets_.top - safe_insets_.bottom};
+}
+
 void DesktopViewport::MoveViewportWithoutUpdate(float dx, float dy) {
   // <dx, dy> is defined on desktop's reference frame. Translation must be
   // flipped and scaled.
diff --git a/remoting/client/ui/desktop_viewport.h b/remoting/client/ui/desktop_viewport.h
index 983c8d9..af264ac 100644
--- a/remoting/client/ui/desktop_viewport.h
+++ b/remoting/client/ui/desktop_viewport.h
@@ -30,7 +30,8 @@
 // reference frame.
 class DesktopViewport {
  public:
-  using TransformationCallback = base::Callback<void(const ViewMatrix&)>;
+  using TransformationCallback =
+      base::RepeatingCallback<void(const ViewMatrix&)>;
 
   DesktopViewport();
   ~DesktopViewport();
@@ -38,10 +39,17 @@
   // Sets the |desktop_size_| and (re)initializes the viewport.
   void SetDesktopSize(int desktop_width, int desktop_height);
 
-  // Sets the |surface_size_| and (re)initializes the viewport if both
-  // dimensions are changed.
+  // Sets the |surface_size_| and (re)initializes the viewport.
   void SetSurfaceSize(int surface_width, int surface_height);
 
+  // Sets insets on the surface area to allow viewport to be panned out of them.
+  // Should be used to adjust for system UI like soft keyboard and screen
+  // notches/cutouts.
+  // This method effectively shrinks the size of the viewport on the surface.
+  // You may want to call this before SetSurfaceSize() so that safe insets are
+  // taken into account when initializing viewport.
+  void SetSafeInsets(int left, int top, int right, int bottom);
+
   // Translates the desktop on the surface's reference frame by <dx, dy>.
   void MoveDesktop(float dx, float dy);
 
@@ -82,8 +90,8 @@
  private:
   struct Bounds {
     float left;
-    float right;
     float top;
+    float right;
     float bottom;
   };
 
@@ -100,6 +108,9 @@
   // locate.
   Bounds GetViewportCenterBounds() const;
 
+  // Gets the size of |surface_size_| inset by |safe_insets_|.
+  ViewMatrix::Vector2D GetSurfaceSafeAreaSize() const;
+
   // Translates the viewport on the desktop's reference frame by <dx, dy>,
   // without calling UpdateViewport().
   void MoveViewportWithoutUpdate(float dx, float dy);
@@ -112,6 +123,7 @@
 
   ViewMatrix::Vector2D desktop_size_{0.f, 0.f};
   ViewMatrix::Vector2D surface_size_{0.f, 0.f};
+  Bounds safe_insets_{0, 0, 0, 0};
 
   ViewMatrix desktop_to_surface_transform_;
 
diff --git a/remoting/client/ui/desktop_viewport_unittest.cc b/remoting/client/ui/desktop_viewport_unittest.cc
index 3694e58..2f297d5 100644
--- a/remoting/client/ui/desktop_viewport_unittest.cc
+++ b/remoting/client/ui/desktop_viewport_unittest.cc
@@ -120,7 +120,7 @@
   // +========+----+
   viewport_.SetDesktopSize(9, 3);
   viewport_.SetSurfaceSize(2, 1);
-  AssertTransformationReceived(FROM_HERE, 0.333f, 0.f, 0.f);
+  AssertTransformationReceived(FROM_HERE, 1 / 3.f, 0.f, 0.f);
 }
 
 TEST_F(DesktopViewportTest, TestViewportInitialization4) {
@@ -292,4 +292,115 @@
                   new_transformation.GetScale());
 }
 
+TEST_F(DesktopViewportTest, AsymmetricalSafeInsetsPanAndZoom) {
+  // Initialize with 6x5 desktop and 6x5 screen with this safe inset:
+  // left: 2, top: 2, right: 1, bottom: 1
+  viewport_.SetDesktopSize(6, 5);
+  viewport_.SetSafeInsets(2, 2, 1, 1);
+  viewport_.SetSurfaceSize(6, 5);
+
+  // Viewport is initialized to fit the inset area instead of the whole surface
+  // area.
+  AssertTransformationReceived(FROM_HERE, 0.5, 2, 2);
+
+  // Move the viewport all the way to the bottom right.
+  viewport_.MoveViewport(100, 100);
+
+  // The bottom right of the desktop is stuck with the bottom right of the
+  // safe area.
+  AssertTransformationReceived(FROM_HERE, 0.5, 2, 1.5);
+
+  // Zoom the viewport on the bottom right of the safe area to match the
+  // resolution of the surface.
+  viewport_.ScaleDesktop(5, 4, 2);
+  AssertTransformationReceived(FROM_HERE, 1, -1, -1);
+
+  // Move the desktop by <1, 1>. Now it perfectly fits the surface.
+  viewport_.MoveDesktop(1, 1);
+  AssertTransformationReceived(FROM_HERE, 1, 0, 0);
+
+  // Move the desktop all the way to the top left. Now it stucks with the top
+  // left corner of the safe area.
+  viewport_.MoveDesktop(100, 100);
+  AssertTransformationReceived(FROM_HERE, 1, 2, 2);
+}
+
+TEST_F(DesktopViewportTest, SingleNotchSafeInsetPanAndZoom) {
+  // Initialize with 6x5 desktop and 6x5 screen with this safe inset:
+  // left: 1, right: 1, top: 0, bottom: 0
+  viewport_.SetDesktopSize(6, 5);
+  viewport_.SetSafeInsets(1, 1, 0, 0);
+  viewport_.SetSurfaceSize(6, 5);
+  AssertTransformationReceived(FROM_HERE, 5 / 6.f, 1, 1);
+
+  viewport_.MoveViewport(100, 100);
+  AssertTransformationReceived(FROM_HERE, 5 / 6.f, 1, 5 / 6.f);
+
+  viewport_.ScaleDesktop(6, 5, 1.2);
+  AssertTransformationReceived(FROM_HERE, 1, 0, 0);
+}
+
+TEST_F(DesktopViewportTest, SymmetricSafeInsetPanAndZoom) {
+  // Initialize with 6x5 desktop and 6x5 screen with this safe inset:
+  // left: 1, right: 1, top: 1, bottom: 1
+  viewport_.SetDesktopSize(6, 5);
+  viewport_.SetSafeInsets(1, 1, 1, 1);
+  viewport_.SetSurfaceSize(6, 5);
+  AssertTransformationReceived(FROM_HERE, 2 / 3.f, 1, 1);
+
+  viewport_.MoveViewport(100, 100);
+  AssertTransformationReceived(FROM_HERE, 2 / 3.f, 1, 2 / 3.f);
+
+  viewport_.ScaleDesktop(5, 4, 1.5);
+  AssertTransformationReceived(FROM_HERE, 1, -1, -1);
+
+  viewport_.MoveDesktop(1, 1);
+  AssertTransformationReceived(FROM_HERE, 1, 0, 0);
+}
+
+TEST_F(DesktopViewportTest, RemoveSafeInsets) {
+  // Initialize with 6x5 desktop and 6x5 screen with this safe inset:
+  // left: 2, top: 2, right: 1, bottom: 1
+  viewport_.SetDesktopSize(6, 5);
+  viewport_.SetSafeInsets(2, 2, 1, 1);
+  viewport_.SetSurfaceSize(6, 5);
+
+  AssertTransformationReceived(FROM_HERE, 0.5, 2, 2);
+
+  // Move the viewport all the way to the bottom right.
+  viewport_.MoveViewport(100, 100);
+  AssertTransformationReceived(FROM_HERE, 0.5, 2, 1.5);
+
+  // Now remove the safe insets.
+  viewport_.SetSafeInsets(0, 0, 0, 0);
+
+  // Desktop is now stretched to fit the whole surface.
+  AssertTransformationReceived(FROM_HERE, 1, 0, 0);
+}
+
+TEST_F(DesktopViewportTest, AddAndRemoveSafeInsets) {
+  // This test case tests showing and hiding soft keyboard.
+
+  // Initialize with 12x9 desktop and screen with this safe inset:
+  // left: 2, top: 2, right: 1, bottom: 1
+  viewport_.SetDesktopSize(12, 9);
+  viewport_.SetSafeInsets(2, 2, 1, 1);
+  viewport_.SetSurfaceSize(12, 9);
+  AssertTransformationReceived(FROM_HERE, 0.75, 2, 2);
+
+  // Increase the bottom insets to simulate keyboard popup.
+  viewport_.SetSafeInsets(2, 2, 1, 4);
+  AssertTransformationReceived(FROM_HERE, 0.75, 2, 2);
+
+  // Move the viewport all the way down. (=moving the desktop all the way up.)
+  viewport_.MoveViewport(100, 100);
+  AssertTransformationReceived(FROM_HERE, 0.75, 2, -1.75);
+
+  // Now remove the extra insets.
+  viewport_.SetSafeInsets(2, 2, 1, 1);
+
+  // Viewport should bounce back.
+  AssertTransformationReceived(FROM_HERE, 0.75, 2, 1.25);
+}
+
 }  // namespace remoting
diff --git a/remoting/host/continue_window_linux.cc b/remoting/host/continue_window_linux.cc
index 2060385..83102dd 100644
--- a/remoting/host/continue_window_linux.cc
+++ b/remoting/host/continue_window_linux.cc
@@ -99,12 +99,21 @@
       gtk_label_new(l10n_util::GetStringUTF8(IDS_CONTINUE_PROMPT).c_str());
   gtk_label_set_line_wrap(GTK_LABEL(text_label), TRUE);
   // TODO(lambroslambrou): Fix magic numbers, as in disconnect_window_gtk.cc.
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_widget_set_margin_start(GTK_WIDGET(text_label), 12);
+  gtk_widget_set_margin_end(GTK_WIDGET(text_label), 12);
+  gtk_widget_set_margin_top(GTK_WIDGET(text_label), 12);
+  gtk_widget_set_margin_bottom(GTK_WIDGET(text_label), 12);
+#else
   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
   gtk_misc_set_padding(GTK_MISC(text_label), 12, 12);
   G_GNUC_END_IGNORE_DEPRECATIONS;
+#endif
   gtk_container_add(GTK_CONTAINER(content_area), text_label);
 
+#if !GTK_CHECK_VERSION(3, 90, 0)
   gtk_widget_show_all(content_area);
+#endif
 }
 
 void ContinueWindowGtk::OnResponse(GtkDialog* dialog, int response_id) {
diff --git a/remoting/host/disconnect_window_linux.cc b/remoting/host/disconnect_window_linux.cc
index eb3d018..b1bd5827 100644
--- a/remoting/host/disconnect_window_linux.cc
+++ b/remoting/host/disconnect_window_linux.cc
@@ -186,7 +186,9 @@
   gtk_window_set_deletable(window, FALSE);
 
   // Allow custom rendering of the background pixmap.
+#if !GTK_CHECK_VERSION(3, 90, 0)
   gtk_widget_set_app_paintable(disconnect_window_, TRUE);
+#endif
   g_signal_connect(disconnect_window_, "draw", G_CALLBACK(OnDrawThunk), this);
 
   // Handle window resizing, to regenerate the background pixmap and window
@@ -199,7 +201,9 @@
                    G_CALLBACK(OnConfigureThunk), this);
 
   // Handle mouse events to allow the user to drag the window around.
+#if !GTK_CHECK_VERSION(3, 90, 0)
   gtk_widget_set_events(disconnect_window_, GDK_BUTTON_PRESS_MASK);
+#endif
   g_signal_connect(disconnect_window_, "button-press-event",
                    G_CALLBACK(OnButtonPressThunk), this);
 
@@ -207,24 +211,40 @@
   // The alignment sets narrow margins at the top and bottom, compared with
   // left and right.  The left margin is made larger to accommodate the
   // window movement gripper.
+  GtkWidget* button_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
+  gtk_box_set_homogeneous(GTK_BOX(button_row), FALSE);
+
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_widget_set_margin_start(GTK_WIDGET(button_row), 24);
+  gtk_widget_set_margin_end(GTK_WIDGET(button_row), 12);
+  gtk_widget_set_margin_top(GTK_WIDGET(button_row), 8);
+  gtk_widget_set_margin_bottom(GTK_WIDGET(button_row), 8);
+  gtk_container_add(GTK_CONTAINER(window), button_row);
+#else
   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
   GtkWidget* align = gtk_alignment_new(0, 0, 1, 1);
   gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 24, 12);
   G_GNUC_END_IGNORE_DEPRECATIONS;
   gtk_container_add(GTK_CONTAINER(window), align);
-
-  GtkWidget* button_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
-  gtk_box_set_homogeneous(GTK_BOX(button_row), FALSE);
   gtk_container_add(GTK_CONTAINER(align), button_row);
+#endif
 
   button_ = gtk_button_new_with_label(
       l10n_util::GetStringUTF8(IDS_STOP_SHARING_BUTTON).c_str());
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_box_pack_end(GTK_BOX(button_row), button_);
+#else
   gtk_box_pack_end(GTK_BOX(button_row), button_, FALSE, FALSE, 0);
+#endif
 
   g_signal_connect(button_, "clicked", G_CALLBACK(OnClickedThunk), this);
 
   message_ = gtk_label_new(nullptr);
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_box_pack_end(GTK_BOX(button_row), message_);
+#else
   gtk_box_pack_end(GTK_BOX(button_row), message_, FALSE, FALSE, 0);
+#endif
 
   // Override any theme setting for the text color, so that the text is
   // readable against the window's background pixmap.
@@ -234,13 +254,16 @@
   gtk_label_set_attributes(GTK_LABEL(message_), attributes);
   pango_attr_list_unref(attributes);
 
+#if !GTK_CHECK_VERSION(3, 90, 0)
+  // GTK4 always uses an RGBA visual for windows.
   GdkScreen* screen = gtk_widget_get_screen(disconnect_window_);
   GdkVisual* visual = gdk_screen_get_rgba_visual(screen);
-
   if (visual)
     gtk_widget_set_visual(disconnect_window_, visual);
 
+  // GTK4 shows windows by default.
   gtk_widget_show_all(disconnect_window_);
+#endif
 
   // Extract the user name from the JID.
   std::string client_jid = client_session_control_->client_jid();
diff --git a/remoting/host/it2me/it2me_confirmation_dialog_linux.cc b/remoting/host/it2me/it2me_confirmation_dialog_linux.cc
index 4d218c5..4d586c4 100644
--- a/remoting/host/it2me/it2me_confirmation_dialog_linux.cc
+++ b/remoting/host/it2me/it2me_confirmation_dialog_linux.cc
@@ -123,12 +123,21 @@
           remote_user_email);
   GtkWidget* text_label = gtk_label_new(base::UTF16ToUTF8(dialog_text).c_str());
   gtk_label_set_line_wrap(GTK_LABEL(text_label), true);
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_widget_set_margin_start(GTK_WIDGET(text_label), 12);
+  gtk_widget_set_margin_end(GTK_WIDGET(text_label), 12);
+  gtk_widget_set_margin_top(GTK_WIDGET(text_label), 12);
+  gtk_widget_set_margin_bottom(GTK_WIDGET(text_label), 12);
+#else
   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
   gtk_misc_set_padding(GTK_MISC(text_label), 12, 12);
   G_GNUC_END_IGNORE_DEPRECATIONS;
+#endif
 
   gtk_container_add(GTK_CONTAINER(content_area), text_label);
+#if !GTK_CHECK_VERSION(3, 90, 0)
   gtk_widget_show_all(content_area);
+#endif
 
   gtk_window_set_urgency_hint(GTK_WINDOW(confirmation_window_), true);
   gtk_window_present(GTK_WINDOW(confirmation_window_));
diff --git a/remoting/host/it2me/it2me_native_messaging_host_main.cc b/remoting/host/it2me/it2me_native_messaging_host_main.cc
index b50eb87..b9daa0b 100644
--- a/remoting/host/it2me/it2me_native_messaging_host_main.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host_main.cc
@@ -109,7 +109,11 @@
   // Required for any calls into GTK functions, such as the Disconnect and
   // Continue windows. Calling with nullptr arguments because we don't have
   // any command line arguments for gtk to consume.
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_init();
+#else
   gtk_init(nullptr, nullptr);
+#endif
 
   // Need to prime the host OS version value for linux to prevent IO on the
   // network thread. base::GetLinuxDistro() caches the result.
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index d1063f4f..954152cb 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -1713,7 +1713,11 @@
     // Required for any calls into GTK functions, such as the Disconnect and
     // Continue windows, though these should not be used for the Me2Me case
     // (crbug.com/104377).
+#if GTK_CHECK_VERSION(3, 90, 0)
+    gtk_init();
+#else
     gtk_init(nullptr, nullptr);
+#endif
   }
 
   // Need to prime the host OS version value for linux to prevent IO on the
diff --git a/remoting/ios/app/host_view_controller.mm b/remoting/ios/app/host_view_controller.mm
index 4a70da9..1d1cc45 100644
--- a/remoting/ios/app/host_view_controller.mm
+++ b/remoting/ios/app/host_view_controller.mm
@@ -66,9 +66,9 @@
   // A placeholder view for anchoring views and calculating visible area.
   UIView* _keyboardPlaceholderView;
 
-  // A display link for animating host surface size change. Use the paused
+  // A display link for animating keyboard height change. Use the paused
   // property to start or stop the animation.
-  CADisplayLink* _surfaceSizeAnimationLink;
+  CADisplayLink* _keyboardHeightAnimationLink;
 }
 @end
 
@@ -104,17 +104,11 @@
   _hostView.accessibilityTraits = UIAccessibilityTraitAllowsDirectInteraction;
   [self.view addSubview:_hostView];
 
-  UILayoutGuide* safeAreaLayoutGuide =
-      remoting::SafeAreaLayoutGuideForView(self.view);
-
   [NSLayoutConstraint activateConstraints:@[
-    [_hostView.topAnchor constraintEqualToAnchor:safeAreaLayoutGuide.topAnchor],
-    [_hostView.bottomAnchor
-        constraintEqualToAnchor:safeAreaLayoutGuide.bottomAnchor],
-    [_hostView.leadingAnchor
-        constraintEqualToAnchor:safeAreaLayoutGuide.leadingAnchor],
-    [_hostView.trailingAnchor
-        constraintEqualToAnchor:safeAreaLayoutGuide.trailingAnchor],
+    [_hostView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
+    [_hostView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
+    [_hostView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
+    [_hostView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
   ]];
 
   _hostView.displayTaskRunner =
@@ -193,6 +187,12 @@
   return YES;
 }
 
+- (BOOL)prefersHomeIndicatorAutoHidden {
+  // Allow home indicator to timeout so that user can see desktop on the bottom
+  // of the screen.
+  return YES;
+}
+
 - (void)viewDidAppear:(BOOL)animated {
   [super viewDidAppear:animated];
   [_client.displayHandler createRendererContext:_hostView];
@@ -243,12 +243,12 @@
     [self applicationWillResignActive:UIApplication.sharedApplication];
   }
 
-  _surfaceSizeAnimationLink =
+  _keyboardHeightAnimationLink =
       [CADisplayLink displayLinkWithTarget:self
-                                  selector:@selector(animateHostSurfaceSize:)];
-  _surfaceSizeAnimationLink.paused = YES;
-  [_surfaceSizeAnimationLink addToRunLoop:NSRunLoop.currentRunLoop
-                                  forMode:NSDefaultRunLoopMode];
+                                  selector:@selector(animateKeyboardHeight:)];
+  _keyboardHeightAnimationLink.paused = YES;
+  [_keyboardHeightAnimationLink addToRunLoop:NSRunLoop.currentRunLoop
+                                     forMode:NSDefaultRunLoopMode];
 }
 
 - (void)viewWillDisappear:(BOOL)animated {
@@ -259,18 +259,23 @@
 
   [[NSNotificationCenter defaultCenter] removeObserver:self];
 
-  _surfaceSizeAnimationLink.paused = YES;
-  [_surfaceSizeAnimationLink invalidate];
+  _keyboardHeightAnimationLink.paused = YES;
+  [_keyboardHeightAnimationLink invalidate];
 }
 
 - (void)viewDidLayoutSubviews {
   [super viewDidLayoutSubviews];
 
+  [self updateViewportSafeInsets];
+
   // Pass the actual size of the view to the renderer.
   [_client.displayHandler setSurfaceSize:_hostView.bounds];
 
-  // Start the animation on the host's visible area.
-  _surfaceSizeAnimationLink.paused = NO;
+  _client.gestureInterpreter->OnSurfaceSizeChanged(
+      _hostView.bounds.size.width, _hostView.bounds.size.height);
+
+  // Start the safe insets animation.
+  _keyboardHeightAnimationLink.paused = NO;
 
   [self resizeHostToFitIfNeeded];
 }
@@ -402,24 +407,22 @@
 }
 
 - (void)resizeHostToFitIfNeeded {
-  // Don't adjust the host resolution if the keyboard is active. That would end
-  // up with a very narrow desktop.
-  // Also don't adjust if it's the phone and in portrait orientation. This is
-  // the most used orientation on phones but the aspect ratio is uncommon on
-  // desktop devices.
+  // Don't adjust if it's the phone and in portrait orientation because UI looks
+  // too tight.
   BOOL isPhonePortrait =
       self.traitCollection.horizontalSizeClass ==
           UIUserInterfaceSizeClassCompact &&
       self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassRegular;
 
-  if (_settings.shouldResizeHostToFit && !isPhonePortrait &&
-      !_clientKeyboard.showsSoftKeyboard) {
-    [_client setHostResolution:_hostView.frame.size
+  if (_settings.shouldResizeHostToFit && !isPhonePortrait) {
+    UIEdgeInsets safeInsets = remoting::SafeAreaInsetsForView(_hostView);
+    CGRect safeRect = UIEdgeInsetsInsetRect(_hostView.frame, safeInsets);
+    [_client setHostResolution:safeRect.size
                          scale:_hostView.contentScaleFactor];
   }
 }
 
-- (void)animateHostSurfaceSize:(CADisplayLink*)link {
+- (void)animateKeyboardHeight:(CADisplayLink*)link {
   // The method is called when the keyboard animation is in-progress. It
   // calculates the intermediate visible area size during the animation and
   // passes it to DesktopViewport.
@@ -429,27 +432,36 @@
   // done on the display thread asynchronously, so unfortunately the animation
   // will not be perfectly synchronized with the keyboard animation.
 
-  CGSize viewSize = _hostView.frame.size;
-  CGFloat targetVisibleHeight =
-      viewSize.height - _keyboardPlaceholderView.frame.size.height;
+  [self updateViewportSafeInsets];
+
+  CALayer* kbPlaceholderLayer =
+      [_keyboardPlaceholderView.layer presentationLayer];
+  CGFloat currentKeyboardHeight = kbPlaceholderLayer.frame.size.height;
+  CGFloat targetKeyboardHeight = _keyboardPlaceholderView.frame.size.height;
+  if (currentKeyboardHeight == targetKeyboardHeight) {
+    // Animation is done.
+    _keyboardHeightAnimationLink.paused = YES;
+  }
+}
+
+- (void)updateViewportSafeInsets {
+  // The viewport safe insets consist of area that is (partially) obstructed by
+  // the notch and the soft keyboard.
   CALayer* kbPlaceholderLayer =
       [_keyboardPlaceholderView.layer presentationLayer];
   CGRect viewKeyboardIntersection =
       CGRectIntersection(kbPlaceholderLayer.frame, _hostView.frame);
-  CGFloat currentVisibleHeight =
-      _hostView.frame.size.height - viewKeyboardIntersection.size.height;
-  _client.gestureInterpreter->OnSurfaceSizeChanged(viewSize.width,
-                                                   currentVisibleHeight);
-  if (currentVisibleHeight == targetVisibleHeight) {
-    // Animation is done.
-    _surfaceSizeAnimationLink.paused = YES;
-  }
+  UIEdgeInsets safeInsets = remoting::SafeAreaInsetsForView(_hostView);
+  safeInsets.bottom =
+      std::max(safeInsets.bottom, viewKeyboardIntersection.size.height);
+  _client.gestureInterpreter->OnSafeInsetsChanged(
+      safeInsets.left, safeInsets.top, safeInsets.right, safeInsets.bottom);
 }
 
 - (void)disconnectFromHost {
   [_client disconnectFromHost];
-  [_surfaceSizeAnimationLink invalidate];
-  _surfaceSizeAnimationLink = nil;
+  [_keyboardHeightAnimationLink invalidate];
+  _keyboardHeightAnimationLink = nil;
 }
 
 - (void)applyInputMode {
diff --git a/remoting/ios/app/view_utils.h b/remoting/ios/app/view_utils.h
index 79273e6..fcef6412 100644
--- a/remoting/ios/app/view_utils.h
+++ b/remoting/ios/app/view_utils.h
@@ -12,10 +12,14 @@
 // Returns the current topmost presenting view controller of the app.
 UIViewController* TopPresentingVC();
 
-// Returns the proper safe area layout guide for iOS 11; returns a dumb layout
+// Returns the proper safe area layout guide for iOS 11+; returns a dumb layout
 // guide for older OS versions that exactly matches the anchors of the view.
 UILayoutGuide* SafeAreaLayoutGuideForView(UIView* view);
 
+// Returns the proper safe area insets for iOS 11+; returns empty insets for
+// older OS versions.
+UIEdgeInsets SafeAreaInsetsForView(UIView* view);
+
 // Posts a delayed accessibility announcement so that it doesn't interrupt with
 // the current announcing speech.
 void PostDelayedAccessibilityNotification(NSString* announcement);
diff --git a/remoting/ios/app/view_utils.mm b/remoting/ios/app/view_utils.mm
index f4e7203..c447397 100644
--- a/remoting/ios/app/view_utils.mm
+++ b/remoting/ios/app/view_utils.mm
@@ -46,6 +46,13 @@
   }
 }
 
+UIEdgeInsets SafeAreaInsetsForView(UIView* view) {
+  if (@available(iOS 11, *)) {
+    return view.safeAreaInsets;
+  }
+  return UIEdgeInsetsZero;
+}
+
 void PostDelayedAccessibilityNotification(NSString* announcement) {
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC),
                  dispatch_get_main_queue(), ^{
diff --git a/remoting/test/it2me_standalone_host_main.cc b/remoting/test/it2me_standalone_host_main.cc
index 8ff87e47..4fa8b51f 100644
--- a/remoting/test/it2me_standalone_host_main.cc
+++ b/remoting/test/it2me_standalone_host_main.cc
@@ -28,7 +28,11 @@
   // Required for any calls into GTK functions, such as the Disconnect and
   // Continue windows. Calling with nullptr arguments because we don't have
   // any command line arguments for gtk to consume.
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_init();
+#else
   gtk_init(nullptr, nullptr);
+#endif
 
   // Need to prime the host OS version value for linux to prevent IO on the
   // network thread. base::GetLinuxDistro() caches the result.
diff --git a/services/BUILD.gn b/services/BUILD.gn
index cc86cb84..5c7841f8 100644
--- a/services/BUILD.gn
+++ b/services/BUILD.gn
@@ -49,8 +49,8 @@
       "//services/ui/ime:tests",
       "//services/ui/input_devices:tests",
       "//services/ui/public/cpp/tests",
-      "//services/ui/public/interfaces:tests",
       "//services/ui/ws2:tests",
+      "//services/ws/public/mojom:tests",
     ]
   }
 
diff --git a/services/content/public/cpp/BUILD.gn b/services/content/public/cpp/BUILD.gn
index 46b7d82..171af0c 100644
--- a/services/content/public/cpp/BUILD.gn
+++ b/services/content/public/cpp/BUILD.gn
@@ -40,7 +40,7 @@
 
     if (enable_remote_navigable_contents_view) {
       deps += [
-        "//services/ui/public/interfaces",
+        "//services/ws/public/mojom",
         "//ui/views/mus/remote_view:remote_view_host",
       ]
     }
diff --git a/services/content/public/cpp/DEPS b/services/content/public/cpp/DEPS
index e1df7c3..95044aa 100644
--- a/services/content/public/cpp/DEPS
+++ b/services/content/public/cpp/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+services/ui/public",
+  "+services/ws/public",
   "+ui/aura",
   "+ui/views",
 ]
diff --git a/services/content/public/cpp/navigable_contents_view.cc b/services/content/public/cpp/navigable_contents_view.cc
index 5e66e6d..694b35f 100644
--- a/services/content/public/cpp/navigable_contents_view.cc
+++ b/services/content/public/cpp/navigable_contents_view.cc
@@ -17,7 +17,7 @@
 #include "ui/views/view.h"                // nogncheck
 
 #if BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"  // nogncheck
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"  // nogncheck
 #include "ui/base/ui_base_features.h"                   // nogncheck
 #include "ui/views/mus/remote_view/remote_view_host.h"  // nogncheck
 #endif  // BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 5ceefa9..49be8db 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -833,6 +833,21 @@
       url_request_context_->host_resolver(), network_service_->net_log()));
 }
 
+void NetworkContext::WriteCacheMetadata(const GURL& url,
+                                        net::RequestPriority priority,
+                                        base::Time expected_response_time,
+                                        const std::vector<uint8_t>& data) {
+  net::HttpCache* cache =
+      url_request_context_->http_transaction_factory()->GetCache();
+  if (!cache)
+    return;
+
+  auto buf = base::MakeRefCounted<net::IOBuffer>(data.size());
+  memcpy(buf->data(), data.data(), data.size());
+  cache->WriteMetadata(url, priority, expected_response_time, buf.get(),
+                       data.size());
+}
+
 void NetworkContext::AddHSTSForTesting(const std::string& host,
                                        base::Time expiry,
                                        bool include_subdomains,
diff --git a/services/network/network_context.h b/services/network/network_context.h
index 6525515..03594c7 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -211,6 +211,10 @@
                    mojom::ResolveHostParametersPtr optional_parameters,
                    mojom::ResolveHostClientPtr response_client) override;
   void CreateHostResolver(mojom::HostResolverRequest request) override;
+  void WriteCacheMetadata(const GURL& url,
+                          net::RequestPriority priority,
+                          base::Time expected_response_time,
+                          const std::vector<uint8_t>& data) override;
   void AddHSTSForTesting(const std::string& host,
                          base::Time expiry,
                          bool include_subdomains,
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 710f901..ee7d3be 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -557,21 +557,28 @@
   // TODO(crbug.com/821021): Consider deleting this if most/all usage goes
   // through CreateHostResolver, but most likely most usage except proxy
   // resolver will directly use this method.
-  ResolveHost(HostPortPair host,

-              ResolveHostParameters? optional_parameters,

-              ResolveHostClient response_client);

-

-  // Creates a HostResolver interface that can be passed to code/processes

-  // without direct access to NetworkContext to make ResolveHost requests.

-  //

-  // If this NetworkContext is destroyed, all outstanding requests from child

-  // HostResolvers will be cancelled. Such requests will receive ERR_FAILED via

-  // |response_client|.

-  //

-  // TODO(crbug.com/821021): If necessary as usage and functionality is added to

-  // the contained ResolveHost method, consider adding the ability for this to

-  // be a restricted resolver with some functionality disabled (eg maybe MDNS).

-  CreateHostResolver(HostResolver& host_resolver);

+  ResolveHost(HostPortPair host,
+              ResolveHostParameters? optional_parameters,
+              ResolveHostClient response_client);
+
+  // Creates a HostResolver interface that can be passed to code/processes
+  // without direct access to NetworkContext to make ResolveHost requests.
+  //
+  // If this NetworkContext is destroyed, all outstanding requests from child
+  // HostResolvers will be cancelled. Such requests will receive ERR_FAILED via
+  // |response_client|.
+  //
+  // TODO(crbug.com/821021): If necessary as usage and functionality is added to
+  // the contained ResolveHost method, consider adding the ability for this to
+  // be a restricted resolver with some functionality disabled (eg maybe MDNS).
+  CreateHostResolver(HostResolver& host_resolver);
+
+  // Caches |data| associated with |url| and |expected_response_time| in the
+  // HttpCache related to this NetworkContext.
+  WriteCacheMetadata(url.mojom.Url url,
+                     RequestPriority priority,
+                     mojo_base.mojom.Time expected_response_time,
+                     array<uint8> data);
 
   [Sync]
   // Adds explicitly-specified data as if it was processed from an
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h
index d746d89..2f651fb 100644
--- a/services/network/test/test_network_context.h
+++ b/services/network/test/test_network_context.h
@@ -109,6 +109,10 @@
                    mojom::ResolveHostParametersPtr optional_parameters,
                    mojom::ResolveHostClientPtr response_client) override {}
   void CreateHostResolver(mojom::HostResolverRequest request) override {}
+  void WriteCacheMetadata(const GURL& url,
+                          net::RequestPriority priority,
+                          base::Time expected_response_time,
+                          const std::vector<uint8_t>& data) override {}
   void AddHSTSForTesting(const std::string& host,
                          base::Time expiry,
                          bool include_subdomains,
diff --git a/services/ui/DEPS b/services/ui/DEPS
index c1d47ca..9cfed23c 100644
--- a/services/ui/DEPS
+++ b/services/ui/DEPS
@@ -9,6 +9,7 @@
   "+mojo/public",
   "+services/catalog/public",
   "+services/service_manager",
+  "+services/ws/public/mojom",
   "+third_party/skia/include",
   "+ui",
 ]
diff --git a/services/ui/gpu_host/BUILD.gn b/services/ui/gpu_host/BUILD.gn
index 043bfdd..0d4ca59 100644
--- a/services/ui/gpu_host/BUILD.gn
+++ b/services/ui/gpu_host/BUILD.gn
@@ -28,9 +28,9 @@
   ]
 
   public_deps = [
-    "//services/ui/public/interfaces",
     "//services/viz/privileged/interfaces",
     "//services/viz/public/interfaces",
+    "//services/ws/public/mojom",
   ]
 
   if (is_chromeos) {
@@ -89,7 +89,7 @@
     "//services/ui/common:mus_common",
     "//services/ui/common:task_runner_test_base",
     "//services/ui/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//testing/gtest",
     "//third_party/mesa_headers",
     "//ui/aura",
diff --git a/services/ui/gpu_host/DEPS b/services/ui/gpu_host/DEPS
index 47f275a..a25dbdd 100644
--- a/services/ui/gpu_host/DEPS
+++ b/services/ui/gpu_host/DEPS
@@ -11,6 +11,7 @@
   "+services/ui/gpu_host",
   "+services/viz/privileged/interfaces",
   "+services/viz/public/interfaces",
+  "+services/ws/public",
   "+ui",
 ]
 
diff --git a/services/ui/gpu_host/arc_client.h b/services/ui/gpu_host/arc_client.h
index ee28464..552ec216 100644
--- a/services/ui/gpu_host/arc_client.h
+++ b/services/ui/gpu_host/arc_client.h
@@ -5,7 +5,7 @@
 #ifndef SERVICES_UI_GPU_HOST_ARC_CLIENT_H_
 #define SERVICES_UI_GPU_HOST_ARC_CLIENT_H_
 
-#include "services/ui/public/interfaces/arc.mojom.h"
+#include "services/ws/public/mojom/arc.mojom.h"
 
 namespace viz {
 namespace mojom {
diff --git a/services/ui/gpu_host/gpu_client.h b/services/ui/gpu_host/gpu_client.h
index 09fda101..ad7b7d01 100644
--- a/services/ui/gpu_host/gpu_client.h
+++ b/services/ui/gpu_host/gpu_client.h
@@ -9,7 +9,7 @@
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/config/gpu_info.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 
 namespace viz {
 namespace mojom {
diff --git a/services/ui/gpu_host/gpu_host.h b/services/ui/gpu_host/gpu_host.h
index 4ac1cd05..ba81e6b 100644
--- a/services/ui/gpu_host/gpu_host.h
+++ b/services/ui/gpu_host/gpu_host.h
@@ -15,12 +15,12 @@
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/bindings/strong_binding_set.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
 #include "services/viz/privileged/interfaces/gl/gpu_host.mojom.h"
 #include "services/viz/privileged/interfaces/gl/gpu_service.mojom.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 
 #if defined(OS_CHROMEOS)
-#include "services/ui/public/interfaces/arc.mojom.h"
+#include "services/ws/public/mojom/arc.mojom.h"
 #endif  // defined(OS_CHROMEOS)
 
 namespace discardable_memory {
diff --git a/services/ui/gpu_host/gpu_host_unittest.cc b/services/ui/gpu_host/gpu_host_unittest.cc
index 9bc7d7a..2bc2139 100644
--- a/services/ui/gpu_host/gpu_host_unittest.cc
+++ b/services/ui/gpu_host/gpu_host_unittest.cc
@@ -13,7 +13,7 @@
 #include "gpu/config/gpu_info.h"
 #include "services/ui/gpu_host/gpu_client.h"
 #include "services/ui/gpu_host/gpu_host_delegate.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/init/gl_factory.h"
 
diff --git a/services/ui/ime/BUILD.gn b/services/ui/ime/BUILD.gn
index 93b70a47..5d82622 100644
--- a/services/ui/ime/BUILD.gn
+++ b/services/ui/ime/BUILD.gn
@@ -20,7 +20,7 @@
     "//base",
     "//mojo/public/cpp/bindings",
     "//services/service_manager/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
   ]
 }
 
@@ -36,7 +36,7 @@
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/cpp:service_test_support",
     "//services/ui/ime/test_ime_driver/public/mojom",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
   ]
 
   data_deps = [
diff --git a/services/ui/ime/ime_driver_bridge.h b/services/ui/ime/ime_driver_bridge.h
index f0c0d7b..e8bf930b 100644
--- a/services/ui/ime/ime_driver_bridge.h
+++ b/services/ui/ime/ime_driver_bridge.h
@@ -9,7 +9,7 @@
 
 #include "base/containers/queue.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 
 namespace service_manager {
 class Connector;
diff --git a/services/ui/ime/ime_registrar_impl.h b/services/ui/ime/ime_registrar_impl.h
index 8eb12c5..4eb3523 100644
--- a/services/ui/ime/ime_registrar_impl.h
+++ b/services/ui/ime/ime_registrar_impl.h
@@ -7,7 +7,7 @@
 
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "services/ui/ime/ime_driver_bridge.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 
 namespace ui {
 
diff --git a/services/ui/ime/ime_unittest.cc b/services/ui/ime/ime_unittest.cc
index 50ac7b8..f274031 100644
--- a/services/ui/ime/ime_unittest.cc
+++ b/services/ui/ime/ime_unittest.cc
@@ -11,8 +11,8 @@
 #include "services/service_manager/public/cpp/service_context.h"
 #include "services/service_manager/public/cpp/service_test.h"
 #include "services/ui/ime/test_ime_driver/public/mojom/constants.mojom.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 #include "ui/events/event.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 
diff --git a/services/ui/ime/test_ime_driver/BUILD.gn b/services/ui/ime/test_ime_driver/BUILD.gn
index 58ec1dec..d0723dad 100644
--- a/services/ui/ime/test_ime_driver/BUILD.gn
+++ b/services/ui/ime/test_ime_driver/BUILD.gn
@@ -17,7 +17,7 @@
   deps = [
     "//base",
     "//services/service_manager/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
   ]
 }
 
@@ -30,7 +30,7 @@
     ":lib",
     "//base",
     "//services/service_manager/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
   ]
 }
 
diff --git a/services/ui/ime/test_ime_driver/test_ime_application.cc b/services/ui/ime/test_ime_driver/test_ime_application.cc
index c54efac6..4063898 100644
--- a/services/ui/ime/test_ime_driver/test_ime_application.cc
+++ b/services/ui/ime/test_ime_driver/test_ime_application.cc
@@ -8,8 +8,8 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/service_context.h"
 #include "services/ui/ime/test_ime_driver/test_ime_driver.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 
 namespace ui {
 namespace test {
diff --git a/services/ui/ime/test_ime_driver/test_ime_driver.cc b/services/ui/ime/test_ime_driver/test_ime_driver.cc
index 59250e6..af0f0ec 100644
--- a/services/ui/ime/test_ime_driver/test_ime_driver.cc
+++ b/services/ui/ime/test_ime_driver/test_ime_driver.cc
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "mojo/public/cpp/bindings/strong_binding.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 
 namespace ui {
 namespace test {
diff --git a/services/ui/ime/test_ime_driver/test_ime_driver.h b/services/ui/ime/test_ime_driver/test_ime_driver.h
index 121b887..f94ddef 100644
--- a/services/ui/ime/test_ime_driver/test_ime_driver.h
+++ b/services/ui/ime/test_ime_driver/test_ime_driver.h
@@ -10,7 +10,7 @@
 #include <map>
 #include <memory>
 
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 
 namespace ui {
 namespace test {
diff --git a/services/ui/input_devices/BUILD.gn b/services/ui/input_devices/BUILD.gn
index 687a256..bb487436 100644
--- a/services/ui/input_devices/BUILD.gn
+++ b/services/ui/input_devices/BUILD.gn
@@ -17,7 +17,7 @@
   ]
 
   public_deps = [
-    "//services/ui/public/interfaces/input_devices",
+    "//services/ws/public/mojom/input_devices",
   ]
 }
 
@@ -35,7 +35,7 @@
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/cpp:service_test_support",
     "//services/ui/public/cpp/input_devices",
-    "//services/ui/public/interfaces/input_devices",
+    "//services/ws/public/mojom/input_devices",
     "//testing/gtest",
     "//ui/events/devices",
   ]
diff --git a/services/ui/input_devices/input_device_server.h b/services/ui/input_devices/input_device_server.h
index 267ca59..4a549fa 100644
--- a/services/ui/input_devices/input_device_server.h
+++ b/services/ui/input_devices/input_device_server.h
@@ -8,7 +8,7 @@
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
-#include "services/ui/public/interfaces/input_devices/input_device_server.mojom.h"
+#include "services/ws/public/mojom/input_devices/input_device_server.mojom.h"
 #include "ui/events/devices/input_device_event_observer.h"
 
 namespace ui {
diff --git a/services/ui/input_devices/input_device_unittests.cc b/services/ui/input_devices/input_device_unittests.cc
index 6fea3da..879d53e 100644
--- a/services/ui/input_devices/input_device_unittests.cc
+++ b/services/ui/input_devices/input_device_unittests.cc
@@ -10,7 +10,7 @@
 #include "base/test/test_mock_time_task_runner.h"
 #include "services/ui/input_devices/input_device_server.h"
 #include "services/ui/public/cpp/input_devices/input_device_client.h"
-#include "services/ui/public/interfaces/input_devices/input_device_server.mojom.h"
+#include "services/ws/public/mojom/input_devices/input_device_server.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/devices/device_data_manager.h"
 #include "ui/events/devices/device_hotplug_event_observer.h"
diff --git a/services/ui/public/cpp/BUILD.gn b/services/ui/public/cpp/BUILD.gn
index bf429f70..b1a1d2d 100644
--- a/services/ui/public/cpp/BUILD.gn
+++ b/services/ui/public/cpp/BUILD.gn
@@ -20,7 +20,7 @@
     "//services/service_manager/public/mojom",
     "//services/ui/common:mus_common",
     "//services/ui/public/cpp/gpu",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
   ]
 
   deps = [
@@ -28,7 +28,7 @@
     "//gpu/command_buffer/client:gles2_cmd_helper",
     "//gpu/command_buffer/client:gles2_interface",
     "//services/service_manager/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//ui/display",
     "//ui/events",
     "//ui/gfx/geometry",
@@ -58,7 +58,7 @@
     "//services/service_manager/public/mojom",
     "//services/ui/common:mus_common",
     "//services/ui/public/cpp/gpu",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia/public/interfaces",
     "//ui/display",
     "//ui/events",
diff --git a/services/ui/public/cpp/gpu/BUILD.gn b/services/ui/public/cpp/gpu/BUILD.gn
index e41b4de..d49bee4b 100644
--- a/services/ui/public/cpp/gpu/BUILD.gn
+++ b/services/ui/public/cpp/gpu/BUILD.gn
@@ -24,7 +24,7 @@
     "//gpu/command_buffer/common",
     "//gpu/ipc/client",
     "//gpu/ipc/common",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//url",
   ]
 
@@ -39,7 +39,7 @@
     "//gpu/skia_bindings",
     "//mojo/public/cpp/system",
     "//services/service_manager/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//ui/gl",
   ]
 }
diff --git a/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.cc b/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
index ecf2dc6..d29a0645 100644
--- a/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
+++ b/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
@@ -14,7 +14,7 @@
 #include "mojo/public/cpp/system/buffer.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/gfx/buffer_format_util.h"
 
 using DestructionCallback = base::Callback<void(const gpu::SyncToken& sync)>;
diff --git a/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.h b/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.h
index b3403a5..c04e3a3 100644
--- a/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.h
+++ b/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.h
@@ -14,7 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 
 namespace base {
 class WaitableEvent;
diff --git a/services/ui/public/cpp/gpu/gpu.cc b/services/ui/public/cpp/gpu/gpu.cc
index 86e7ef8..2cc4831 100644
--- a/services/ui/public/cpp/gpu/gpu.cc
+++ b/services/ui/public/cpp/gpu/gpu.cc
@@ -19,8 +19,8 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
 #include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 
 namespace ui {
 
diff --git a/services/ui/public/cpp/gpu/gpu.h b/services/ui/public/cpp/gpu/gpu.h
index 1e2f1c55..2cf525a2 100644
--- a/services/ui/public/cpp/gpu/gpu.h
+++ b/services/ui/public/cpp/gpu/gpu.h
@@ -14,7 +14,7 @@
 #include "components/viz/common/gpu/context_provider.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 
 namespace service_manager {
 class Connector;
diff --git a/services/ui/public/cpp/input_devices/BUILD.gn b/services/ui/public/cpp/input_devices/BUILD.gn
index 624ea7b..e2c1445 100644
--- a/services/ui/public/cpp/input_devices/BUILD.gn
+++ b/services/ui/public/cpp/input_devices/BUILD.gn
@@ -15,7 +15,7 @@
   ]
 
   public_deps = [
-    "//services/ui/public/interfaces/input_devices",
+    "//services/ws/public/mojom/input_devices",
   ]
 
   if (is_chromeos) {
@@ -24,7 +24,7 @@
       "input_device_controller_client.h",
     ]
     public_deps += [
-      "//services/ui/public/interfaces:constants",
+      "//services/ws/public/mojom:constants",
       "//ui/ozone",
     ]
   }
@@ -44,7 +44,7 @@
 
   public_deps = [
     ":input_devices",
-    "//services/ui/public/interfaces/input_devices",
+    "//services/ws/public/mojom/input_devices",
   ]
 }
 
@@ -63,7 +63,7 @@
     ]
 
     public_deps = [
-      "//services/ui/public/interfaces/input_devices",
+      "//services/ws/public/mojom/input_devices",
     ]
 
     # This target is really an implementation detail of the ui service, which
diff --git a/services/ui/public/cpp/input_devices/input_device_client.h b/services/ui/public/cpp/input_devices/input_device_client.h
index 51feeee7..e69ae56 100644
--- a/services/ui/public/cpp/input_devices/input_device_client.h
+++ b/services/ui/public/cpp/input_devices/input_device_client.h
@@ -10,7 +10,7 @@
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "services/ui/public/interfaces/input_devices/input_device_server.mojom.h"
+#include "services/ws/public/mojom/input_devices/input_device_server.mojom.h"
 #include "ui/events/devices/input_device.h"
 #include "ui/events/devices/input_device_event_observer.h"
 #include "ui/events/devices/input_device_manager.h"
diff --git a/services/ui/public/cpp/input_devices/input_device_controller.h b/services/ui/public/cpp/input_devices/input_device_controller.h
index 554d944..8c2e4e8 100644
--- a/services/ui/public/cpp/input_devices/input_device_controller.h
+++ b/services/ui/public/cpp/input_devices/input_device_controller.h
@@ -9,7 +9,7 @@
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/ui/public/interfaces/input_devices/input_device_controller.mojom.h"
+#include "services/ws/public/mojom/input_devices/input_device_controller.mojom.h"
 
 namespace ui {
 
diff --git a/services/ui/public/cpp/input_devices/input_device_controller_client.cc b/services/ui/public/cpp/input_devices/input_device_controller_client.cc
index c817c78..6682180 100644
--- a/services/ui/public/cpp/input_devices/input_device_controller_client.cc
+++ b/services/ui/public/cpp/input_devices/input_device_controller_client.cc
@@ -8,7 +8,7 @@
 
 #include "base/bind_helpers.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 
 namespace ui {
 
diff --git a/services/ui/public/cpp/input_devices/input_device_controller_client.h b/services/ui/public/cpp/input_devices/input_device_controller_client.h
index eac33ad..7bfa98e 100644
--- a/services/ui/public/cpp/input_devices/input_device_controller_client.h
+++ b/services/ui/public/cpp/input_devices/input_device_controller_client.h
@@ -10,7 +10,7 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "services/ui/public/interfaces/input_devices/input_device_controller.mojom.h"
+#include "services/ws/public/mojom/input_devices/input_device_controller.mojom.h"
 
 namespace base {
 class FilePath;
diff --git a/services/ui/public/interfaces/OWNERS b/services/ui/public/interfaces/OWNERS
deleted file mode 100644
index 08850f4..0000000
--- a/services/ui/public/interfaces/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/services/ui/test_ws/BUILD.gn b/services/ui/test_ws/BUILD.gn
index 25eadfe..47ba925f 100644
--- a/services/ui/test_ws/BUILD.gn
+++ b/services/ui/test_ws/BUILD.gn
@@ -23,8 +23,8 @@
     "//services/service_manager/public/mojom",
     "//services/ui/gpu_host",
     "//services/ui/public/cpp",
-    "//services/ui/public/interfaces",
     "//services/ui/ws2:lib",
+    "//services/ws/public/mojom",
     "//ui/aura",
     "//ui/aura:test_support",
     "//ui/compositor:test_support",
diff --git a/services/ui/test_ws/test_gpu_interface_provider.h b/services/ui/test_ws/test_gpu_interface_provider.h
index cfd0727..cf5b77f3 100644
--- a/services/ui/test_ws/test_gpu_interface_provider.h
+++ b/services/ui/test_ws/test_gpu_interface_provider.h
@@ -6,8 +6,8 @@
 #define SERVICES_UI_TEST_WS_TEST_GPU_INTERFACE_PROVIDER_H_
 
 #include "components/discardable_memory/public/interfaces/discardable_shared_memory_manager.mojom.h"
-#include "services/ui/public/interfaces/gpu.mojom.h"
 #include "services/ui/ws2/gpu_interface_provider.h"
+#include "services/ws/public/mojom/gpu.mojom.h"
 
 namespace discardable_memory {
 class DiscardableSharedMemoryManager;
diff --git a/services/ui/test_ws/test_ws.cc b/services/ui/test_ws/test_ws.cc
index cddbac0..6f66d25 100644
--- a/services/ui/test_ws/test_ws.cc
+++ b/services/ui/test_ws/test_ws.cc
@@ -19,11 +19,11 @@
 #include "services/service_manager/public/mojom/service_factory.mojom.h"
 #include "services/ui/gpu_host/gpu_host.h"
 #include "services/ui/gpu_host/gpu_host_delegate.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
 #include "services/ui/test_ws/test_drag_drop_client.h"
 #include "services/ui/test_ws/test_gpu_interface_provider.h"
 #include "services/ui/ws2/window_service.h"
 #include "services/ui/ws2/window_service_delegate.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/aura/test/aura_test_helper.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
diff --git a/services/ui/ws2/BUILD.gn b/services/ui/ws2/BUILD.gn
index 58344872..7192bc0f 100644
--- a/services/ui/ws2/BUILD.gn
+++ b/services/ui/ws2/BUILD.gn
@@ -76,7 +76,7 @@
     "//services/ui/common:mus_common",
     "//services/ui/ime:lib",
     "//services/ui/input_devices",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//ui/aura",
     "//ui/base/mojo:lib",
     "//ui/wm",
@@ -143,7 +143,7 @@
     "//services/service_manager/public/cpp:service_test_support",
     "//services/ui/common:mus_common",
     "//services/ui/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//testing/gtest",
     "//ui/aura",
     "//ui/aura:test_support",
@@ -200,7 +200,7 @@
     "//services/service_manager/public/mojom",
     "//services/ui/common:task_runner_test_base",
     "//services/ui/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//testing/gtest",
     "//third_party/mesa_headers",
     "//ui/aura:test_support",
diff --git a/services/ui/ws2/drag_drop_delegate.h b/services/ui/ws2/drag_drop_delegate.h
index 156a6c8..a2c37df 100644
--- a/services/ui/ws2/drag_drop_delegate.h
+++ b/services/ui/ws2/drag_drop_delegate.h
@@ -7,8 +7,8 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
 #include "services/ui/ws2/ids.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/client/drag_drop_delegate.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 
diff --git a/services/ui/ws2/embedding.h b/services/ui/ws2/embedding.h
index 21729787..6ec728c 100644
--- a/services/ui/ws2/embedding.h
+++ b/services/ui/ws2/embedding.h
@@ -10,7 +10,7 @@
 #include "base/callback_forward.h"
 #include "base/component_export.h"
 #include "base/macros.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 
 namespace aura {
 class Window;
diff --git a/services/ui/ws2/embedding_unittest.cc b/services/ui/ws2/embedding_unittest.cc
index 202b4232..1955bde5 100644
--- a/services/ui/ws2/embedding_unittest.cc
+++ b/services/ui/ws2/embedding_unittest.cc
@@ -9,10 +9,10 @@
 #include <memory>
 #include <queue>
 
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
 #include "services/ui/ws2/window_service.h"
 #include "services/ui/ws2/window_service_test_setup.h"
 #include "services/ui/ws2/window_tree_test_helper.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tracker.h"
diff --git a/services/ui/ws2/event_injector.h b/services/ui/ws2/event_injector.h
index b9977f1..b56eb2e2f 100644
--- a/services/ui/ws2/event_injector.h
+++ b/services/ui/ws2/event_injector.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/ui/public/interfaces/event_injector.mojom.h"
+#include "services/ws/public/mojom/event_injector.mojom.h"
 
 namespace ui {
 namespace ws2 {
diff --git a/services/ui/ws2/focus_handler_unittest.cc b/services/ui/ws2/focus_handler_unittest.cc
index fd3acc70..37df2d0 100644
--- a/services/ui/ws2/focus_handler_unittest.cc
+++ b/services/ui/ws2/focus_handler_unittest.cc
@@ -9,11 +9,11 @@
 #include <memory>
 #include <queue>
 
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
 #include "services/ui/ws2/event_test_utils.h"
 #include "services/ui/ws2/window_service.h"
 #include "services/ui/ws2/window_service_test_setup.h"
 #include "services/ui/ws2/window_tree_test_helper.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/layout_manager.h"
 #include "ui/aura/window.h"
diff --git a/services/ui/ws2/injected_event_handler_unittest.cc b/services/ui/ws2/injected_event_handler_unittest.cc
index d7b8ca4..d3759fb 100644
--- a/services/ui/ws2/injected_event_handler_unittest.cc
+++ b/services/ui/ws2/injected_event_handler_unittest.cc
@@ -9,12 +9,12 @@
 #include "base/bind.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/test/test_connector_factory.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
 #include "services/ui/ws2/gpu_interface_provider.h"
 #include "services/ui/ws2/window_service_test_setup.h"
 #include "services/ui/ws2/window_tree.h"
 #include "services/ui/ws2/window_tree_test_helper.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/test/window_event_dispatcher_test_api.h"
 #include "ui/aura/window.h"
diff --git a/services/ui/ws2/remoting_event_injector.h b/services/ui/ws2/remoting_event_injector.h
index 70e660d..54024d6 100644
--- a/services/ui/ws2/remoting_event_injector.h
+++ b/services/ui/ws2/remoting_event_injector.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/ui/public/interfaces/remoting_event_injector.mojom.h"
+#include "services/ws/public/mojom/remoting_event_injector.mojom.h"
 
 namespace ui {
 
diff --git a/services/ui/ws2/screen_provider.h b/services/ui/ws2/screen_provider.h
index b705555..335bb7c 100644
--- a/services/ui/ws2/screen_provider.h
+++ b/services/ui/ws2/screen_provider.h
@@ -10,8 +10,8 @@
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
-#include "services/ui/public/interfaces/screen_provider_observer.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/screen_provider_observer.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/display/display_observer.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/gfx/geometry/insets.h"
diff --git a/services/ui/ws2/screen_provider_unittest.cc b/services/ui/ws2/screen_provider_unittest.cc
index 1612109..5f2211e 100644
--- a/services/ui/ws2/screen_provider_unittest.cc
+++ b/services/ui/ws2/screen_provider_unittest.cc
@@ -8,13 +8,13 @@
 
 #include "base/run_loop.h"
 #include "services/service_manager/public/cpp/test/test_connector_factory.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/screen_provider_observer.mojom.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
 #include "services/ui/ws2/gpu_interface_provider.h"
 #include "services/ui/ws2/test_screen_provider_observer.h"
 #include "services/ui/ws2/window_service.h"
 #include "services/ui/ws2/window_service_test_setup.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/screen_provider_observer.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/display.h"
 #include "ui/display/screen_base.h"
diff --git a/services/ui/ws2/test_change_tracker.h b/services/ui/ws2/test_change_tracker.h
index 944db16..57043d6 100644
--- a/services/ui/ws2/test_change_tracker.h
+++ b/services/ui/ws2/test_change_tracker.h
@@ -13,7 +13,7 @@
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "services/ui/common/types.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/gfx/geometry/mojo/geometry.mojom.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point_f.h"
diff --git a/services/ui/ws2/test_screen_provider_observer.h b/services/ui/ws2/test_screen_provider_observer.h
index 2a114002..a99ffbe 100644
--- a/services/ui/ws2/test_screen_provider_observer.h
+++ b/services/ui/ws2/test_screen_provider_observer.h
@@ -11,8 +11,8 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "services/ui/public/interfaces/screen_provider_observer.mojom.h"
 #include "services/ui/ws2/window_service_delegate.h"
+#include "services/ws/public/mojom/screen_provider_observer.mojom.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/events/event.h"
 
diff --git a/services/ui/ws2/test_window_tree_client.h b/services/ui/ws2/test_window_tree_client.h
index 186948c..50d73ab 100644
--- a/services/ui/ws2/test_window_tree_client.h
+++ b/services/ui/ws2/test_window_tree_client.h
@@ -12,9 +12,9 @@
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
 #include "services/ui/ws2/test_change_tracker.h"
 #include "services/ui/ws2/test_screen_provider_observer.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 
 namespace ui {
 namespace ws2 {
diff --git a/services/ui/ws2/topmost_window_observer.h b/services/ui/ws2/topmost_window_observer.h
index e8df691..9a55341 100644
--- a/services/ui/ws2/topmost_window_observer.h
+++ b/services/ui/ws2/topmost_window_observer.h
@@ -5,7 +5,7 @@
 #ifndef SERVICES_UI_WS2_TOPMOST_WINDOW_OBSERVER_H_
 #define SERVICES_UI_WS2_TOPMOST_WINDOW_OBSERVER_H_
 
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/window_observer.h"
 #include "ui/events/event_handler.h"
 #include "ui/gfx/geometry/point.h"
diff --git a/services/ui/ws2/user_activity_monitor.h b/services/ui/ws2/user_activity_monitor.h
index 85f3159..a089e96d8 100644
--- a/services/ui/ws2/user_activity_monitor.h
+++ b/services/ui/ws2/user_activity_monitor.h
@@ -14,7 +14,7 @@
 #include "base/timer/timer.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
-#include "services/ui/public/interfaces/user_activity_monitor.mojom.h"
+#include "services/ws/public/mojom/user_activity_monitor.mojom.h"
 #include "ui/events/event_handler.h"
 
 namespace aura {
diff --git a/services/ui/ws2/window_delegate_impl.cc b/services/ui/ws2/window_delegate_impl.cc
index 236a283..70954de 100644
--- a/services/ui/ws2/window_delegate_impl.cc
+++ b/services/ui/ws2/window_delegate_impl.cc
@@ -4,6 +4,7 @@
 
 #include "services/ui/ws2/window_delegate_impl.h"
 
+#include "services/ui/ws2/embedding.h"
 #include "services/ui/ws2/server_window.h"
 #include "services/ui/ws2/window_properties.h"
 #include "ui/aura/window.h"
@@ -43,8 +44,12 @@
       return server_window->window()->delegate()->GetCursor(toplevel_point);
     }
 
-    if (server_window->HasEmbedding())
+    // Assume that if the embedder is intercepting events, it's also responsible
+    // for the cursor (which is the case with content embeddings).
+    if (server_window->HasEmbedding() &&
+        !server_window->embedding()->embedding_tree_intercepts_events()) {
       return server_window->cursor();
+    }
   }
 
   // TODO(sky): there should be a NOTREACHED() here, but we're hitting this on
diff --git a/services/ui/ws2/window_delegate_impl_unittest.cc b/services/ui/ws2/window_delegate_impl_unittest.cc
index 554b38a..6ceaedcf 100644
--- a/services/ui/ws2/window_delegate_impl_unittest.cc
+++ b/services/ui/ws2/window_delegate_impl_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "services/ui/ws2/window_delegate_impl.h"
 
+#include "services/ui/ws2/embedding.h"
 #include "services/ui/ws2/test_window_service_delegate.h"
 #include "services/ui/ws2/window_service_test_setup.h"
 #include "services/ui/ws2/window_tree_test_helper.h"
@@ -34,5 +35,60 @@
             top_level->GetCursor(gfx::Point()).native_type());
 }
 
+TEST(WindowDeleteImplTest, GetCursorForEmbedding) {
+  WindowServiceTestSetup setup;
+  // WindowDelegateImpl deletes itself when the window is deleted.
+  WindowDelegateImpl* delegate = new WindowDelegateImpl();
+  setup.delegate()->set_delegate_for_next_top_level(delegate);
+  aura::Window* top_level =
+      setup.window_tree_test_helper()->NewTopLevelWindow();
+  delegate->set_window(top_level);
+
+  // Create an embedding.
+  aura::Window* embed_window = setup.window_tree_test_helper()->NewWindow();
+  ASSERT_TRUE(embed_window);
+  top_level->AddChild(embed_window);
+  std::unique_ptr<EmbeddingHelper> embedding_helper =
+      setup.CreateEmbedding(embed_window);
+
+  // Set a cursor on the embedded window from the embedded client and ensure we
+  // get it back.
+  const ui::CursorData help_cursor(ui::CursorType::kHelp);
+  embedding_helper->window_tree_test_helper->SetCursor(embed_window,
+                                                       help_cursor);
+  EXPECT_EQ(help_cursor.cursor_type(),
+            embed_window->GetCursor(gfx::Point()).native_type());
+}
+
+TEST(WindowDeleteImplTest, GetCursorForEmbeddingInterceptsEvents) {
+  WindowServiceTestSetup setup;
+  // WindowDelegateImpl deletes itself when the window is deleted.
+  WindowDelegateImpl* delegate = new WindowDelegateImpl();
+  setup.delegate()->set_delegate_for_next_top_level(delegate);
+  aura::Window* top_level =
+      setup.window_tree_test_helper()->NewTopLevelWindow();
+  delegate->set_window(top_level);
+
+  // Set a cursor on |top_level|.
+  const ui::CursorData help_cursor(ui::CursorType::kHelp);
+  setup.window_tree_test_helper()->SetCursor(top_level, help_cursor);
+
+  // Create an embedding.
+  aura::Window* embed_window = setup.window_tree_test_helper()->NewWindow();
+  ASSERT_TRUE(embed_window);
+  top_level->AddChild(embed_window);
+  std::unique_ptr<EmbeddingHelper> embedding_helper = setup.CreateEmbedding(
+      embed_window, ui::mojom::kEmbedFlagEmbedderInterceptsEvents);
+
+  // Set a cursor on the embedding. Because the embedding was created with
+  // kEmbedFlagEmbedderInterceptsEvents the cursor should come from the parent
+  // (|top_level|).
+  const ui::CursorData ibeam_cursor(ui::CursorType::kIBeam);
+  embedding_helper->window_tree_test_helper->SetCursor(embed_window,
+                                                       ibeam_cursor);
+  EXPECT_EQ(help_cursor.cursor_type(),
+            embed_window->GetCursor(gfx::Point()).native_type());
+}
+
 }  // namespace ws2
 }  // namespace ui
diff --git a/services/ui/ws2/window_server_test_impl.cc b/services/ui/ws2/window_server_test_impl.cc
index 57a1f1a..afcff40 100644
--- a/services/ui/ws2/window_server_test_impl.cc
+++ b/services/ui/ws2/window_server_test_impl.cc
@@ -4,9 +4,9 @@
 
 #include "services/ui/ws2/window_server_test_impl.h"
 
-#include "services/ui/public/interfaces/window_tree.mojom.h"
 #include "services/ui/ws2/window_service.h"
 #include "services/ui/ws2/window_tree.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 
 namespace ui {
 namespace ws2 {
diff --git a/services/ui/ws2/window_server_test_impl.h b/services/ui/ws2/window_server_test_impl.h
index cb776ba..fdcf035 100644
--- a/services/ui/ws2/window_server_test_impl.h
+++ b/services/ui/ws2/window_server_test_impl.h
@@ -5,7 +5,7 @@
 #ifndef SERVICES_UI_WS2_WINDOW_SERVER_TEST_IMPL_H_
 #define SERVICES_UI_WS2_WINDOW_SERVER_TEST_IMPL_H_
 
-#include "services/ui/public/interfaces/window_server_test.mojom.h"
+#include "services/ws/public/mojom/window_server_test.mojom.h"
 
 namespace ui {
 namespace ws2 {
diff --git a/services/ui/ws2/window_service.cc b/services/ui/ws2/window_service.cc
index eacc2a2d..afe12bc 100644
--- a/services/ui/ws2/window_service.cc
+++ b/services/ui/ws2/window_service.cc
@@ -10,7 +10,6 @@
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/bind_source_info.h"
 #include "services/ui/common/switches.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
 #include "services/ui/ws2/embedding.h"
 #include "services/ui/ws2/event_injector.h"
 #include "services/ui/ws2/gpu_interface_provider.h"
@@ -23,6 +22,7 @@
 #include "services/ui/ws2/window_service_observer.h"
 #include "services/ui/ws2/window_tree.h"
 #include "services/ui/ws2/window_tree_factory.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/env.h"
 #include "ui/base/mojo/clipboard_host.h"
 #include "ui/wm/core/shadow_types.h"
diff --git a/services/ui/ws2/window_service.h b/services/ui/ws2/window_service.h
index e1e53fba..1c817952 100644
--- a/services/ui/ws2/window_service.h
+++ b/services/ui/ws2/window_service.h
@@ -19,13 +19,13 @@
 #include "services/ui/ime/ime_driver_bridge.h"
 #include "services/ui/ime/ime_registrar_impl.h"
 #include "services/ui/input_devices/input_device_server.h"
-#include "services/ui/public/interfaces/event_injector.mojom.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
-#include "services/ui/public/interfaces/remoting_event_injector.mojom.h"
-#include "services/ui/public/interfaces/user_activity_monitor.mojom.h"
-#include "services/ui/public/interfaces/window_server_test.mojom.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
 #include "services/ui/ws2/ids.h"
+#include "services/ws/public/mojom/event_injector.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
+#include "services/ws/public/mojom/remoting_event_injector.mojom.h"
+#include "services/ws/public/mojom/user_activity_monitor.mojom.h"
+#include "services/ws/public/mojom/window_server_test.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/mus/property_converter.h"
 #include "ui/base/mojo/clipboard.mojom.h"
 
diff --git a/services/ui/ws2/window_service_delegate.h b/services/ui/ws2/window_service_delegate.h
index f6108476..5ca61ca 100644
--- a/services/ui/ws2/window_service_delegate.h
+++ b/services/ui/ws2/window_service_delegate.h
@@ -14,8 +14,8 @@
 #include "base/callback_forward.h"
 #include "base/component_export.h"
 #include "base/containers/flat_map.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/ui_base_types.h"
diff --git a/services/ui/ws2/window_service_unittest.cc b/services/ui/ws2/window_service_unittest.cc
index c78e433d..2ad98a7 100644
--- a/services/ui/ws2/window_service_unittest.cc
+++ b/services/ui/ws2/window_service_unittest.cc
@@ -9,12 +9,12 @@
 #include "base/run_loop.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/test/test_connector_factory.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
 #include "services/ui/ws2/gpu_interface_provider.h"
 #include "services/ui/ws2/window_service_test_setup.h"
 #include "services/ui/ws2/window_tree.h"
 #include "services/ui/ws2/window_tree_test_helper.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ui {
diff --git a/services/ui/ws2/window_tree.h b/services/ui/ws2/window_tree.h
index 35411c4..b0da755c 100644
--- a/services/ui/ws2/window_tree.h
+++ b/services/ui/ws2/window_tree.h
@@ -16,9 +16,9 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
 #include "services/ui/ws2/focus_handler.h"
 #include "services/ui/ws2/ids.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/client/capture_client_observer.h"
 #include "ui/aura/window_observer.h"
 
diff --git a/services/ui/ws2/window_tree_binding.h b/services/ui/ws2/window_tree_binding.h
index 1d77ac9d..a1f8a66 100644
--- a/services/ui/ws2/window_tree_binding.h
+++ b/services/ui/ws2/window_tree_binding.h
@@ -11,9 +11,9 @@
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "services/ui/public/interfaces/screen_provider_observer.mojom.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
 #include "services/ui/ws2/window_tree_binding.h"
+#include "services/ws/public/mojom/screen_provider_observer.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 
 namespace aura {
 class Window;
diff --git a/services/ui/ws2/window_tree_client_unittest.cc b/services/ui/ws2/window_tree_client_unittest.cc
index b2482be..ef704e9e 100644
--- a/services/ui/ws2/window_tree_client_unittest.cc
+++ b/services/ui/ws2/window_tree_client_unittest.cc
@@ -15,11 +15,11 @@
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/cpp/service_test.h"
 #include "services/ui/common/util.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
 #include "services/ui/ws2/ids.h"
 #include "services/ui/ws2/test_window_tree_client.h"
 #include "services/ui/ws2/window_server_service_test_base.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/base/cursor/cursor.h"
 
 using mojo::InterfaceRequest;
diff --git a/services/ui/ws2/window_tree_factory.h b/services/ui/ws2/window_tree_factory.h
index cd0e1e1..46d6acf2 100644
--- a/services/ui/ws2/window_tree_factory.h
+++ b/services/ui/ws2/window_tree_factory.h
@@ -12,7 +12,7 @@
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 
 namespace ui {
 namespace ws2 {
diff --git a/services/ui/ws2/window_tree_test_helper.h b/services/ui/ws2/window_tree_test_helper.h
index f522b38..3a913a3 100644
--- a/services/ui/ws2/window_tree_test_helper.h
+++ b/services/ui/ws2/window_tree_test_helper.h
@@ -11,10 +11,10 @@
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/optional.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
 #include "services/ui/ws2/ids.h"
 #include "services/ui/ws2/window_tree.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace aura {
diff --git a/services/ui/ws2/window_tree_unittest.cc b/services/ui/ws2/window_tree_unittest.cc
index 5002913..b9cb161 100644
--- a/services/ui/ws2/window_tree_unittest.cc
+++ b/services/ui/ws2/window_tree_unittest.cc
@@ -13,13 +13,13 @@
 #include "base/run_loop.h"
 #include "base/unguessable_token.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
 #include "services/ui/ws2/event_test_utils.h"
 #include "services/ui/ws2/server_window.h"
 #include "services/ui/ws2/server_window_test_helper.h"
 #include "services/ui/ws2/window_service.h"
 #include "services/ui/ws2/window_service_test_setup.h"
 #include "services/ui/ws2/window_tree_test_helper.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/layout_manager.h"
diff --git a/services/video_capture/public/mojom/BUILD.gn b/services/video_capture/public/mojom/BUILD.gn
index c3b0257..0ddccbe 100644
--- a/services/video_capture/public/mojom/BUILD.gn
+++ b/services/video_capture/public/mojom/BUILD.gn
@@ -20,7 +20,7 @@
     "//media/capture/mojom:image_capture",
     "//media/capture/mojom:video_capture",
     "//media/mojo/interfaces",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//ui/gfx/geometry/mojo",
   ]
 }
diff --git a/services/viz/privileged/interfaces/compositing/display_private.mojom b/services/viz/privileged/interfaces/compositing/display_private.mojom
index f1144ab..888386d 100644
--- a/services/viz/privileged/interfaces/compositing/display_private.mojom
+++ b/services/viz/privileged/interfaces/compositing/display_private.mojom
@@ -44,6 +44,14 @@
     mojo_base.mojom.TimeTicks timebase,
     mojo_base.mojom.TimeDelta interval);
 
+  // Attempts to immediately draw and swap a frame if possible. Note that this
+  // is not a sync IPC, as all current uses are during tear down of the root
+  // compositor frame sink and are immediately followed by a call to
+  // FrameSinkManager::DestroyCompositorFrameSink which is an associated sync
+  // IPC. If this function is needed in cases where ordering isn't guaranteed
+  // by other means, it should be made sync.
+  ForceImmediateDrawAndSwapIfPossible();
+
   // Notifies the display to pause VSync signals. Used for VR.
   [EnableIf=is_android]
   SetVSyncPaused(bool paused);
diff --git a/services/ws/DEPS b/services/ws/DEPS
new file mode 100644
index 0000000..126938d
--- /dev/null
+++ b/services/ws/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+mojo/public",
+  "+third_party/skia/include",
+  "+ui",
+]
diff --git a/services/ui/public/interfaces/BUILD.gn b/services/ws/public/mojom/BUILD.gn
similarity index 88%
rename from services/ui/public/interfaces/BUILD.gn
rename to services/ws/public/mojom/BUILD.gn
index 168d04d..c07d3e8 100644
--- a/services/ui/public/interfaces/BUILD.gn
+++ b/services/ws/public/mojom/BUILD.gn
@@ -5,7 +5,7 @@
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//testing/test.gni")
 
-mojom("interfaces") {
+mojom("mojom") {
   sources = [
     "event_injector.mojom",
     "gpu.mojom",
@@ -29,9 +29,9 @@
     "//gpu/ipc/common:interfaces",
     "//media/mojo/interfaces",
     "//mojo/public/mojom/base",
-    "//services/ui/public/interfaces/cursor",
-    "//services/ui/public/interfaces/ime",
     "//services/viz/public/interfaces",
+    "//services/ws/public/mojom/cursor",
+    "//services/ws/public/mojom/ime",
     "//skia/public/interfaces",
     "//ui/base/mojo",
     "//ui/display/mojo:interfaces",
@@ -65,8 +65,8 @@
   deps = [
     "//base",
     "//base/test:test_support",
-    "//services/ui/public/interfaces/cursor",
-    "//services/ui/public/interfaces/ime:test_interfaces",
+    "//services/ws/public/mojom/cursor",
+    "//services/ws/public/mojom/ime:test_interfaces",
     "//skia/public/interfaces",
     "//testing/gtest",
     "//ui/display/types",
diff --git a/ash/components/autoclick/public/mojom/OWNERS b/services/ws/public/mojom/OWNERS
similarity index 100%
rename from ash/components/autoclick/public/mojom/OWNERS
rename to services/ws/public/mojom/OWNERS
diff --git a/services/ui/public/interfaces/arc.mojom b/services/ws/public/mojom/arc.mojom
similarity index 100%
rename from services/ui/public/interfaces/arc.mojom
rename to services/ws/public/mojom/arc.mojom
diff --git a/services/ui/public/interfaces/constants.mojom b/services/ws/public/mojom/constants.mojom
similarity index 100%
rename from services/ui/public/interfaces/constants.mojom
rename to services/ws/public/mojom/constants.mojom
diff --git a/services/ui/public/interfaces/cursor/BUILD.gn b/services/ws/public/mojom/cursor/BUILD.gn
similarity index 100%
rename from services/ui/public/interfaces/cursor/BUILD.gn
rename to services/ws/public/mojom/cursor/BUILD.gn
diff --git a/services/ui/public/interfaces/cursor/DEPS b/services/ws/public/mojom/cursor/DEPS
similarity index 100%
rename from services/ui/public/interfaces/cursor/DEPS
rename to services/ws/public/mojom/cursor/DEPS
diff --git a/services/ui/public/interfaces/cursor/OWNERS b/services/ws/public/mojom/cursor/OWNERS
similarity index 100%
rename from services/ui/public/interfaces/cursor/OWNERS
rename to services/ws/public/mojom/cursor/OWNERS
diff --git a/services/ui/public/interfaces/cursor/cursor.mojom b/services/ws/public/mojom/cursor/cursor.mojom
similarity index 100%
rename from services/ui/public/interfaces/cursor/cursor.mojom
rename to services/ws/public/mojom/cursor/cursor.mojom
diff --git a/services/ui/public/interfaces/cursor/cursor.typemap b/services/ws/public/mojom/cursor/cursor.typemap
similarity index 71%
rename from services/ui/public/interfaces/cursor/cursor.typemap
rename to services/ws/public/mojom/cursor/cursor.typemap
index b64a81c54..66041be 100644
--- a/services/ui/public/interfaces/cursor/cursor.typemap
+++ b/services/ws/public/mojom/cursor/cursor.typemap
@@ -2,15 +2,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-mojom = "//services/ui/public/interfaces/cursor/cursor.mojom"
+mojom = "//services/ws/public/mojom/cursor/cursor.mojom"
 public_headers = [
   "//ui/base/cursor/cursor_type.h",
   "//ui/base/cursor/cursor_data.h",
 ]
-traits_headers =
-    [ "//services/ui/public/interfaces/cursor/cursor_struct_traits.h" ]
+traits_headers = [ "//services/ws/public/mojom/cursor/cursor_struct_traits.h" ]
 sources = [
-  "//services/ui/public/interfaces/cursor/cursor_struct_traits.cc",
+  "//services/ws/public/mojom/cursor/cursor_struct_traits.cc",
 ]
 public_deps = [
   "//ui/base",
diff --git a/services/ui/public/interfaces/cursor/cursor_struct_traits.cc b/services/ws/public/mojom/cursor/cursor_struct_traits.cc
similarity index 98%
rename from services/ui/public/interfaces/cursor/cursor_struct_traits.cc
rename to services/ws/public/mojom/cursor/cursor_struct_traits.cc
index 17f0aff5..7038967 100644
--- a/services/ui/public/interfaces/cursor/cursor_struct_traits.cc
+++ b/services/ws/public/mojom/cursor/cursor_struct_traits.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ui/public/interfaces/cursor/cursor_struct_traits.h"
+#include "services/ws/public/mojom/cursor/cursor_struct_traits.h"
 
 #include "mojo/public/cpp/base/time_mojom_traits.h"
-#include "services/ui/public/interfaces/cursor/cursor.mojom.h"
+#include "services/ws/public/mojom/cursor/cursor.mojom.h"
 #include "skia/public/interfaces/bitmap_skbitmap_struct_traits.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/geometry/mojo/geometry_struct_traits.h"
diff --git a/services/ui/public/interfaces/cursor/cursor_struct_traits.h b/services/ws/public/mojom/cursor/cursor_struct_traits.h
similarity index 81%
rename from services/ui/public/interfaces/cursor/cursor_struct_traits.h
rename to services/ws/public/mojom/cursor/cursor_struct_traits.h
index 8c29aa3..b40c4f9 100644
--- a/services/ui/public/interfaces/cursor/cursor_struct_traits.h
+++ b/services/ws/public/mojom/cursor/cursor_struct_traits.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_UI_PUBLIC_INTERFACES_CURSOR_CURSOR_STRUCT_TRAITS_H_
-#define SERVICES_UI_PUBLIC_INTERFACES_CURSOR_CURSOR_STRUCT_TRAITS_H_
+#ifndef SERVICES_WS_PUBLIC_MOJOM_CURSOR_CURSOR_STRUCT_TRAITS_H_
+#define SERVICES_WS_PUBLIC_MOJOM_CURSOR_CURSOR_STRUCT_TRAITS_H_
 
-#include "services/ui/public/interfaces/cursor/cursor.mojom-shared.h"
+#include "services/ws/public/mojom/cursor/cursor.mojom-shared.h"
 #include "ui/base/cursor/cursor_data.h"
 #include "ui/base/cursor/cursor_type.h"
 
@@ -39,4 +39,4 @@
 
 }  // namespace mojo
 
-#endif  // SERVICES_UI_PUBLIC_INTERFACES_CURSOR_CURSOR_STRUCT_TRAITS_H_
+#endif  // SERVICES_WS_PUBLIC_MOJOM_CURSOR_CURSOR_STRUCT_TRAITS_H_
diff --git a/services/ui/public/interfaces/cursor/cursor_struct_traits_unittest.cc b/services/ws/public/mojom/cursor/cursor_struct_traits_unittest.cc
similarity index 96%
rename from services/ui/public/interfaces/cursor/cursor_struct_traits_unittest.cc
rename to services/ws/public/mojom/cursor/cursor_struct_traits_unittest.cc
index f844eed..8b13a2fd 100644
--- a/services/ui/public/interfaces/cursor/cursor_struct_traits_unittest.cc
+++ b/services/ws/public/mojom/cursor/cursor_struct_traits_unittest.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ui/public/interfaces/cursor/cursor_struct_traits.h"
+#include "services/ws/public/mojom/cursor/cursor_struct_traits.h"
 
 #include "mojo/public/cpp/base/time_mojom_traits.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/ui/public/interfaces/cursor/cursor.mojom.h"
+#include "services/ws/public/mojom/cursor/cursor.mojom.h"
 #include "skia/public/interfaces/bitmap_skbitmap_struct_traits.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/cursor/cursor.h"
diff --git a/services/ui/public/interfaces/cursor/typemaps.gni b/services/ws/public/mojom/cursor/typemaps.gni
similarity index 69%
rename from services/ui/public/interfaces/cursor/typemaps.gni
rename to services/ws/public/mojom/cursor/typemaps.gni
index c2ecaca..b9217de 100644
--- a/services/ui/public/interfaces/cursor/typemaps.gni
+++ b/services/ws/public/mojom/cursor/typemaps.gni
@@ -2,4 +2,4 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-typemaps = [ "//services/ui/public/interfaces/cursor/cursor.typemap" ]
+typemaps = [ "//services/ws/public/mojom/cursor/cursor.typemap" ]
diff --git a/services/ui/public/interfaces/event_injector.mojom b/services/ws/public/mojom/event_injector.mojom
similarity index 100%
rename from services/ui/public/interfaces/event_injector.mojom
rename to services/ws/public/mojom/event_injector.mojom
diff --git a/services/ui/public/interfaces/gpu.mojom b/services/ws/public/mojom/gpu.mojom
similarity index 100%
rename from services/ui/public/interfaces/gpu.mojom
rename to services/ws/public/mojom/gpu.mojom
diff --git a/services/ui/public/interfaces/ime/BUILD.gn b/services/ws/public/mojom/ime/BUILD.gn
similarity index 100%
rename from services/ui/public/interfaces/ime/BUILD.gn
rename to services/ws/public/mojom/ime/BUILD.gn
diff --git a/services/ui/public/interfaces/ime/OWNERS b/services/ws/public/mojom/ime/OWNERS
similarity index 100%
rename from services/ui/public/interfaces/ime/OWNERS
rename to services/ws/public/mojom/ime/OWNERS
diff --git a/services/ui/public/interfaces/ime/ime.mojom b/services/ws/public/mojom/ime/ime.mojom
similarity index 100%
rename from services/ui/public/interfaces/ime/ime.mojom
rename to services/ws/public/mojom/ime/ime.mojom
diff --git a/services/ui/public/interfaces/ime/ime.typemap b/services/ws/public/mojom/ime/ime.typemap
similarity index 81%
rename from services/ui/public/interfaces/ime/ime.typemap
rename to services/ws/public/mojom/ime/ime.typemap
index feb425d31..18ce4be 100644
--- a/services/ui/public/interfaces/ime/ime.typemap
+++ b/services/ws/public/mojom/ime/ime.typemap
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-mojom = "//services/ui/public/interfaces/ime/ime.mojom"
+mojom = "//services/ws/public/mojom/ime/ime.mojom"
 public_headers = [
   "//ui/base/ime/candidate_window.h",
   "//ui/base/ime/composition_text.h",
@@ -10,9 +10,9 @@
   "//ui/base/ime/text_input_mode.h",
   "//ui/base/ime/text_input_type.h",
 ]
-traits_headers = [ "//services/ui/public/interfaces/ime/ime_struct_traits.h" ]
+traits_headers = [ "//services/ws/public/mojom/ime/ime_struct_traits.h" ]
 sources = [
-  "//services/ui/public/interfaces/ime/ime_struct_traits.cc",
+  "//services/ws/public/mojom/ime/ime_struct_traits.cc",
 ]
 public_deps = [
   "//ui/base/ime",
diff --git a/services/ui/public/interfaces/ime/ime_struct_traits.cc b/services/ws/public/mojom/ime/ime_struct_traits.cc
similarity index 98%
rename from services/ui/public/interfaces/ime/ime_struct_traits.cc
rename to services/ws/public/mojom/ime/ime_struct_traits.cc
index e05a5e8..37b3167 100644
--- a/services/ui/public/interfaces/ime/ime_struct_traits.cc
+++ b/services/ws/public/mojom/ime/ime_struct_traits.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ui/public/interfaces/ime/ime_struct_traits.h"
+#include "services/ws/public/mojom/ime/ime_struct_traits.h"
 
 #include "mojo/public/cpp/base/string16_mojom_traits.h"
 #include "ui/gfx/range/mojo/range_struct_traits.h"
diff --git a/services/ui/public/interfaces/ime/ime_struct_traits.h b/services/ws/public/mojom/ime/ime_struct_traits.h
similarity index 94%
rename from services/ui/public/interfaces/ime/ime_struct_traits.h
rename to services/ws/public/mojom/ime/ime_struct_traits.h
index a90bdb9..b8bcc20d 100644
--- a/services/ui/public/interfaces/ime/ime_struct_traits.h
+++ b/services/ws/public/mojom/ime/ime_struct_traits.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_UI_PUBLIC_INTERFACES_IME_IME_STRUCT_TRAITS_H_
-#define SERVICES_UI_PUBLIC_INTERFACES_IME_IME_STRUCT_TRAITS_H_
+#ifndef SERVICES_WS_PUBLIC_MOJOM_IME_IME_STRUCT_TRAITS_H_
+#define SERVICES_WS_PUBLIC_MOJOM_IME_IME_STRUCT_TRAITS_H_
 
-#include "services/ui/public/interfaces/ime/ime.mojom-shared.h"
+#include "services/ws/public/mojom/ime/ime.mojom-shared.h"
 #include "ui/base/ime/candidate_window.h"
 #include "ui/base/ime/composition_text.h"
 #include "ui/base/ime/ime_text_span.h"
@@ -140,4 +140,4 @@
 
 }  // namespace mojo
 
-#endif  // SERVICES_UI_PUBLIC_INTERFACES_IME_IME_STRUCT_TRAITS_H_
+#endif  // SERVICES_WS_PUBLIC_MOJOM_IME_IME_STRUCT_TRAITS_H_
diff --git a/services/ui/public/interfaces/ime/ime_struct_traits_test.mojom b/services/ws/public/mojom/ime/ime_struct_traits_test.mojom
similarity index 88%
rename from services/ui/public/interfaces/ime/ime_struct_traits_test.mojom
rename to services/ws/public/mojom/ime/ime_struct_traits_test.mojom
index 3b1d358..c4b1aec 100644
--- a/services/ui/public/interfaces/ime/ime_struct_traits_test.mojom
+++ b/services/ws/public/mojom/ime/ime_struct_traits_test.mojom
@@ -4,7 +4,7 @@
 
 module ui.mojom;
 
-import "services/ui/public/interfaces/ime/ime.mojom";
+import "services/ws/public/mojom/ime/ime.mojom";
 import "ui/platform_window/mojo/text_input_state.mojom";
 
 interface IMEStructTraitsTest {
diff --git a/services/ui/public/interfaces/ime/ime_struct_traits_unittest.cc b/services/ws/public/mojom/ime/ime_struct_traits_unittest.cc
similarity index 97%
rename from services/ui/public/interfaces/ime/ime_struct_traits_unittest.cc
rename to services/ws/public/mojom/ime/ime_struct_traits_unittest.cc
index 48beb25..d4d113a 100644
--- a/services/ui/public/interfaces/ime/ime_struct_traits_unittest.cc
+++ b/services/ws/public/mojom/ime/ime_struct_traits_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ui/public/interfaces/ime/ime_struct_traits.h"
+#include "services/ws/public/mojom/ime/ime_struct_traits.h"
 
 #include <utility>
 
@@ -10,7 +10,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "mojo/public/cpp/base/string16_mojom_traits.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/ui/public/interfaces/ime/ime_struct_traits_test.mojom.h"
+#include "services/ws/public/mojom/ime/ime_struct_traits_test.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ime/composition_text.h"
 #include "ui/base/ime/ime_text_span.h"
diff --git a/services/ui/public/interfaces/ime/typemaps.gni b/services/ws/public/mojom/ime/typemaps.gni
similarity index 71%
rename from services/ui/public/interfaces/ime/typemaps.gni
rename to services/ws/public/mojom/ime/typemaps.gni
index c6583d0..9785b15 100644
--- a/services/ui/public/interfaces/ime/typemaps.gni
+++ b/services/ws/public/mojom/ime/typemaps.gni
@@ -2,4 +2,4 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-typemaps = [ "//services/ui/public/interfaces/ime/ime.typemap" ]
+typemaps = [ "//services/ws/public/mojom/ime/ime.typemap" ]
diff --git a/services/ui/public/interfaces/input_devices/BUILD.gn b/services/ws/public/mojom/input_devices/BUILD.gn
similarity index 100%
rename from services/ui/public/interfaces/input_devices/BUILD.gn
rename to services/ws/public/mojom/input_devices/BUILD.gn
diff --git a/services/ui/public/interfaces/input_devices/OWNERS b/services/ws/public/mojom/input_devices/OWNERS
similarity index 100%
rename from services/ui/public/interfaces/input_devices/OWNERS
rename to services/ws/public/mojom/input_devices/OWNERS
diff --git a/services/ui/public/interfaces/input_devices/input_device_controller.mojom b/services/ws/public/mojom/input_devices/input_device_controller.mojom
similarity index 100%
rename from services/ui/public/interfaces/input_devices/input_device_controller.mojom
rename to services/ws/public/mojom/input_devices/input_device_controller.mojom
diff --git a/services/ui/public/interfaces/input_devices/input_device_server.mojom b/services/ws/public/mojom/input_devices/input_device_server.mojom
similarity index 100%
rename from services/ui/public/interfaces/input_devices/input_device_server.mojom
rename to services/ws/public/mojom/input_devices/input_device_server.mojom
diff --git a/services/ui/public/interfaces/remoting_event_injector.mojom b/services/ws/public/mojom/remoting_event_injector.mojom
similarity index 100%
rename from services/ui/public/interfaces/remoting_event_injector.mojom
rename to services/ws/public/mojom/remoting_event_injector.mojom
diff --git a/services/ui/public/interfaces/screen_provider_observer.mojom b/services/ws/public/mojom/screen_provider_observer.mojom
similarity index 85%
rename from services/ui/public/interfaces/screen_provider_observer.mojom
rename to services/ws/public/mojom/screen_provider_observer.mojom
index c49f3f5..e384ce3 100644
--- a/services/ui/public/interfaces/screen_provider_observer.mojom
+++ b/services/ws/public/mojom/screen_provider_observer.mojom
@@ -4,14 +4,14 @@
 
 module ui.mojom;
 
-import "services/ui/public/interfaces/window_tree_constants.mojom";
+import "services/ws/public/mojom/window_tree_constants.mojom";
 
 interface ScreenProviderObserver {
   // Sent when the observer is added as well as any time the set of displays
   // changes in any way. |displays| contains all known displays. If the system
   // that WS is running on has an integrated display, for example a laptop
   // internal display, then |internal_display_id| will be the corresponding
-  // dislay id. If there is no internal display then |internal_display_id| will
+  // display id. If there is no internal display then |internal_display_id| will
   // be kInvalidDisplayID. |display_id_for_new_windows| is the display on which
   // to place new top-level windows, usually the display on which a window was
   // last activated.
diff --git a/services/ui/public/interfaces/user_activity_monitor.mojom b/services/ws/public/mojom/user_activity_monitor.mojom
similarity index 100%
rename from services/ui/public/interfaces/user_activity_monitor.mojom
rename to services/ws/public/mojom/user_activity_monitor.mojom
diff --git a/services/ui/public/interfaces/window_manager.mojom b/services/ws/public/mojom/window_manager.mojom
similarity index 99%
rename from services/ui/public/interfaces/window_manager.mojom
rename to services/ws/public/mojom/window_manager.mojom
index d48fdc9..96025dd9 100644
--- a/services/ui/public/interfaces/window_manager.mojom
+++ b/services/ws/public/mojom/window_manager.mojom
@@ -11,7 +11,7 @@
   // . Properties that are used at creation time only and not persisted.
   // . Long lived properties. These properties may be changed at any time and
   //   are mapped to aura::Window properties. aura::PropertyConverter defines
-  //   the mapping between the property defined here and the corresonding
+  //   the mapping between the property defined here and the corresponding
   //   aura property. For properties defined in PropertyConverter any change to
   //   to the aura property is mapped to the property defined here and sent to
   //   all clients that know about the window (which is generally just the owner
diff --git a/services/ui/public/interfaces/window_manager_constants.mojom b/services/ws/public/mojom/window_manager_constants.mojom
similarity index 100%
rename from services/ui/public/interfaces/window_manager_constants.mojom
rename to services/ws/public/mojom/window_manager_constants.mojom
diff --git a/services/ui/public/interfaces/window_server_test.mojom b/services/ws/public/mojom/window_server_test.mojom
similarity index 100%
rename from services/ui/public/interfaces/window_server_test.mojom
rename to services/ws/public/mojom/window_server_test.mojom
diff --git a/services/ui/public/interfaces/window_tree.mojom b/services/ws/public/mojom/window_tree.mojom
similarity index 98%
rename from services/ui/public/interfaces/window_tree.mojom
rename to services/ws/public/mojom/window_tree.mojom
index bf8af6fe..5816255 100644
--- a/services/ui/public/interfaces/window_tree.mojom
+++ b/services/ws/public/mojom/window_tree.mojom
@@ -5,14 +5,14 @@
 module ui.mojom;
 
 import "mojo/public/mojom/base/unguessable_token.mojom";
-import "services/ui/public/interfaces/cursor/cursor.mojom";
-import "services/ui/public/interfaces/screen_provider_observer.mojom";
-import "services/ui/public/interfaces/window_manager_constants.mojom";
-import "services/ui/public/interfaces/window_tree_constants.mojom";
 import "services/viz/public/interfaces/compositing/compositor_frame_sink.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_info.mojom";
+import "services/ws/public/mojom/cursor/cursor.mojom";
+import "services/ws/public/mojom/screen_provider_observer.mojom";
+import "services/ws/public/mojom/window_manager_constants.mojom";
+import "services/ws/public/mojom/window_tree_constants.mojom";
 import "ui/base/mojo/ui_base_types.mojom";
 import "ui/events/mojo/event.mojom";
 import "ui/events/mojo/event_constants.mojom";
diff --git a/services/ui/public/interfaces/window_tree_constants.mojom b/services/ws/public/mojom/window_tree_constants.mojom
similarity index 100%
rename from services/ui/public/interfaces/window_tree_constants.mojom
rename to services/ws/public/mojom/window_tree_constants.mojom
diff --git a/storage/browser/fileapi/file_system_file_util.h b/storage/browser/fileapi/file_system_file_util.h
index f38e000d..bd0d0a9 100644
--- a/storage/browser/fileapi/file_system_file_util.h
+++ b/storage/browser/fileapi/file_system_file_util.h
@@ -125,14 +125,17 @@
                                      const FileSystemURL& url,
                                      int64_t length) = 0;
 
-  // Copies or moves a single file from |src_url| to |dest_url|.
-  // The filesystem type of |src_url| and |dest_url| MUST be same.
-  // For |option|, please see file_system_operation.h
+  // Copies a single file or moves a single file or directory from |src_url| to
+  // |dest_url|. Whether moving a directory is supported is
+  // implementation-defined. The filesystem type of |src_url| and |dest_url|
+  // MUST be same. For |option|, please see file_system_operation.h
   //
   // This returns:
   // - File::FILE_ERROR_NOT_FOUND if |src_url|
   //   or the parent directory of |dest_url| does not exist.
-  // - File::FILE_ERROR_NOT_A_FILE if |src_url| exists but is not a file.
+  // - File::FILE_ERROR_NOT_A_FILE if |src_url| exists but is not a file and the
+  //   operation is copy or the implementation does not support moving
+  //   directories.
   // - File::FILE_ERROR_INVALID_OPERATION if |dest_url| exists and
   //   is not a file.
   // - File::FILE_ERROR_FAILED if |dest_url| does not exist and
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 14c0b71..a5d1696c7 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -21694,51 +21694,6 @@
       {
         "args": [
           "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "experiment_percentage": 100,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "chrome_public_test_apk"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 20
-        },
-        "test": "chrome_public_test_apk"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_cardboard_skipdon_setupcomplete.json",
           "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk"
@@ -22004,100 +21959,6 @@
       },
       {
         "args": [
-          "--enable-surface-synchronization",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "experiment_percentage": 100,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "surface_sync_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "surface_sync_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 9
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--enable-features=VizDisplayCompositor",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "experiment_percentage": 100,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "viz_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "viz_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 10
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index fc48a3f..4c7285f 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -422,7 +422,7 @@
         "args": [
           "--enable-features=Mash",
           "--override-use-software-gl-for-tests",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mash.browser_tests.filter"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.mash.browser_tests.filter"
         ],
         "name": "mash_browser_tests",
         "swarming": {
@@ -434,6 +434,20 @@
       },
       {
         "args": [
+          "--enable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter"
+        ],
+        "name": "single_process_mash_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "hard_timeout": 1800,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
           "--enable-features=VizDisplayCompositor"
         ],
         "name": "viz_browser_tests",
@@ -1077,11 +1091,24 @@
         "args": [
           "--enable-features=Mash",
           "--override-use-software-gl-for-tests",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mash.browser_tests.filter"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.mash.browser_tests.filter"
         ],
         "name": "mash_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "hard_timeout": 1800
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--enable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter"
+        ],
+        "name": "single_process_mash_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
           "hard_timeout": 1800,
           "shards": 10
         },
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 9d4f873f..1af6fe87 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -1451,6 +1451,20 @@
       },
       {
         "args": [
+          "--enable-features=Mash",
+          "--override-use-software-gl-for-tests",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter"
+        ],
+        "name": "mash_fyi_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "hard_timeout": 1800,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
           "--enable-features=VizDisplayCompositor"
         ],
         "name": "viz_browser_tests",
@@ -1479,9 +1493,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--enable-features=Mash"
         ],
-        "name": "single_process_mash_content_unittests",
+        "name": "mash_fyi_content_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 09a32eb..25997ba7 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -4484,7 +4484,7 @@
         "args": [
           "--enable-features=Mash",
           "--override-use-software-gl-for-tests",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mash.browser_tests.filter"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.mash.browser_tests.filter"
         ],
         "name": "mash_browser_tests",
         "swarming": {
@@ -4496,6 +4496,20 @@
       },
       {
         "args": [
+          "--enable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter"
+        ],
+        "name": "single_process_mash_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "hard_timeout": 1800,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
           "--enable-features=VizDisplayCompositor"
         ],
         "name": "viz_browser_tests",
@@ -5138,7 +5152,7 @@
         "args": [
           "--enable-features=Mash",
           "--override-use-software-gl-for-tests",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mash.browser_tests.filter"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.mash.browser_tests.filter"
         ],
         "name": "mash_browser_tests",
         "swarming": {
@@ -5150,6 +5164,20 @@
       },
       {
         "args": [
+          "--enable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter"
+        ],
+        "name": "single_process_mash_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "hard_timeout": 1800,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
           "--enable-features=VizDisplayCompositor"
         ],
         "name": "viz_browser_tests",
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 65b07f1..3e48477 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -20,7 +20,9 @@
   testonly = true
 
   data = [
-    "//testing/buildbot/filters/mash.browser_tests.filter",
+    "//testing/buildbot/filters/chromeos.mash.browser_tests.filter",
+    "//testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter",
+    "//testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter",
     "//testing/buildbot/filters/mojo.fyi.network_browser_tests.filter",
     "//testing/buildbot/filters/webui_polymer2_browser_tests.filter",
   ]
diff --git a/testing/buildbot/filters/chromeos.mash.browser_tests.filter b/testing/buildbot/filters/chromeos.mash.browser_tests.filter
new file mode 100644
index 0000000..fb319e2d
--- /dev/null
+++ b/testing/buildbot/filters/chromeos.mash.browser_tests.filter
@@ -0,0 +1,4 @@
+# Sanity test that validates basic login functionality.  The full suite of
+# Mash browser tests runs on the mojo.fyi waterfall.
+# Context: http://crbug.com/874090
+LoginUtilsTest.MashLogin
diff --git a/testing/buildbot/filters/mash.browser_tests.filter b/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter
similarity index 100%
rename from testing/buildbot/filters/mash.browser_tests.filter
rename to testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter
diff --git a/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter b/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter
new file mode 100644
index 0000000..6512437
--- /dev/null
+++ b/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter
@@ -0,0 +1,6 @@
+# Placeholder tests that validate basic login functionality.
+# TODO: replace with full suite and blacklist once it's ready.
+# Bug: http://crbug.com/874090
+LoginPromptBrowserTest.*
+LoginUtilsTest.*
+LoginUIKeyboardTestWithUsersAndOwner.*
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 3442ef3..9db5a19 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -262,6 +262,10 @@
     ],
   },
   'chrome_public_test_apk': {
+    'remove_from': [
+      # TODO(crbug.com/877280): Add it back when we have the capacity.
+      'android-marshmallow-arm64-rel',
+    ],
     'modifications': {
       # chromium.android
       'android-kitkat-arm-rel': {
@@ -269,11 +273,6 @@
           'shards': 12,
         },
       },
-      'android-marshmallow-arm64-rel': {
-        # TODO(crbug.com/731759): Enable this once we're confident that it
-        # passes.
-        'experiment_percentage': 100,
-      },
       'KitKat Phone Tester (dbg)': {
         'swarming': {
           'shards': 20,
@@ -1031,14 +1030,15 @@
     },
   },
   'surface_sync_content_browsertests': {
+    'remove_from': [
+      # TODO(crbug.com/877280): Add it back when we have the capacity.
+      'android-marshmallow-arm64-rel',
+    ],
     'modifications': {
       # chromium.android
       'android-kitkat-arm-rel': {
         'experiment_percentage': 100,
       },
-      'android-marshmallow-arm64-rel': {
-        'experiment_percentage': 100,
-      },
     },
   },
   'sync_integration_tests': {
@@ -1261,15 +1261,14 @@
       'KitKat Phone Tester (dbg)',
       # Currently flaky timeouts on Windows 10. crbug.com/839824
       'Win10 Tests x64 (dbg)',
+      # TODO(crbug.com/877280): Add it back when we have the capacity.
+      'android-marshmallow-arm64-rel',
     ],
     'modifications': {
       # chromium.android
       'android-kitkat-arm-rel': {
         'experiment_percentage': 100,
       },
-      'android-marshmallow-arm64-rel': {
-        'experiment_percentage': 100,
-      },
       # chromium.clang
       'ToTLinuxUBSanVptr': {
         'swarming': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index f980bdb..825bcf4 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -1730,13 +1730,53 @@
      },
    },
 
-  'mash_chromium_gtests': {
+  'mash_fyi_chromium_gtests': {
+    'mash_fyi_browser_tests': {
+      'test': 'browser_tests',
+      'args': [
+        '--enable-features=Mash',
+        '--override-use-software-gl-for-tests',
+        '--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter',
+      ],
+      'swarming': {
+        'hard_timeout': 1800,
+        'shards': 10,
+      },
+    },
+    'mash_fyi_content_unittests': {
+      'test': 'content_unittests',
+      'args': [
+        '--enable-features=Mash',
+      ],
+    },
+  },
+
+  # These can be folded into linux_chromeos_specific_gtests once we stop moving
+  # tests around on the main waterfall.
+  'mash_chromium_gtests' : {
     'mash_browser_tests': {
       'test': 'browser_tests',
       'args': [
         '--enable-features=Mash',
         '--override-use-software-gl-for-tests',
-        '--test-launcher-filter-file=../../testing/buildbot/filters/mash.browser_tests.filter',
+        '--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.mash.browser_tests.filter',
+      ],
+      'swarming': {
+        'hard_timeout': 1800,
+        'shards': 1,
+      },
+    },
+  },
+
+  # These can be folded into linux_chromeos_specific_gtests once we stop moving
+  # tests around on the main waterfall.
+  'single_process_mash_chromium_gtests': {
+    'single_process_mash_browser_tests': {
+      'test': 'browser_tests',
+      'args': [
+        '--enable-features=SingleProcessMash',
+        '--override-use-software-gl-for-tests',
+        '--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter',
       ],
       'swarming': {
         'hard_timeout': 1800,
@@ -2024,6 +2064,7 @@
       ],
       'test': 'content_browsertests',
     },
+
     'not_site_per_process_content_unittests': {
       'args': [
         '--disable-site-isolation-trials'
@@ -2367,13 +2408,13 @@
     #   linux_chromium_gtests
     #   - non_android_and_cast_and_chromeos_chromium_gtests
     #   + linux_chromeos_specific_gtests
-    #   + mash_chromium_gtests
     'aura_gtests',
     'chromium_gtests',
     'chromium_gtests_for_devices_with_graphical_output',
     'chromium_gtests_for_linux_and_chromeos_only',
     'linux_chromeos_specific_gtests',
     'linux_flavor_specific_chromium_gtests',
+    'single_process_mash_chromium_gtests',
     'mash_chromium_gtests',
     'non_android_chromium_gtests',
   ],
@@ -2708,7 +2749,7 @@
 
   'mojo_chromiumos_fyi_gtests': [
     'aura_gtests',
-    'mash_chromium_gtests',
+    'mash_fyi_chromium_gtests',
     'mojo_chromiumos_specific_gtests',
     'viz_gtests',
     'viz_non_android_fyi_gtests',
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index e42b12c..60fabcc 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2242,6 +2242,7 @@
 # regularly on Windows. crbug.com/850964
 crbug.com/613672 [ Mac Win ] virtual/threaded/external/wpt/feature-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html [ Skip ]
 crbug.com/613672 [ Mac ] fast/events/touch/gesture/gesture-scroll.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/gesture-scroll-by-page.html [ Skip ]
 crbug.com/613672 [ Mac ] fast/events/touch/gesture/gesture-scrollbar-touchpad-fling.html [ Skip ]
 crbug.com/613672 [ Mac ] fast/events/touch/gesture/gesture-scrollbar-touchscreen-fling.html [ Skip ]
 crbug.com/613672 [ Mac ] fast/events/touch/gesture/gesture-scrollbar-mainframe.html [ Skip ]
@@ -2260,6 +2261,7 @@
 crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-page-zoomed.html [ Skip ]
 crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-page.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/gesture-scroll.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/gesture-scroll-by-page.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/gesture-scrollbar-touchpad-fling.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/gesture-scrollbar-touchscreen-fling.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/gesture-scrollbar-mainframe.html [ Skip ]
@@ -2278,6 +2280,7 @@
 crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-page-zoomed.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-page.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/gesture-scroll.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/gesture-scroll-by-page.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/gesture-scrollbar-touchpad-fling.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/gesture-scrollbar-touchscreen-fling.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/gesture-scrollbar-mainframe.html [ Skip ]
@@ -2296,6 +2299,7 @@
 crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-page-zoomed.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-page.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/gesture-scroll.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/gesture-scroll-by-page.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/gesture-scrollbar-touchpad-fling.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/gesture-scrollbar-touchscreen-fling.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/gesture-scrollbar-mainframe.html [ Skip ]
@@ -2314,6 +2318,7 @@
 crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-scroll-page-zoomed.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-scroll-page.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/gesture-scroll.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/gesture-scroll-by-page.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/gesture-scrollbar-mainframe.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/gesture-scrollbar.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden.html [ Skip ]
@@ -5034,3 +5039,6 @@
 
 # Sheriff 2018-08-23
 crbug.com/877183 [ Linux Win ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-003v.html [ Pass Failure ]
+
+# Sheriff 2018-08-24
+crbug.com/877299 external/wpt/editing/run/forwarddelete.html?1-1000 [ Timeout Pass ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index ebb79f1..8fc043c 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -168504,6 +168504,11 @@
      {}
     ]
    ],
+   "service-workers/service-worker/ServiceWorkerGlobalScope/isSecureContext.serviceworker.js": [
+    [
+     {}
+    ]
+   ],
    "service-workers/service-worker/ServiceWorkerGlobalScope/resources/close-worker.js": [
     [
      {}
@@ -252343,6 +252348,12 @@
      {}
     ]
    ],
+   "service-workers/service-worker/ServiceWorkerGlobalScope/isSecureContext.https.html": [
+    [
+     "/service-workers/service-worker/ServiceWorkerGlobalScope/isSecureContext.https.html",
+     {}
+    ]
+   ],
    "service-workers/service-worker/ServiceWorkerGlobalScope/postmessage.https.html": [
     [
      "/service-workers/service-worker/ServiceWorkerGlobalScope/postmessage.https.html",
@@ -355071,7 +355082,7 @@
    "support"
   ],
   "dom/events/Event-defaultPrevented.html": [
-   "cfd5d4815a12d59a212b7948bb58c6ce854afcc1",
+   "573c83e7b5469a0802f38b06a2a081a9ad919496",
    "testharness"
   ],
   "dom/events/Event-dispatch-bubble-canceled.html": [
@@ -406102,6 +406113,14 @@
    "43bdaf1a6bbcd07d7f351c7f46da8452ce0b6d59",
    "testharness"
   ],
+  "service-workers/service-worker/ServiceWorkerGlobalScope/isSecureContext.https.html": [
+   "41cc5f5179344c2a9c61401d19d55b718696285b",
+   "testharness"
+  ],
+  "service-workers/service-worker/ServiceWorkerGlobalScope/isSecureContext.serviceworker.js": [
+   "606ca087384898ef737f8e03081d49b807a6938f",
+   "support"
+  ],
   "service-workers/service-worker/ServiceWorkerGlobalScope/postmessage.https.html": [
    "590ee3c06c61aa6788a40a79c2d14fe813da11fe",
    "testharness"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/dom/events/Event-defaultPrevented.html b/third_party/WebKit/LayoutTests/external/wpt/dom/events/Event-defaultPrevented.html
index f023008..2548fa3e0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/dom/events/Event-defaultPrevented.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/dom/events/Event-defaultPrevented.html
@@ -40,6 +40,7 @@
   assert_equals(ev.defaultPrevented, true, "defaultPrevented");
 }, "preventDefault() should change defaultPrevented if cancelable is true.");
 test(function() {
+  ev.initEvent("foo", true, true);
   assert_equals(ev.cancelable, true, "cancelable (before)");
   ev.returnValue = false;
   assert_equals(ev.cancelable, true, "cancelable (after)");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/isSecureContext.https.html b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/isSecureContext.https.html
new file mode 100644
index 0000000..399820dd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/isSecureContext.https.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Service Worker: isSecureContext</title>
+</head>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/test-helpers.sub.js"></script>
+<script>
+'use strict';
+
+promise_test(async (t) => {
+    var url = 'isSecureContext.serviceworker.js';
+    var scope = 'empty.html';
+    var frame_sw, sw_registration;
+
+    await service_worker_unregister(t, scope);
+    var f = await with_iframe(scope);
+    t.add_cleanup(function() {
+        f.remove();
+    });
+    frame_sw = f.contentWindow.navigator.serviceWorker;
+    var registration = await navigator.serviceWorker.register(url, {scope: scope});
+    sw_registration = registration;
+    await wait_for_state(t, registration.installing, 'activated');
+    fetch_tests_from_worker(sw_registration.active);
+}, 'Setting up tests');
+
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/isSecureContext.serviceworker.js b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/isSecureContext.serviceworker.js
new file mode 100644
index 0000000..5033594e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/isSecureContext.serviceworker.js
@@ -0,0 +1,5 @@
+importScripts("/resources/testharness.js");
+
+test(() => {
+    assert_true(self.isSecureContext, true);
+}, "isSecureContext");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCIceTransport-extension.https.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCIceTransport-extension.https.html
index 1f7273f3..88a10bb 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCIceTransport-extension.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCIceTransport-extension.https.html
@@ -9,6 +9,12 @@
 // These tests are based on the following extension specification:
 // https://w3c.github.io/webrtc-ice/
 
+function makeIceTransport(t) {
+  const iceTransport = new RTCIceTransport();
+  t.add_cleanup(() => iceTransport.stop());
+  return iceTransport;
+}
+
 test(() => {
   const iceTransport = new RTCIceTransport();
 }, 'RTCIceTransport constructor does not throw.');
@@ -25,10 +31,98 @@
     'Expect no remote candidates');
   assert_equals(iceTransport.getSelectedCandidatePair(), null,
     'Expect no selected candidate pair');
-  assert_equals(iceTransport.getLocalParameters(), null,
-    'Expect no local parameters');
+  assert_not_equals(iceTransport.getLocalParameters(), null,
+    'Expect local parameters generated');
   assert_equals(iceTransport.getRemoteParameters(), null,
     'Expect no remote parameters');
 }, 'RTCIceTransport initial properties are set.');
 
+test(t => {
+  const iceTransport = makeIceTransport(t);
+  assert_throws(new TypeError(), () =>
+    iceTransport.gather({ iceServers: null }));
+}, 'gather() with { iceServers: null } should throw TypeError');
+
+test(t => {
+  const iceTransport = makeIceTransport(t);
+  iceTransport.gather({ iceServers: undefined });
+}, 'gather() with { iceServers: undefined } should succeed');
+
+test(t => {
+  const iceTransport = makeIceTransport(t);
+  iceTransport.gather({ iceServers: [{
+    urls: ['turns:turn.example.org', 'turn:turn.example.net'],
+    username: 'user',
+    credential: 'cred',
+  }] });
+}, 'gather() with one turns server, one turn server, username, credential' +
+    ' should succeed');
+
+test(t => {
+  const iceTransport = makeIceTransport(t);
+  iceTransport.gather({ iceServers: [{
+    urls: ['stun:stun1.example.net', 'stun:stun2.example.net'],
+  }] });
+}, 'gather() with 2 stun servers should succeed');
+
+test(t => {
+  const iceTransport = makeIceTransport(t);
+  iceTransport.stop();
+  assert_throws('InvalidStateError', () => iceTransport.gather({}));
+}, 'gather() throws if closed');
+
+test(t => {
+  const iceTransport = makeIceTransport(t);
+  iceTransport.gather({});
+  assert_equals(iceTransport.gatheringState, 'gathering');
+}, `gather() transitions gatheringState to 'gathering'`);
+
+test(t => {
+  const iceTransport = makeIceTransport(t);
+  iceTransport.gather({});
+  assert_throws('InvalidStateError', () => iceTransport.gather({}));
+}, 'gather() throws if called twice');
+
+promise_test(async t => {
+  const iceTransport = makeIceTransport(t);
+  const watcher = new EventWatcher(t, iceTransport, 'gatheringstatechange');
+  iceTransport.gather({});
+  await watcher.wait_for('gatheringstatechange');
+  assert_equals(iceTransport.gatheringState, 'complete');
+}, `eventually transition gatheringState to 'complete'`);
+
+promise_test(async t => {
+  const iceTransport = makeIceTransport(t);
+  const watcher = new EventWatcher(t, iceTransport,
+      [ 'icecandidate', 'gatheringstatechange' ]);
+  iceTransport.gather({});
+  let candidate;
+  do {
+    ({ candidate } = await watcher.wait_for('icecandidate'));
+  } while (candidate !== null);
+  assert_equals(iceTransport.gatheringState, 'gathering');
+  await watcher.wait_for('gatheringstatechange');
+  assert_equals(iceTransport.gatheringState, 'complete');
+}, 'onicecandidate fires with null candidate before gatheringState' +
+    ` transitions to 'complete'`);
+
+promise_test(async t => {
+  const iceTransport = makeIceTransport(t);
+  const watcher = new EventWatcher(t, iceTransport, 'icecandidate');
+  iceTransport.gather({});
+  const { candidate } = await watcher.wait_for('icecandidate');
+  assert_not_equals(candidate.candidate, '');
+  assert_array_equals(iceTransport.getLocalCandidates(), [candidate]);
+}, 'gather() returns at least one host candidate');
+
+promise_test(async t => {
+  const iceTransport = makeIceTransport(t);
+  const watcher = new EventWatcher(t, iceTransport, 'icecandidate');
+  iceTransport.gather({ gatherPolicy: 'relay' });
+  const { candidate } = await watcher.wait_for('icecandidate');
+  assert_equals(candidate, null);
+  assert_array_equals(iceTransport.getLocalCandidates(), []);
+}, `gather() returns no candidates with { gatherPolicy: 'relay'} and no turn` +
+    ' servers');
+
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window-expected.txt
index 4630fec..b4600751 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -321,10 +321,10 @@
 FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "getRemoteCertificates()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
 FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "onstatechange" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
 FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "onerror" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: existence and properties of interface object assert_equals: prototype of RTCIceTransport is not EventTarget expected function "function EventTarget() { [native code] }" but got function "function () { [native code] }"
+FAIL RTCIceTransport interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function () { [native code] }" did not throw
 PASS RTCIceTransport interface object length
 PASS RTCIceTransport interface object name
-FAIL RTCIceTransport interface: existence and properties of interface prototype object assert_equals: prototype of RTCIceTransport.prototype is not EventTarget.prototype expected object "[object EventTarget]" but got object "[object Object]"
+PASS RTCIceTransport interface: existence and properties of interface prototype object
 PASS RTCIceTransport interface: existence and properties of interface prototype object's "constructor" property
 PASS RTCIceTransport interface: existence and properties of interface prototype object's @@unscopables property
 PASS RTCIceTransport interface: attribute role
@@ -337,7 +337,7 @@
 PASS RTCIceTransport interface: operation getLocalParameters()
 PASS RTCIceTransport interface: operation getRemoteParameters()
 FAIL RTCIceTransport interface: attribute onstatechange assert_true: The prototype object must have a property "onstatechange" expected true got false
-FAIL RTCIceTransport interface: attribute ongatheringstatechange assert_true: The prototype object must have a property "ongatheringstatechange" expected true got false
+PASS RTCIceTransport interface: attribute ongatheringstatechange
 FAIL RTCIceTransport interface: attribute onselectedcandidatepairchange assert_true: The prototype object must have a property "onselectedcandidatepairchange" expected true got false
 FAIL RTCIceTransport must be primary interface of idlTestObjects.iceTransport assert_equals: wrong typeof object expected "object" but got "undefined"
 FAIL Stringification of idlTestObjects.iceTransport assert_equals: wrong typeof object expected "object" but got "undefined"
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-by-page-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-by-page-expected.txt
deleted file mode 100644
index 645e28d3..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-by-page-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This tests gesture scrolling by pages.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS document.scrollingElement.scrollTop >= window.innerHeight * 0.875 * 2 became true
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-by-page.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-by-page.html
index 3f53c10..fefdf85 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-by-page.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-by-page.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 ::-webkit-scrollbar {
     width: 0px;
@@ -20,8 +20,8 @@
 }
 
 </style>
-</head>
-<body style="margin:0" onload="runTest();">
+
+<body style="margin:0">
 
 <div id="greenbox"></div>
 <div id="redbox"></div>
@@ -29,32 +29,20 @@
 <p id="description"></p>
 <div id="console"></div>
 <script type="text/javascript">
+  // Turn on smooth scrolling.
+  internals.settings.setScrollAnimatorEnabled(true);
 
-function gestureScroll()
-{
-    eventSender.gestureScrollBegin("touchpad", 10, 20);
-    eventSender.gestureScrollUpdate("touchpad", 0, -1, "Page");
-    eventSender.gestureScrollUpdate("touchpad", 0, -2, "Page");
-    eventSender.gestureScrollUpdate("touchpad", 0, 1, "Page");
-    eventSender.gestureScrollEnd("touchpad", 0, 0);
+  var x = 30;
+  var y = 30;
+  var mouse_input = GestureSourceType.MOUSE_INPUT;
 
-    // see kMinFractionToStepWhenPaging in ScrollableArea.cppP
-    // 2 is the expected number of pages scrolled (-1 + -2 + 1)
-    shouldBecomeEqual("document.scrollingElement.scrollTop >= window.innerHeight * 0.875 * 2", "true", finishJSTest, 1000);
-}
+promise_test (async () => {
+  await smoothScroll(1, x, y, mouse_input, 'down', SPEED_INSTANT, false, true);
+  await smoothScroll(1, x, y, mouse_input, 'down', SPEED_INSTANT, false, true);
+  await waitFor(() => {
+    return document.scrollingElement.scrollTop >= window.innerHeight * 0.875 * 2; })
+});
 
-jsTestIsAsync = true;
-
-function runTest()
-{
-    if (window.eventSender) {
-        description('This tests gesture scrolling by pages.');
-        gestureScroll();
-    } else {
-        debug("This test requires DumpRenderTree.  Gesture-scroll the page to validate the implementation.");
-    }
-}
 </script>
 
 </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-display-contents-crash-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-display-contents-crash-expected.txt
deleted file mode 100644
index 88e5335..0000000
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-display-contents-crash-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-Shouldn't crash.
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-display-contents-crash.html b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-display-contents-crash.html
index 0623be38..44fb1553 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-display-contents-crash.html
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-display-contents-crash.html
@@ -1,13 +1,12 @@
 <div style="display: contents"><a href="#"></a></div>
 Shouldn't crash.
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script>
-testRunner.dumpAsText();
-testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-
-function runTest() {
-  eventSender.keyDown("ArrowLeft");
-}
-
-window.onload = runTest;
+    test(() => {
+        assert_true(!!window.testRunner);
+        testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
+        testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
+        eventSender.keyDown("ArrowLeft");
+    }, "No crash or assertion failure");
 </script>
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-div-in-anchor-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-div-in-anchor-expected.txt
deleted file mode 100644
index 1c858f0..0000000
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-div-in-anchor-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-DivInLinkA
-DivInLinkB
- PASS successfullyParsed is true
-
-TEST COMPLETE
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e1"
-This test is testing that we can navigate on links with divs inside.
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-div-in-anchor.html b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-div-in-anchor.html
index dbefb71..0337d10 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-div-in-anchor.html
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-div-in-anchor.html
@@ -1,43 +1,15 @@
-<html>
-  <head>
-    <script src="../../resources/js-test.js"></script>
-    <script src="resources/spatial-navigation-utils.js"></script>
-    <script type="application/javascript">
-
-    var resultMap = [
-      ["Down", "e1"],
-      ["DONE", "DONE"]
-    ];
-
-    if (window.testRunner) {
-      testRunner.dumpAsText();
-      testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-      testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-      testRunner.waitUntilDone();
-    }
-
-    function runTest()
-    {
-      // starting the test itself: get to a known place.
-      document.getElementById("start").focus();
-
-      initTest(resultMap, testCompleted);
-    }
-
-    function testCompleted()
-    {
-      if (window.testRunner)
-        testRunner.notifyDone();
-    }
-
-    window.onload = runTest;
-    </script>
-  </head>
-<body id="some-content" xmlns="http://www.w3.org/1999/xhtml" style="padding:20px">
 <a href="#" id="start"><div>DivInLinkA</div></a>
 <a href="#" id="e1"><div>DivInLinkB</div></a>
 
-<div id="console"></div>
-This test is testing that we can navigate on links with divs inside.
-</body>
-</html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
+<script>
+  var resultMap = [
+    ["Down", "e1"],
+  ];
+
+  // Start at a known place.
+  document.getElementById("start").focus();
+  snav.assertFocusMoves(resultMap);
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-fully-aligned-horizontally-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-fully-aligned-horizontally-expected.txt
deleted file mode 100644
index 60c3770a..0000000
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-fully-aligned-horizontally-expected.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-test
-H                               ow Now Brown Cow
-Ho                                w Now Brown Cow
-How                                 Now Brown Cow
-How N                                ow Brown Cow
-How No                                w Brown Cow
-How Now                                 Brown Cow
-How Now B                                rown Cow
-How Now Br                                own Cow
-How Now Bro                                wn Cow
-How Now Brow                                n Cow
-How Now Brown                                 Cow
-How Now Brown C                                ow
-How Now Brown Co                                w
-How Now Brown Cow
-
-test
- PASS successfullyParsed is true
-
-TEST COMPLETE
-PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "4"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "3"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "5"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "6"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "8"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "7"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "9"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "10"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "12"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "11"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "13"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "14"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "16"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "15"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "17"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "18"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "20"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "19"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "21"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "22"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "24"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "23"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "25"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "26"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "27"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
-
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-fully-aligned-horizontally.html b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-fully-aligned-horizontally.html
index d40935d..faacd22a 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-fully-aligned-horizontally.html
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-fully-aligned-horizontally.html
@@ -1,98 +1,58 @@
-<html>
-  <!--
-    This test ensures the correctness of the "Fully aligned" precedence
-    logic implemented by Spatial Navigation algorithm in an horizontal direction:
-    targets whose middle falls between the top and bottom of the current focused
-    element are preferably to move focus to, even if it is not the shortest distance.
+<a id="start" href="a">test<br></a>
+<a id="1" href="a">H</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<a id="2" href="p">ow Now Brown Cow</a><br>
+<a id="3" href="a">Ho</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp; <a id="4" href="p">w Now Brown Cow</a><br>
+<a id="5" href="a">How</a> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a id="6" href="p">Now Brown Cow</a><br>
+<a id="7" href="a">How N</a> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; <a id="8" href="p">ow Brown Cow</a><br>
+<a id="9" href="a">How No</a> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <a id="10" href="p">w Brown Cow</a><br>
+<a id="11" href="a">How Now </a>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <a id="12" href="p">Brown Cow</a><br>
+<a id="13" href="a">How Now B</a> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <a id="14" href="p">rown Cow</a><br>
+<a id="15" href="a">How Now Br</a> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <a id="16" href="p">own Cow</a><br>
+<a id="17" href="a">How Now Bro</a> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <a id="18" href="p">wn Cow</a><br>
+<a id="19" href="a">How Now Brow</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a id="20" href="p">n Cow</a><br>
+<a id="21" href="a">How Now Brown</a> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; <a id="22" href="p">Cow</a><br>
+<a id="23" href="a">How Now Brown C</a> &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <a id="24" href="p">ow</a><br>
+<a id="25" href="a">How Now Brown Co</a> &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <a id="26" href="p">w</a><br>
+<a id="27" href="a">How Now Brown Cow</a><br><br>
+<a id="end" href="a">test<br></a>
 
-    * Pre-conditions:
-    1) DRT support for SNav enable/disable.
+<p>This test ensures the correctness of the "Fully aligned" precedence logic implemented by Spatial Navigation algorithm in an horizontal direction: targets whose middle falls between the top and bottom of the current focused element are preferably to move focus to, even if it is not the shortest distance.</p>
 
-    * Navigation steps:
-    1) Loads this page, focus goes to "start" automatically.
-    2) Focus moves preferably to elements right up or above the
-       current focused element, even when there are some other closer
-       but not vertically aligned elements in the same direction.
-  -->
-  <head>
-    <script src="../../resources/js-test.js"></script>
-    <script src="resources/spatial-navigation-utils.js"></script>
-    <script type="application/javascript">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
+<script>
+  var resultMap = [
+    ["Down", "1"],
+    ["Right", "2"],
+    ["Down", "4"],
+    ["Left", "3"],
+    ["Down", "5"],
+    ["Right", "6"],
+    ["Down", "8"],
+    ["Left", "7"],
+    ["Down", "9"],
+    ["Right", "10"],
+    ["Down", "12"],
+    ["Left", "11"],
+    ["Down", "13"],
+    ["Right", "14"],
+    ["Down", "16"],
+    ["Left", "15"],
+    ["Down", "17"],
+    ["Right", "18"],
+    ["Down", "20"],
+    ["Left", "19"],
+    ["Down", "21"],
+    ["Right", "22"],
+    ["Down", "24"],
+    ["Left", "23"],
+    ["Down", "25"],
+    ["Right", "26"],
+    ["Down", "27"],
+    ["Down", "end"],
+  ];
 
-    var resultMap = [
-      ["Down", "1"],
-      ["Right", "2"],
-      ["Down", "4"],
-      ["Left", "3"],
-      ["Down", "5"],
-      ["Right", "6"],
-      ["Down", "8"],
-      ["Left", "7"],
-      ["Down", "9"],
-      ["Right", "10"],
-      ["Down", "12"],
-      ["Left", "11"],
-      ["Down", "13"],
-      ["Right", "14"],
-      ["Down", "16"],
-      ["Left", "15"],
-      ["Down", "17"],
-      ["Right", "18"],
-      ["Down", "20"],
-      ["Left", "19"],
-      ["Down", "21"],
-      ["Right", "22"],
-      ["Down", "24"],
-      ["Left", "23"],
-      ["Down", "25"],
-      ["Right", "26"],
-      ["Down", "27"],
-      ["Down", "end"],
-      ["DONE", "DONE"]
-    ];
-
-    if (window.testRunner) {
-      testRunner.dumpAsText();
-      testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-      testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-      testRunner.waitUntilDone();
-    }
-
-    function runTest()
-    {
-      // starting the test itself: get to a known place.
-      document.getElementById("start").focus();
-
-      initTest(resultMap, testCompleted);
-    }
-
-    function testCompleted()
-    {
-      if (window.testRunner)
-        testRunner.notifyDone();
-    }
-
-    window.onload = runTest;
-    </script>
-  </head>
-  <body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
-    <a id="start" href="a">test<br></a>
-    <a id="1" href="a">H</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<a id="2" href="p">ow Now Brown Cow</a><br>
-    <a id="3" href="a">Ho</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp; <a id="4" href="p">w Now Brown Cow</a><br>
-    <a id="5" href="a">How</a> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a id="6" href="p">Now Brown Cow</a><br>
-    <a id="7" href="a">How N</a> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; <a id="8" href="p">ow Brown Cow</a><br>
-    <a id="9" href="a">How No</a> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <a id="10" href="p">w Brown Cow</a><br>
-    <a id="11" href="a">How Now </a>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <a id="12" href="p">Brown Cow</a><br>
-    <a id="13" href="a">How Now B</a> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <a id="14" href="p">rown Cow</a><br>
-    <a id="15" href="a">How Now Br</a> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <a id="16" href="p">own Cow</a><br>
-    <a id="17" href="a">How Now Bro</a> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <a id="18" href="p">wn Cow</a><br>
-    <a id="19" href="a">How Now Brow</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a id="20" href="p">n Cow</a><br>
-    <a id="21" href="a">How Now Brown</a> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; <a id="22" href="p">Cow</a><br>
-    <a id="23" href="a">How Now Brown C</a> &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <a id="24" href="p">ow</a><br>
-    <a id="25" href="a">How Now Brown Co</a> &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <a id="26" href="p">w</a><br>
-    <a id="27" href="a">How Now Brown Cow</a><br><br>
-    <a id="end" href="a">test<br></a>
-    <div id="console"></div>
-  </body>
-</html>
-
+  // Start at a known place.
+  document.getElementById("start").focus();
+  snav.assertFocusMoves(resultMap);
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-fully-aligned-vertically-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-fully-aligned-vertically-expected.txt
deleted file mode 100644
index d2657a7..0000000
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-fully-aligned-vertically-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-test
-test             test
-test
-test
-test
-test
-
-
-
-
-test
-test
- PASS successfullyParsed is true
-
-TEST COMPLETE
-PASS gFocusedDocument.activeElement.getAttribute("id") is "4"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "6"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "7"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "8"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "6"
-
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-fully-aligned-vertically.html b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-fully-aligned-vertically.html
index abca83a3..29801b6 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-fully-aligned-vertically.html
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-fully-aligned-vertically.html
@@ -1,81 +1,41 @@
-<html>
-  <!--
-    This test ensures the correctness of the "Fully aligned" precedence
-    logic implemented by Spatial Navigation algorithm in an vertical direction:
-    targets whose middle falls between the top and bottom of the current focused
-    element are preferably to move focus to, even if it is not the shortest distance.
+<div style="margin-left: 40px; text-align: left;">
+  <div style="margin-left: 40px;">
+    <a id="start" href="a">test<br></a>
+  </div>
+  <a id="2" href="a">test</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a id="3" href="a">test</a><br>
+  <div style="margin-left: 40px;">
+    <a id="4" href="a">test<br></a>
+  </div>
+  <div style="margin-left: 80px;">
+    <a id="5" href="a">test<br></a>
+  </div>
+  <div style="margin-left: 40px;">
+    <a id="6" href="a">test<br></a>
+  </div>
+  <a id="7" href="a">test<br></a>
+  <br><br><br><br>
+  <div style="margin-left: 40px;">
+    <a id="8" href="a">test<br></a>
+  </div>
+  <a id="end" href="a">test<br></a>
+</div>
 
-    * Pre-conditions:
-    1) DRT support for SNav enable/disable.
+<p>This test ensures the correctness of the "Fully aligned" precedence logic implemented by Spatial Navigation algorithm in an vertical direction: targets whose middle falls between the top and bottom of the current focused element are preferably to move focus to, even if it is not the shortest distance.</p>
 
-    * Navigation steps:
-    1) Loads this page, focus goes to "start" automatically.
-    2) Focus moves preferably to elements right up or above the
-       current focused element, even when there are some other closer
-       but not vertically aligned elements in the same direction.
-  -->
-  <head>
-    <script src="../../resources/js-test.js"></script>
-    <script src="resources/spatial-navigation-utils.js"></script>
-    <script type="application/javascript">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
+<script>
+  var resultMap = [
+    ["Down", "4"],
+    ["Down", "6"],
+    ["Down", "7"],
+    ["Down", "end"],
+    ["Up", "8"],
+    ["Up", "6"],
+  ];
 
-    var resultMap = [
-      ["Down", "4"],
-      ["Down", "6"],
-      ["Down", "7"],
-      ["Down", "end"],
-      ["Up", "8"],
-      ["Up", "6"],
-      ["DONE", "DONE"]
-    ];
-
-    if (window.testRunner) {
-      testRunner.dumpAsText();
-      testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-      testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-      testRunner.waitUntilDone();
-    }
-
-    function runTest()
-    {
-      // starting the test itself: get to a known place.
-      document.getElementById("start").focus();
-
-      initTest(resultMap, testCompleted);
-    }
-
-    function testCompleted()
-    {
-      if (window.testRunner)
-        testRunner.notifyDone();
-    }
-
-    window.onload = runTest;
-    </script>
-  </head>
-  <body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
-    <div style="margin-left: 40px; text-align: left;">
-      <div style="margin-left: 40px;">
-        <a id="start" href="a">test<br></a>
-      </div>
-      <a id="2" href="a">test</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a id="3" href="a">test</a><br>
-      <div style="margin-left: 40px;">
-        <a id="4" href="a">test<br></a>
-      </div>
-      <div style="margin-left: 80px;">
-        <a id="5" href="a">test<br></a>
-      </div>
-      <div style="margin-left: 40px;">
-        <a id="6" href="a">test<br></a>
-      </div>
-      <a id="7" href="a">test<br></a>
-      <br><br><br><br>
-      <div style="margin-left: 40px;">
-        <a id="8" href="a">test<br></a>
-      </div>
-      <a id="end" href="a">test<br></a>
-    </div>
-    <div id="console"></div>
-  </body>
-</html>
-
+  // Start at a known place.
+  document.getElementById("start").focus();
+  snav.assertFocusMoves(resultMap);
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-focusable-element-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-focusable-element-expected.txt
deleted file mode 100644
index 40bbddea..0000000
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-focusable-element-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is link_1.
-
-
-This is hidden link.
-This is link_2.
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
-This test is to test that focusable elements with display:none do not grab the focus.
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-focusable-element.html b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-focusable-element.html
index baf820dcf..7de1065 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-focusable-element.html
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-focusable-element.html
@@ -1,45 +1,16 @@
-<html>
-  <head>
-    <script src="../../resources/js-test.js"></script>
-    <script src="resources/spatial-navigation-utils.js"></script>
-    <script type="application/javascript">
+<p>This is <a id="start" href="a">link_1</a>.</p>
+<br>This is <a id="1" style="display:none;" href="a">you should not see me</a> hidden link.<br>
+<p>This is <a id="end" href="a">link_2</a>.</p>
 
-    var resultMap = [
-      ["Down", "end"],
-      ["DONE", "DONE"]
-    ];
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
+<script>
+  var resultMap = [
+    ["Down", "end"],
+  ];
 
-    if (window.testRunner) {
-      testRunner.dumpAsText();
-      testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-      testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-      testRunner.waitUntilDone();
-    }
-
-    function runTest()
-    {
-      // starting the test itself: get to a known place.
-      document.getElementById("start").focus();
-
-      initTest(resultMap, testCompleted);
-    }
-
-    function testCompleted()
-    {
-      if (window.testRunner)
-        testRunner.notifyDone();
-    }
-
-    window.onload = runTest;
-
-    </script>
-  </head>
-
-  <body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
-      <p>This is <a id="start" href="a">link_1</a>.</p>
-      <br>This is <a id="1" style="display:none;" href="a">you should not see me</a> hidden link.<br>
-      <p>This is <a id="end" href="a">link_2</a>.</p>
-    <div id="console"></div>
-    <p>This test is to test that focusable elements with display:none do not grab the focus.</p>
-  </body>
-</html>
+  // Start at a known place.
+  document.getElementById("start").focus();
+  snav.assertFocusMoves(resultMap);
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-area-not-focusable-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-area-not-focusable-expected.txt
deleted file mode 100644
index e7a08aca..0000000
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-area-not-focusable-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
- 
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "3"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
-This test tests that areas of an imagemap without an href are not focusable, thus can not be reached with spatial navigation.
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-area-not-focusable.html b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-area-not-focusable.html
index 187e7aa..4cbde2d4 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-area-not-focusable.html
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-area-not-focusable.html
@@ -1,57 +1,28 @@
-<html>
-  <head>
+<map name="map" title="map" id="firstmap">
+  <area shape="rect" coords="20,20,70,70" href="#" id="1">
+  <area shape="rect" coords="20,130,70,180" id="2">
+</map>
 
-    <script src="../../resources/js-test.js"></script>
-    <script src="resources/spatial-navigation-utils.js"></script>
-    <script type="application/javascript">
+<a id="start" href="a"><img src="resources/green.png" width=50px height=50px></a>
+<div>
+  <img src="resources/green.png" width=200px height=200px usemap="#map"><a id="6" href="a"><img src="resources/green.png" width=50px height=50px></a>
+</div>
+<a id="3" href="a"><img src="resources/green.png" width=50px height=50px></a>
 
-    var resultMap = [
-      ["Down", "1"],
-      ["Down", "3"],
-      ["Up", "1"],
-      ["Up", "start"],
-      ["DONE", "DONE"]
-    ];
+<p>This test tests that areas of an imagemap without an href are not focusable, thus can not be reached with spatial navigation.</p>
 
-    if (window.testRunner) {
-      testRunner.dumpAsText();
-      testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-      testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-      testRunner.waitUntilDone();
-    }
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
+<script>
+  var resultMap = [
+    ["Down", "1"],
+    ["Down", "3"],
+    ["Up", "1"],
+    ["Up", "start"],
+  ];
 
-    function runTest()
-    {
-      // starting the test itself: get to a known place.
-      document.getElementById("start").focus();
-
-      initTest(resultMap, testCompleted);
-    }
-
-    function testCompleted()
-    {
-      if (window.testRunner)
-        testRunner.notifyDone();
-    }
-
-    window.onload = runTest;
-
-    </script>
-  </head>
-
-  <body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
-  <map name="map" title="map" id="firstmap">
-    <area shape="rect" coords="20,20,70,70" href="#" id="1">
-    <area shape="rect" coords="20,130,70,180" id="2">
-  </map>
-
-    <a id="start" href="a"><img src="resources/green.png" width=50px height=50px></a>
-    <div>
-      <img src="resources/green.png" width=200px height=200px usemap="#map"><a id="6" href="a"><img src="resources/green.png" width=50px height=50px></a>
-    </div>
-    <a id="3" href="a"><img src="resources/green.png" width=50px height=50px></a>
-    <div id="console"></div>
-    <div>This test tests that areas of an imagemap without an href are not focusable, thus can not be reached with spatial navigation.</div>
-  </body>
-</html>
-
+  // Start at a known place.
+  document.getElementById("start").focus();
+  snav.assertFocusMoves(resultMap);
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-area-without-image-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-area-without-image-expected.txt
deleted file mode 100644
index bd9c9022..0000000
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-area-without-image-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
- 
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-PASS gFocusedDocument.activeElement.getAttribute("id") is "3"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
-This test tests that areas of an imagemap without an image using it, are not focusable, thus can not be reached with spatial navigation.
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-area-without-image.html b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-area-without-image.html
index 13e9b737..b5f4246 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-area-without-image.html
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-area-without-image.html
@@ -1,55 +1,26 @@
-<html>
-  <head>
+<map name="map" title="map" id="firstmap">
+  <area shape="rect" coords="20,20,70,70" href="#" id="1">
+  <area shape="rect" coords="20,130,70,180" href="#" id="2">
+</map>
 
-    <script src="../../resources/js-test.js"></script>
-    <script src="resources/spatial-navigation-utils.js"></script>
-    <script type="application/javascript">
+<a id="start" href="a"><img src="resources/green.png" width=50px height=50px></a>
+<div>
+  <img src="resources/green.png" width=200px height=200px usemap="#othermap"><a id="6" href="a"><img src="resources/green.png" width=50px height=50px></a>
+</div>
+<a id="3" href="a"><img src="resources/green.png" width=50px height=50px></a>
 
-    var resultMap = [
-      ["Down", "3"],
-      ["Up", "start"],
-      ["DONE", "DONE"]
-    ];
+<p>This test tests that areas of an imagemap without an image using it, are not focusable, thus can not be reached with spatial navigation.</p>
 
-    if (window.testRunner) {
-      testRunner.dumpAsText();
-      testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-      testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-      testRunner.waitUntilDone();
-    }
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
+<script>
+  var resultMap = [
+    ["Down", "3"],
+    ["Up", "start"],
+  ];
 
-    function runTest()
-    {
-      // starting the test itself: get to a known place.
-      document.getElementById("start").focus();
-
-      initTest(resultMap, testCompleted);
-    }
-
-    function testCompleted()
-    {
-      if (window.testRunner)
-        testRunner.notifyDone();
-    }
-
-    window.onload = runTest;
-
-    </script>
-  </head>
-
-  <body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
-  <map name="map" title="map" id="firstmap">
-    <area shape="rect" coords="20,20,70,70" href="#" id="1">
-    <area shape="rect" coords="20,130,70,180" href="#" id="2">
-  </map>
-
-    <a id="start" href="a"><img src="resources/green.png" width=50px height=50px></a>
-    <div>
-      <img src="resources/green.png" width=200px height=200px usemap="#othermap"><a id="6" href="a"><img src="resources/green.png" width=50px height=50px></a>
-    </div>
-    <a id="3" href="a"><img src="resources/green.png" width=50px height=50px></a>
-    <div id="console"></div>
-    <div>This test tests that areas of an imagemap without an image using it, are not focusable, thus can not be reached with spatial navigation.</div>
-  </body>
-</html>
-
+  // Start at a known place.
+  document.getElementById("start").focus();
+  snav.assertFocusMoves(resultMap);
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-overlapped-areas-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-overlapped-areas-expected.txt
deleted file mode 100644
index 7d7984ba..0000000
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-overlapped-areas-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
- 
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-PASS gFocusedDocument.activeElement.getAttribute("id") is "4"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "5"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "4"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "3"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "3"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
-This test tests that areas of an imagemap can be reached with spatial navigation even if they are overlapped.
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-overlapped-areas.html b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-overlapped-areas.html
index 0641d6e..22aab431f 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-overlapped-areas.html
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-overlapped-areas.html
@@ -1,67 +1,38 @@
-<html>
-  <head>
+<map name="map" title="map" id="firstmap">
+  <area shape="circle" coords="45,45,25" href="#" id="1">
+  <area shape="rect" coords="45,60,95,110" href="#" id="2">
+  <area shape="poly" coords="80,20,130,20,130,180,30,180,30,130,80,130" href="#" id="3">
+  <area shape="default" href="#" id="4">
+</map>
 
-    <script src="../../resources/js-test.js"></script>
-    <script src="resources/spatial-navigation-utils.js"></script>
-    <script type="application/javascript">
+<a id="start" href="a"><img src="resources/green.png" width=50px height=50px></a>
+<div>
+  <img src="resources/green.png" width=200px height=200px usemap="#map">
+</div>
+<a id="5" href="a"><img src="resources/green.png" width=50px height=50px></a>
 
-    var resultMap = [
-      ["Down", "4"],
-      ["Down", "1"],
-      ["Down", "2"],
-      ["Down", "5"],
-      ["Up", "4"],
-      ["Up", "3"],
-      ["Up", "2"],
-      ["Left", "1"],
-      ["Right", "3"],
-      ["Left", "2"],
-      ["Up", "1"],
-      ["Up", "start"],
-      ["DONE", "DONE"]
-    ];
+<p>This test tests that areas of an imagemap can be reached with spatial navigation even if they are overlapped.</p>
 
-    if (window.testRunner) {
-      testRunner.dumpAsText();
-      testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-      testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-      testRunner.waitUntilDone();
-    }
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
+<script>
+  var resultMap = [
+    ["Down", "4"],
+    ["Down", "1"],
+    ["Down", "2"],
+    ["Down", "5"],
+    ["Up", "4"],
+    ["Up", "3"],
+    ["Up", "2"],
+    ["Left", "1"],
+    ["Right", "3"],
+    ["Left", "2"],
+    ["Up", "1"],
+    ["Up", "start"],
+  ];
 
-    function runTest()
-    {
-      // starting the test itself: get to a known place.
-      document.getElementById("start").focus();
-
-      initTest(resultMap, testCompleted);
-    }
-
-    function testCompleted()
-    {
-      if (window.testRunner)
-        testRunner.notifyDone();
-    }
-
-    window.onload = runTest;
-
-    </script>
-  </head>
-
-  <body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
-  <map name="map" title="map" id="firstmap">
-    <area shape="circle" coords="45,45,25" href="#" id="1">
-    <area shape="rect" coords="45,60,95,110" href="#" id="2">
-    <area shape="poly" coords="80,20,130,20,130,180,30,180,30,130,80,130" href="#" id="3">
-    <area shape="default" href="#" id="4">
-  </map>
-
-    <a id="start" href="a"><img src="resources/green.png" width=50px height=50px></a>
-    <div>
-      <img src="resources/green.png" width=200px height=200px usemap="#map">
-    </div>
-    <a id="5" href="a"><img src="resources/green.png" width=50px height=50px></a>
-    <div id="console"></div>
-    <div>This test tests that areas of an imagemap can be reached with spatial navigation even if they are overlapped.</div>
-  </body>
-</html>
-
+  // Start at a known place.
+  document.getElementById("start").focus();
+  snav.assertFocusMoves(resultMap);
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-simple-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-simple-expected.txt
deleted file mode 100644
index 83ba4df..0000000
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-simple-expected.txt
+++ /dev/null
@@ -1,17 +0,0 @@
- 
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "3"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "5"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "3"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "4"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "6"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "4"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
-This test tests that areas of an imagemap can be reached with spatial navigation.
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-simple.html b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-simple.html
index b9ce67d..5691c28 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-simple.html
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-imagemap-simple.html
@@ -1,65 +1,36 @@
-<html>
-  <head>
+<map name="map" title="map" id="firstmap">
+  <area shape="rect" coords="20,20,70,70" href="#" id="1">
+  <area shape="rect" coords="130,20,180,70" href="#" id="2">
+  <area shape="rect" coords="20,130,70,180" href="#" id="3">
+  <area shape="rect" coords="130,130,180,180" href="#" id="4">
+</map>
 
-    <script src="../../resources/js-test.js"></script>
-    <script src="resources/spatial-navigation-utils.js"></script>
-    <script type="application/javascript">
+<a id="start" href="a"><img src="resources/green.png" width=50px height=50px></a>
+<div>
+  <img src="resources/green.png" width=200px height=200px usemap="#map"><a id="6" href="a"><img src="resources/green.png" width=50px height=50px></a>
+</div>
+<a id="5" href="a"><img src="resources/green.png" width=50px height=50px></a>
 
-    var resultMap = [
-      ["Down", "1"],
-      ["Down", "3"],
-      ["Down", "5"],
-      ["Up", "3"],
-      ["Right", "4"],
-      ["Right", "6"],
-      ["Left", "4"],
-      ["Up", "2"],
-      ["Left", "1"],
-      ["Up", "start"],
-      ["DONE", "DONE"]
-    ];
+<p>This test tests that areas of an imagemap can be reached with spatial navigation.</p>
 
-    if (window.testRunner) {
-      testRunner.dumpAsText();
-      testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-      testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-      testRunner.waitUntilDone();
-    }
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
+<script>
+  var resultMap = [
+    ["Down", "1"],
+    ["Down", "3"],
+    ["Down", "5"],
+    ["Up", "3"],
+    ["Right", "4"],
+    ["Right", "6"],
+    ["Left", "4"],
+    ["Up", "2"],
+    ["Left", "1"],
+    ["Up", "start"],
+  ];
 
-    function runTest()
-    {
-      // starting the test itself: get to a known place.
-      document.getElementById("start").focus();
-
-      initTest(resultMap, testCompleted);
-    }
-
-    function testCompleted()
-    {
-      if (window.testRunner)
-        testRunner.notifyDone();
-    }
-
-    window.onload = runTest;
-
-    </script>
-  </head>
-
-  <body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
-  <map name="map" title="map" id="firstmap">
-    <area shape="rect" coords="20,20,70,70" href="#" id="1">
-    <area shape="rect" coords="130,20,180,70" href="#" id="2">
-    <area shape="rect" coords="20,130,70,180" href="#" id="3">
-    <area shape="rect" coords="130,130,180,180" href="#" id="4">
-  </map>
-
-    <a id="start" href="a"><img src="resources/green.png" width=50px height=50px></a>
-    <div>
-      <img src="resources/green.png" width=200px height=200px usemap="#map"><a id="6" href="a"><img src="resources/green.png" width=50px height=50px></a>
-    </div>
-    <a id="5" href="a"><img src="resources/green.png" width=50px height=50px></a>
-    <div id="console"></div>
-    <div>This test tests that areas of an imagemap can be reached with spatial navigation.</div>
-  </body>
-</html>
-
+  // Start at a known place.
+  document.getElementById("start").focus();
+  snav.assertFocusMoves(resultMap);
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-media-elements-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-media-elements-expected.txt
deleted file mode 100644
index 4910987..0000000
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-media-elements-expected.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-This is a link start of Test.
-
-This is a link i2.
-
-This is a link i4.
-
-This is a link i6.
-
-This is a link i8.
-
-This is a link End of Test.
-
-PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "v1"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "i2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "v3"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "i4"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "v5"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "i6"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "a7"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "i8"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "i8"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-This tests that a media elements ie: <Audio> or <video>, without tabindex are able to be reached through keyboard access
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-media-elements.html b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-media-elements.html
index 84019d64..fb42a2d 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-media-elements.html
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-media-elements.html
@@ -1,72 +1,39 @@
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-  <head>
-    <script src="../../resources/js-test.js"></script>
-    <script src="resources/spatial-navigation-utils.js"></script>
-    <script type="application/javascript">
+<p>This is a link <a id="start" href="a">start of Test</a>.</p>
+<video id="v1" controls tabindex="0" src="../../media/content/test.mp4"></video>
+<p>This is a link <a id="i2" href="a">i2</a>.</p>
+<video id="v3" controls src="../../media/content/test.mp4"></video>
+<p>This is a link <a id="i4" href="a">i4</a>.</p>
+<video id="v5" tabindex="0" src="../../media/content/test.mp4"></video>
+<p>This is a link <a id="i6" href="a">i6</a>.</p>
+<audio id="a7" controls src="../../media/content/test.wav"></audio>
+<p>This is a link <a id="i8" href="a">i8</a>.</p>
+<!-- 'a9' is not focussable: no controls attribute as well no tab index.
+Key down from 'i8' should go to 'end'. -->
+<audio id="a9" src="../../media/content/test.mp4"></audio>
+<p>This is a link <a id="end" href="a">End of Test</a>.</p>
 
-    var resultMap = [
-      ["Up", "start"],
-      ["Down", "v1"],
-      ["Down", "i2"],
-      ["Down", "v3"],
-      ["Down", "i4"],
-      ["Down", "v5"],
-      ["Down", "i6"],
-      ["Down", "a7"],
-      ["Down", "i8"],
-      ["Down", "end"],
-      ["Up", "i8"],
-      ["Down", "end"],
-      ["DONE", "DONE"]
-    ];
+<p>This tests that a media elements ie: <code>&lt;Audio&gt;</code> or <code>&lt;video&gt;</code>, without tabindex are able to be reached through keyboard access</p>
 
-    if (window.testRunner) {
-      testRunner.dumpAsText();
-      testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-      testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-    }
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
+<script>
+  var resultMap = [
+    ["Up", "start"],
+    ["Down", "v1"],
+    ["Down", "i2"],
+    ["Down", "v3"],
+    ["Down", "i4"],
+    ["Down", "v5"],
+    ["Down", "i6"],
+    ["Down", "a7"],
+    ["Down", "i8"],
+    ["Down", "end"],
+    ["Up", "i8"],
+    ["Down", "end"],
+  ];
 
-    function runTest()
-    {
-      // starting the test itself: get to a known place.
-      document.getElementById("start").focus();
-
-      initTest(resultMap, testCompleted);
-    }
-
-    function testCompleted()
-    {
-      finishJSTest();
-    }
-
-    window.onload = runTest;
-    jsTestIsAsync = true;
-    </script>
-  </head>
-
-  <body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
-
-    <p>This is a link <a id="start" href="a">start of Test</a>.</p>
-    <video id="v1" controls tabindex="0" src="../../media/content/test.mp4"></video>
-
-    <p>This is a link <a id="i2" href="a">i2</a>.</p>
-    <video id="v3" controls src="../../media/content/test.mp4"></video>
-
-    <p>This is a link <a id="i4" href="a">i4</a>.</p>
-    <video id="v5" tabindex="0" src="../../media/content/test.mp4"></video>
-
-    <p>This is a link <a id="i6" href="a">i6</a>.</p>
-    <audio id="a7" controls src="../../media/content/test.wav"></audio>
-
-    <p>This is a link <a id="i8" href="a">i8</a>.</p>
-    <!-- 'a9' is not focussable: no controls attribute as well no tab index.
-    Key down from 'i8' should go to 'end'. -->
-    <audio id="a9" src="../../media/content/test.mp4"></audio> 
-
-    <p>This is a link <a id="end" href="a">End of Test</a>.</p>
-
-    <div id="console"></div>
-    <p>This tests that a media elements ie: <code>&lt;Audio&gt;</code> or <code>&lt;video&gt;</code>, without tabindex are able to be reached through keyboard access</p>
-  </body>
-</html>
+  // Start at a known place.
+  document.getElementById("start").focus();
+  snav.assertFocusMoves(resultMap);
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/geolocation-emulation-tests-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/geolocation-emulation-tests-expected.txt
index 4a00ebdd..67e3f39 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/geolocation-emulation-tests-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/geolocation-emulation-tests-expected.txt
@@ -1,23 +1,17 @@
 Tests that geolocation emulation with latitude and longitude works as expected.
 
 
-Running: testPermissionGranted
-geolocation-emulation-tests.js:13 Permission granted.
-
 Running: testGeolocationUnavailable
-geolocation-emulation-tests.js:48 Position unavailable
+Position unavailable
 
 Running: testOverridenGeolocation
-geolocation-emulation-tests.js:41 Latitude: 50 Longitude: 100
+Latitude: 50 Longitude: 100
 
 Running: testInvalidParam
 error: Protocol Error: Invalid type of argument 'latitude' for method 'Emulation.setGeolocationOverride' call. It must be 'number' but it is 'boolean'.
 
 Running: testInvalidGeolocation
-geolocation-emulation-tests.js:41 Latitude: 50 Longitude: 100
-
-Running: testTimestampOfOverridenPosition
-geolocation-emulation-tests.js:79 PASSED
+Latitude: 50 Longitude: 100
 
 Running: testNoOverride
 Override was cleared correctly.
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/geolocation-emulation-tests.js b/third_party/WebKit/LayoutTests/http/tests/devtools/geolocation-emulation-tests.js
index 46e8e41..ca93a10 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/geolocation-emulation-tests.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/geolocation-emulation-tests.js
@@ -6,139 +6,57 @@
   TestRunner.addResult(`Tests that geolocation emulation with latitude and longitude works as expected.\n`);
 
   await TestRunner.loadModule('console_test_runner');
-  await TestRunner.addScriptTag('../resources/permissions-helper.js');
+  await TestRunner.navigatePromise('https://devtools.test:8443/devtools/network/resources/empty.html');
+  await TestRunner.BrowserAgent.invoke_grantPermissions({
+    origin: 'https://devtools.test:8443',
+    permissions: ['geolocation']
+  });
   await TestRunner.evaluateInPagePromise(`
-      function grantGeolocationPermission() {
-        PermissionsHelper.setPermission('geolocation', 'granted').then(function(p) {
-          console.log("Permission granted.");
-        });
+    async function getPositionPromise() {
+      try {
+        const position = await new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject));
+        if ((new Date(position.timestamp)).toDateString() != (new Date()).toDateString())
+            return 'Unexpected error occured: timestamps mismatch.';
+        if (position && position.coords)
+            return 'Latitude: ' + position.coords.latitude + ' Longitude: ' + position.coords.longitude;
+        return 'Unexpected error occured. Test failed.';
+      } catch (error) {
+        const suffix = error.message ? ' (' + error.message + ')' : '';
+        if (error.code === error.PERMISSION_DENIED)
+          return 'Permission denied' + suffix;
+        if (error.code === error.POSITION_UNAVAILABLE)
+          return 'Position unavailable' + suffix;
+        if (error.code === error.TIMEOUT)
+          return 'Request timed out' + suffix;
+        return 'Unknown error' + suffix;
       }
-
-      function serializeGeolocationError(error) {
-          var result = "Unknown error"
-          switch (error.code)
-          {
-              case error.PERMISSION_DENIED:
-                  result = "Permission denied";
-                  break;
-              case error.POSITION_UNAVAILABLE:
-                  result = "Position unavailable";
-                  break;
-              case error.TIMEOUT:
-                  result = "Request timed out";
-                  break;
-          }
-          if (error.message)
-              result += " (" + error.message + ")";
-          return result;
-      }
-
-      function overrideGeolocation()
-      {
-          function testSuccess(position)
-          {
-              if (position && position.coords)
-                  console.log("Latitude: " + position.coords.latitude + " Longitude: " + position.coords.longitude);
-              else
-                  console.log("Unexpected error occured. Test failed.");
-          }
-
-          function testFailed(error)
-          {
-              console.log(serializeGeolocationError(error));
-          }
-
-          navigator.geolocation.getCurrentPosition(testSuccess, testFailed);
-      }
-
-      function getPositionPromise()
-      {
-          return new Promise((resolve, reject) => {
-              function testSuccess(position)
-              {
-                  if (position && position.coords)
-                      resolve("Latitude: " + position.coords.latitude + " Longitude: " + position.coords.longitude);
-                  else
-                      resolve("Unexpected error occured. Test failed.");
-              }
-
-              function testFailed(error)
-              {
-                  resolve(serializeGeolocationError(error));
-              }
-
-              navigator.geolocation.getCurrentPosition(testSuccess, testFailed);
-          });
-      }
-
-      function overridenTimestampGeolocation()
-      {
-          function testSuccess(position)
-          {
-              if ((new Date(position.timestamp)).toDateString() == (new Date()).toDateString())
-                  console.log("PASSED");
-              else
-                  console.log("Unexpected error occured. Test failed.");
-          }
-
-          function testFailed(error)
-          {
-              console.log(serializeGeolocationError(error));
-          }
-
-          navigator.geolocation.getCurrentPosition(testSuccess, testFailed);
-      }
+    }
   `);
 
-  function consoleSniffAndDump(next) {
-    ConsoleTestRunner.addConsoleSniffer(() => {
-        ConsoleTestRunner.dumpConsoleMessages();
-        Console.ConsoleView.clearConsole();
-        next();
-    });
-  }
-
-  var positionBeforeOverride;
+  const positionBeforeOverride = await TestRunner.evaluateInPageAsync('getPositionPromise()');
 
   TestRunner.runTestSuite([
-    function testPermissionGranted(next) {
-      consoleSniffAndDump(savePositionBeforeOverride);
-      TestRunner.evaluateInPage('grantGeolocationPermission()');
-
-      async function savePositionBeforeOverride() {
-        positionBeforeOverride = await TestRunner.evaluateInPageAsync('getPositionPromise()');
-        Console.ConsoleView.clearConsole();
-        next();
-      }
-    },
-
-    function testGeolocationUnavailable(next) {
+    async function testGeolocationUnavailable(next) {
       TestRunner.EmulationAgent.setGeolocationOverride();
-      consoleSniffAndDump(next);
-      TestRunner.evaluateInPage('overrideGeolocation()');
+      TestRunner.addResult(await TestRunner.evaluateInPageAsync('getPositionPromise()'));
+      next();
     },
 
-    function testOverridenGeolocation(next) {
+    async function testOverridenGeolocation(next) {
       TestRunner.EmulationAgent.setGeolocationOverride(50, 100, 95);
-      consoleSniffAndDump(next);
-      TestRunner.evaluateInPage('overrideGeolocation()');
+      TestRunner.addResult(await TestRunner.evaluateInPageAsync('getPositionPromise()'));
+      next();
     },
 
-    function testInvalidParam(next) {
+    async function testInvalidParam(next) {
       TestRunner.EmulationAgent.setGeolocationOverride(true, 100, 95);
       next();
     },
 
-    function testInvalidGeolocation(next) {
+    async function testInvalidGeolocation(next) {
       TestRunner.EmulationAgent.setGeolocationOverride(200, 300, 95);
-      consoleSniffAndDump(next);
-      TestRunner.evaluateInPage('overrideGeolocation()');
-    },
-
-    function testTimestampOfOverridenPosition(next) {
-      TestRunner.EmulationAgent.setGeolocationOverride(50, 100, 95);
-      consoleSniffAndDump(next);
-      TestRunner.evaluateInPage('overridenTimestampGeolocation()');
+      TestRunner.addResult(await TestRunner.evaluateInPageAsync('getPositionPromise()'));
+      next();
     },
 
     async function testNoOverride(next) {
@@ -148,7 +66,11 @@
         TestRunner.addResult('Override was cleared correctly.');
       else
         TestRunner.addResult('Position differs from value before override.');
+
+      // Reset browser context permissions after running this test.
+      await TestRunner.BrowserAgent.invoke_resetPermissions();
       next();
     }
   ]);
 })();
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/browser-grant-permissions-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/browser-grant-permissions-expected.txt
new file mode 100644
index 0000000..cbfb516
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/browser-grant-permissions-expected.txt
@@ -0,0 +1,14 @@
+Test that permissions could be granted
+- Granted: ["geolocation"]
+- Granted: ["audioCapture"]
+- Granted: ["geolocation","audioCapture"]
+- Failed to grant: ["eee"]  error: Unknown permission type: eee
+- Resetting all permissions
+[
+    [0] : INITIAL '': denied
+    [1] : CHANGED 'geolocation': granted
+    [2] : CHANGED 'geolocation': denied
+    [3] : CHANGED 'geolocation': granted
+    [4] : CHANGED 'geolocation': denied
+]
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/browser-grant-permissions.js b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/browser-grant-permissions.js
new file mode 100644
index 0000000..a1f59f17
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/browser-grant-permissions.js
@@ -0,0 +1,73 @@
+(async function(testRunner) {
+  const {page, session, dp} = await testRunner.startBlank(
+      `Test that permissions could be granted`);
+
+  // Reset all permissions initially.
+  await dp.Browser.resetPermissions();
+
+  await page.navigate('http://devtools.test:8000/inspector-protocol/resources/empty.html');
+
+  // Start listening for geolocation changes.
+  await session.evaluateAsync(async () => {
+    window.subscriptionChanges = [];
+    const result = await navigator.permissions.query({name: 'geolocation'});
+    window.subscriptionChanges.push(`INITIAL '${name}': ${result.state}`);
+    result.onchange = () => window.subscriptionChanges.push(`CHANGED 'geolocation': ${result.state}`);
+  });
+
+  await Promise.all([
+    grant('geolocation'),
+    waitPermission('geolocation', 'granted')
+  ]);
+
+  await Promise.all([
+    grant('audioCapture'),
+    waitPermission('geolocation', 'denied'),
+    waitPermission('microphone', 'granted'),
+  ]);
+
+  await Promise.all([
+    grant('geolocation', 'audioCapture'),
+    waitPermission('geolocation', 'granted'),
+    waitPermission('microphone', 'granted'),
+  ]);
+
+  await grant('eee');
+
+  testRunner.log('- Resetting all permissions'),
+  await Promise.all([
+    dp.Browser.resetPermissions(),
+    waitPermission('geolocation', 'denied'),
+    waitPermission('microphone', 'denied'),
+  ]);
+
+  testRunner.log(await session.evaluate(() => window.subscriptionChanges));
+
+  testRunner.completeTest();
+
+  async function grant(...permissions) {
+    const response = await dp.Browser.grantPermissions({
+      origin: 'http://devtools.test:8000',
+      permissions
+    });
+    if (response.error)
+      testRunner.log('- Failed to grant: ' + JSON.stringify(permissions) + '  error: ' + response.error.message);
+    else
+      testRunner.log('- Granted: ' + JSON.stringify(permissions));
+  }
+
+  async function waitPermission(name, state) {
+    await session.evaluateAsync(async (name, state) => {
+      const result = await navigator.permissions.query({name});
+      if (result.state === state)
+        return;
+      return new Promise(resolve => {
+        result.onchange = () => {
+          if (result.state === state)
+            resolve();
+        }
+      });
+    });
+  }
+})
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/empty.html b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/empty.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/empty.html
diff --git a/third_party/WebKit/LayoutTests/http/tests/intersection-observer/resources/v2-subframe.html b/third_party/WebKit/LayoutTests/http/tests/intersection-observer/resources/v2-subframe.html
index 4645ec36..e38860a 100644
--- a/third_party/WebKit/LayoutTests/http/tests/intersection-observer/resources/v2-subframe.html
+++ b/third_party/WebKit/LayoutTests/http/tests/intersection-observer/resources/v2-subframe.html
@@ -3,10 +3,18 @@
 <script>
 var results = [];
 
+// This wraps the usual definition of waitForNotification in a 100ms setTimeout.
+// The extra delay is necessary to ensure that notifications have been generated
+// when the observer is tracking visibility; see:
+//   http://szager-chromium.github.io/IntersectionObserver/#dom-intersectionobserver-trackvisibility
 function waitForNotification(f) {
-  requestAnimationFrame(function () {
-    setTimeout(function () { setTimeout(f); });
-  });
+  setTimeout(() => {
+    requestAnimationFrame(function () {
+      setTimeout(function () {
+        setTimeout(f)
+      })
+    })
+  }, 100)
 }
 
 onload = () => {
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/resources/intersection-observer-test-utils.js b/third_party/WebKit/LayoutTests/intersection-observer/resources/intersection-observer-test-utils.js
index 1c4483b7..ed44edd 100644
--- a/third_party/WebKit/LayoutTests/intersection-observer/resources/intersection-observer-test-utils.js
+++ b/third_party/WebKit/LayoutTests/intersection-observer/resources/intersection-observer-test-utils.js
@@ -41,6 +41,20 @@
   }, description);
 }
 
+// This wraps waitForNotification in a 100ms setTimeout. The extra delay is
+// necessary to ensure that notifications have been generated when the observer
+// is tracking visibility; see:
+//   http://szager-chromium.github.io/IntersectionObserver/#dom-intersectionobserver-trackvisibility
+function waitForNotificationV2(f) {
+  setTimeout(() => { waitForNotification(f) }, 100);
+}
+
+function runTestCycleV2(f, description) {
+  async_test(function(t) {
+    waitForNotificationV2(t.step_func_done(f));
+  }, description);
+}
+
 // Root bounds for a root with an overflow clip as defined by:
 //   http://wicg.github.io/IntersectionObserver/#intersectionobserver-root-intersection-rectangle
 function contentBounds(root) {
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/v2/animated-occlusion.html b/third_party/WebKit/LayoutTests/intersection-observer/v2/animated-occlusion.html
index 5d5ac0b..5ecd3f9 100644
--- a/third_party/WebKit/LayoutTests/intersection-observer/v2/animated-occlusion.html
+++ b/third_party/WebKit/LayoutTests/intersection-observer/v2/animated-occlusion.html
@@ -45,7 +45,7 @@
   internals.runtimeFlags.intersectionObserverV2Enabled = true;
 }
 
-runTestCycle(function() {
+runTestCycleV2(function() {
   assert_equals(window.innerWidth, 800, "Window must be 800 pixels wide.");
   assert_equals(window.innerHeight, 600, "Window must be 600 pixels high.");
 
@@ -59,13 +59,13 @@
   observer.observe(target);
   entries = entries.concat(observer.takeRecords());
   assert_equals(entries.length, 0, "No initial notifications.");
-  runTestCycle(step0, "First rAF.");
+  runTestCycleV2(step0, "First rAF.");
 }, "IntersectionObserverV2 in a single document using the implicit root, with an animated occluding element.");
 
 function step0() {
   occluder.style.animation = "rotate .1s linear";
   setTimeout(() => {
-    runTestCycle(step1, "occluder.style.animation = 'rotate .1s linear'");
+    runTestCycleV2(step1, "occluder.style.animation = 'rotate .1s linear'");
   }, 50);
   checkLastEntry(entries, 0, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, true]);
 }
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/v2/simple-effects.html b/third_party/WebKit/LayoutTests/intersection-observer/v2/simple-effects.html
index 44e883ca..a0f3ce6 100644
--- a/third_party/WebKit/LayoutTests/intersection-observer/v2/simple-effects.html
+++ b/third_party/WebKit/LayoutTests/intersection-observer/v2/simple-effects.html
@@ -36,7 +36,7 @@
   internals.runtimeFlags.intersectionObserverV2Enabled = true;
 }
 
-runTestCycle(function() {
+runTestCycleV2(function() {
   target = document.getElementById("target");
   effects = document.getElementById("effects");
   assert_true(!!target, "target exists");
@@ -47,24 +47,24 @@
   observer.observe(target);
   entries = entries.concat(observer.takeRecords());
   assert_equals(entries.length, 0, "No initial notifications.");
-  runTestCycle(step0, "First rAF.");
+  runTestCycleV2(step0, "First rAF.");
 }, "IntersectionObserverV2 in a single document using the implicit root, with a non-zero opacity ancestor.");
 
 function step0() {
   effects.style.opacity = "0.99";
-  runTestCycle(step1, "effects.style.opacity = 0.99");
+  runTestCycleV2(step1, "effects.style.opacity = 0.99");
   checkLastEntry(entries, 0, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, true]);
 }
 
 function step1() {
   effects.style.opacity = "1";
-  runTestCycle(step2, "effects.style.opacity = 1");
+  runTestCycleV2(step2, "effects.style.opacity = 1");
   checkLastEntry(entries, 1, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, false]);
 }
 
 function step2() {
   effects.style.filter = "grayscale(50%)";
-  runTestCycle(step3, "effects.style.filter = grayscale(50%)");
+  runTestCycleV2(step3, "effects.style.filter = grayscale(50%)");
   checkLastEntry(entries, 2, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, true]);
 }
 
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/v2/simple-occlusion.html b/third_party/WebKit/LayoutTests/intersection-observer/v2/simple-occlusion.html
index ac73e36..a5c8efdd 100644
--- a/third_party/WebKit/LayoutTests/intersection-observer/v2/simple-occlusion.html
+++ b/third_party/WebKit/LayoutTests/intersection-observer/v2/simple-occlusion.html
@@ -36,7 +36,7 @@
   internals.runtimeFlags.intersectionObserverV2Enabled = true;
 }
 
-runTestCycle(function() {
+runTestCycleV2(function() {
   target = document.getElementById("target");
   occluder = document.getElementById("occluder");
   assert_true(!!target, "target exists");
@@ -47,19 +47,19 @@
   observer.observe(target);
   entries = entries.concat(observer.takeRecords());
   assert_equals(entries.length, 0, "No initial notifications.");
-  runTestCycle(step0, "First rAF.");
+  runTestCycleV2(step0, "First rAF.");
 }, "IntersectionObserverV2 in a single document using the implicit root, with an occluding element.");
 
 function step0() {
   occluder.style.marginTop = "-10px";
-  runTestCycle(step1, "occluder.style.marginTop = '-10px'");
+  runTestCycleV2(step1, "occluder.style.marginTop = '-10px'");
   checkLastEntry(entries, 0, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, true]);
 }
 
 function step1() {
   // Occluding elements with opacity=0 should not affect target visibility.
   occluder.style.opacity = "0";
-  runTestCycle(step2, "occluder.style.opacity = 0");
+  runTestCycleV2(step2, "occluder.style.opacity = 0");
   checkLastEntry(entries, 1, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, false]);
 }
 
diff --git a/third_party/WebKit/LayoutTests/resources/gesture-util.js b/third_party/WebKit/LayoutTests/resources/gesture-util.js
index 27bf1f1..c27b874 100644
--- a/third_party/WebKit/LayoutTests/resources/gesture-util.js
+++ b/third_party/WebKit/LayoutTests/resources/gesture-util.js
@@ -89,7 +89,7 @@
 // the synthetic gesture code modified to guarantee the single update behavior.
 const SPEED_INSTANT = 200000;
 
-function smoothScroll(pixels_to_scroll, start_x, start_y, gesture_source_type, direction, speed_in_pixels_s) {
+function smoothScroll(pixels_to_scroll, start_x, start_y, gesture_source_type, direction, speed_in_pixels_s, precise_scrolling_deltas, scroll_by_page) {
   return new Promise((resolve, reject) => {
     if (chrome && chrome.gpuBenchmarking) {
       chrome.gpuBenchmarking.smoothScrollBy(pixels_to_scroll,
@@ -98,7 +98,9 @@
                                             start_y,
                                             gesture_source_type,
                                             direction,
-                                            speed_in_pixels_s);
+                                            speed_in_pixels_s,
+                                            precise_scrolling_deltas,
+                                            scroll_by_page);
     } else {
       reject('This test requires chrome.gpuBenchmarking');
     }
diff --git a/third_party/WebKit/LayoutTests/virtual/threaded/animations/invisible-composited-animations-prevent-scroll.html b/third_party/WebKit/LayoutTests/virtual/threaded/animations/invisible-composited-animations-prevent-scroll.html
index d813abe..3ac4c58 100644
--- a/third_party/WebKit/LayoutTests/virtual/threaded/animations/invisible-composited-animations-prevent-scroll.html
+++ b/third_party/WebKit/LayoutTests/virtual/threaded/animations/invisible-composited-animations-prevent-scroll.html
@@ -44,6 +44,7 @@
         50 /* start_y */,
         GESTURE_SOURCE_TYPE,
         'down',
-        20000 /* speed */);
+        20000 /* speed */,
+        false /* precise_scrolling_deltas */);
   });
 </script>
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt
index cb68be6..df1ff32 100644
--- a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -321,10 +321,10 @@
 FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "getRemoteCertificates()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
 FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "onstatechange" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
 FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "onerror" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: existence and properties of interface object assert_equals: prototype of RTCIceTransport is not EventTarget expected function "function EventTarget() { [native code] }" but got function "function () { [native code] }"
+FAIL RTCIceTransport interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function () { [native code] }" did not throw
 PASS RTCIceTransport interface object length
 PASS RTCIceTransport interface object name
-FAIL RTCIceTransport interface: existence and properties of interface prototype object assert_equals: prototype of RTCIceTransport.prototype is not EventTarget.prototype expected object "[object EventTarget]" but got object "[object Object]"
+PASS RTCIceTransport interface: existence and properties of interface prototype object
 PASS RTCIceTransport interface: existence and properties of interface prototype object's "constructor" property
 PASS RTCIceTransport interface: existence and properties of interface prototype object's @@unscopables property
 PASS RTCIceTransport interface: attribute role
@@ -337,7 +337,7 @@
 PASS RTCIceTransport interface: operation getLocalParameters()
 PASS RTCIceTransport interface: operation getRemoteParameters()
 FAIL RTCIceTransport interface: attribute onstatechange assert_true: The prototype object must have a property "onstatechange" expected true got false
-FAIL RTCIceTransport interface: attribute ongatheringstatechange assert_true: The prototype object must have a property "ongatheringstatechange" expected true got false
+PASS RTCIceTransport interface: attribute ongatheringstatechange
 FAIL RTCIceTransport interface: attribute onselectedcandidatepairchange assert_true: The prototype object must have a property "onselectedcandidatepairchange" expected true got false
 FAIL RTCIceTransport must be primary interface of idlTestObjects.iceTransport assert_equals: wrong typeof object expected "object" but got "undefined"
 FAIL Stringification of idlTestObjects.iceTransport assert_equals: wrong typeof object expected "object" but got "undefined"
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 67a1da1..1373695 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -5430,17 +5430,23 @@
     setter candidate
     setter sdpMLineIndex
     setter sdpMid
-interface RTCIceTransport
+interface RTCIceTransport : EventTarget
     attribute @@toStringTag
     getter gatheringState
+    getter ongatheringstatechange
+    getter onicecandidate
     getter role
     getter state
     method constructor
+    method gather
     method getLocalCandidates
     method getLocalParameters
     method getRemoteCandidates
     method getRemoteParameters
     method getSelectedCandidatePair
+    method stop
+    setter ongatheringstatechange
+    setter onicecandidate
 interface RTCPeerConnection : EventTarget
     static method generateCertificate
     attribute @@toStringTag
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index fbe413d5b..ee3ee54 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -565,7 +565,7 @@
   public_deps = [
     "//net",
     "//services/service_manager/public/cpp",
-    "//services/ui/public/interfaces/ime",
+    "//services/ws/public/mojom/ime",
     "//skia",
     "//third_party/blink/public/common",
     "//third_party/webrtc/api:libjingle_peerconnection_api",
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index 9853089..e650acf 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -62,6 +62,7 @@
 #include "third_party/blink/public/platform/web_url_error.h"
 #include "third_party/blink/public/platform/web_url_loader.h"
 #include "third_party/blink/public/platform/web_url_loader_factory.h"
+#include "third_party/webrtc/p2p/base/portallocator.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -71,6 +72,10 @@
 class GpuMemoryBufferManager;
 }
 
+namespace rtc {
+class Thread;
+}
+
 namespace service_manager {
 class Connector;
 class InterfaceProvider;
@@ -99,6 +104,7 @@
 class WebFileSystem;
 class WebGraphicsContext3DProvider;
 class WebImageCaptureFrameGrabber;
+class WebLocalFrame;
 class WebMIDIAccessor;
 class WebMIDIAccessorClient;
 class WebMediaCapabilitiesClient;
@@ -600,6 +606,24 @@
   // resources.
   virtual std::unique_ptr<WebMediaStreamCenter> CreateMediaStreamCenter();
 
+  // Returns the SingleThreadTaskRunner suitable for running WebRTC networking.
+  // An rtc::Thread will have already been created.
+  // May return null if WebRTC functionality is not implemented.
+  virtual scoped_refptr<base::SingleThreadTaskRunner> GetWebRtcWorkerThread() {
+    return nullptr;
+  }
+
+  // Returns the rtc::Thread instance associated with the WebRTC worker thread.
+  // TODO(bugs.webrtc.org/9419): Remove once WebRTC can be built as a component.
+  // May return null if WebRTC functionality is not implemented.
+  virtual rtc::Thread* GetWebRtcWorkerThreadRtcThread() { return nullptr; }
+
+  // May return null if WebRTC functionality is not implemented.
+  virtual std::unique_ptr<cricket::PortAllocator> CreateWebRtcPortAllocator(
+      WebLocalFrame* frame) {
+    return nullptr;
+  }
+
   // Creates a WebCanvasCaptureHandler to capture Canvas output.
   virtual std::unique_ptr<WebCanvasCaptureHandler>
   CreateCanvasCaptureHandler(const WebSize&, double, WebMediaStreamTrack*);
diff --git a/third_party/blink/public/web/DEPS b/third_party/blink/public/web/DEPS
index 31473f67..368f8834 100644
--- a/third_party/blink/public/web/DEPS
+++ b/third_party/blink/public/web/DEPS
@@ -9,11 +9,11 @@
     "+cc/paint/paint_canvas.h",
     "+cc/paint/paint_flags.h",
     "+mojo/public",
-    # Enforce to use mojom-shared.h in blink/public so that it can compile
-    # inside and outside Blink.
-    "+services/ui/public/interfaces/ime/ime.mojom-shared.h",
     "+services/network/public/mojom/cors.mojom-shared.h",
     "+services/service_manager/public",
+    # Enforce to use mojom-shared.h in blink/public so that it can compile
+    # inside and outside Blink.
+    "+services/ws/public/mojom/ime/ime.mojom-shared.h",
     "+third_party/blink/public/platform",
     "+third_party/blink/public/web",
 
diff --git a/third_party/blink/public/web/web_ime_text_span.h b/third_party/blink/public/web/web_ime_text_span.h
index fe790f8..a6b5204 100644
--- a/third_party/blink/public/web/web_ime_text_span.h
+++ b/third_party/blink/public/web/web_ime_text_span.h
@@ -34,7 +34,7 @@
 #include <string>
 #include <vector>
 
-#include "services/ui/public/interfaces/ime/ime.mojom-shared.h"
+#include "services/ws/public/mojom/ime/ime.mojom-shared.h"
 #include "third_party/skia/include/core/SkColor.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/idls/core/test_interface.idl b/third_party/blink/renderer/bindings/tests/idls/core/test_interface.idl
index b3262e53..bd066d54 100644
--- a/third_party/blink/renderer/bindings/tests/idls/core/test_interface.idl
+++ b/third_party/blink/renderer/bindings/tests/idls/core/test_interface.idl
@@ -72,7 +72,7 @@
 
     void voidMethodTestInterfaceEmptyArg(TestInterfaceEmpty testInterfaceEmptyArg);
     void voidMethodDoubleArgFloatArg(double doubleArg, float floatArg);
-    void voidMethodNullableAndOptionalObjectArgs(object objectArg, optional object optionalObjectArg, object? nullableObjectArg);
+    void voidMethodNullableAndOptionalObjectArgs(object objectArg, object? nullableObjectArg, optional object optionalObjectArg);
     void voidMethodUnrestrictedDoubleArgUnrestrictedFloatArg(unrestricted double unrestrictedDoubleArg, unrestricted float unrestrictedFloatArg);
     void voidMethodTestEnumArg(TestEnum testEnumArg);
     [PerWorldBindings] void voidMethod();
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
index 67da698..0be42531 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
@@ -1564,8 +1564,8 @@
   }
 
   ScriptValue objectArg;
-  ScriptValue optionalObjectArg;
   ScriptValue nullableObjectArg;
+  ScriptValue optionalObjectArg;
   int numArgsPassed = info.Length();
   while (numArgsPassed > 0) {
     if (!info[numArgsPassed - 1]->IsUndefined())
@@ -1579,29 +1579,29 @@
     return;
   }
 
-  if (UNLIKELY(numArgsPassed <= 1)) {
-    impl->voidMethodNullableAndOptionalObjectArgs(objectArg);
-    return;
-  }
   if (info[1]->IsObject()) {
-    optionalObjectArg = ScriptValue(ScriptState::Current(info.GetIsolate()), info[1]);
-  } else if (info[1]->IsUndefined()) {
-    optionalObjectArg = ScriptValue(ScriptState::Current(info.GetIsolate()), v8::Undefined(info.GetIsolate()));
-  } else {
-    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodNullableAndOptionalObjectArgs", "TestInterface", "parameter 2 ('optionalObjectArg') is not an object."));
-    return;
-  }
-
-  if (info[2]->IsObject()) {
-    nullableObjectArg = ScriptValue(ScriptState::Current(info.GetIsolate()), info[2]);
-  } else if (info[2]->IsNullOrUndefined()) {
+    nullableObjectArg = ScriptValue(ScriptState::Current(info.GetIsolate()), info[1]);
+  } else if (info[1]->IsNullOrUndefined()) {
     nullableObjectArg = ScriptValue(ScriptState::Current(info.GetIsolate()), v8::Null(info.GetIsolate()));
   } else {
-    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodNullableAndOptionalObjectArgs", "TestInterface", "parameter 3 ('nullableObjectArg') is not an object."));
+    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodNullableAndOptionalObjectArgs", "TestInterface", "parameter 2 ('nullableObjectArg') is not an object."));
     return;
   }
 
-  impl->voidMethodNullableAndOptionalObjectArgs(objectArg, optionalObjectArg, nullableObjectArg);
+  if (UNLIKELY(numArgsPassed <= 2)) {
+    impl->voidMethodNullableAndOptionalObjectArgs(objectArg, nullableObjectArg);
+    return;
+  }
+  if (info[2]->IsObject()) {
+    optionalObjectArg = ScriptValue(ScriptState::Current(info.GetIsolate()), info[2]);
+  } else if (info[2]->IsUndefined()) {
+    optionalObjectArg = ScriptValue(ScriptState::Current(info.GetIsolate()), v8::Undefined(info.GetIsolate()));
+  } else {
+    V8ThrowException::ThrowTypeError(info.GetIsolate(), ExceptionMessages::FailedToExecute("voidMethodNullableAndOptionalObjectArgs", "TestInterface", "parameter 3 ('optionalObjectArg') is not an object."));
+    return;
+  }
+
+  impl->voidMethodNullableAndOptionalObjectArgs(objectArg, nullableObjectArg, optionalObjectArg);
 }
 
 static void voidMethodUnrestrictedDoubleArgUnrestrictedFloatArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index a56239e..2570c171 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -104,7 +104,7 @@
   public_deps = [
     "//services/network/public/cpp:cpp",
     "//services/service_manager/public/cpp",
-    "//services/ui/public/interfaces/ime",
+    "//services/ws/public/mojom/ime",
     "//skia",
     "//third_party/angle:translator",
     "//third_party/blink/public:core_mojo_bindings_blink",
diff --git a/third_party/blink/renderer/core/DEPS b/third_party/blink/renderer/core/DEPS
index 6394fd59..03e5d763 100644
--- a/third_party/blink/renderer/core/DEPS
+++ b/third_party/blink/renderer/core/DEPS
@@ -40,7 +40,7 @@
     "+services/network/public/mojom",
     "+services/resource_coordinator/public/cpp/resource_coordinator_features.h",
     "+services/service_manager/public",
-    "+services/ui/public/interfaces/ime/ime.mojom-shared.h",
+    "+services/ws/public/mojom/ime/ime.mojom-shared.h",
     "+skia/public/interfaces/bitmap_skbitmap_struct_traits.h",
     "+skia/ext/skia_utils_mac.h",
     "+third_party/blink/public/common",
diff --git a/third_party/blink/renderer/core/css/css_image_value.cc b/third_party/blink/renderer/core/css/css_image_value.cc
index e443608..8cafaa9 100644
--- a/third_party/blink/renderer/core/css/css_image_value.cc
+++ b/third_party/blink/renderer/core/css/css_image_value.cc
@@ -78,8 +78,9 @@
         document.GetFrame()->IsClientLoFiAllowed(params.GetResourceRequest())) {
       params.SetClientLoFiPlaceholder();
     }
-
-    cached_image_ = StyleFetchedImage::Create(document, params);
+    cached_image_ = StyleFetchedImage::Create(
+        document, params,
+        image_request_optimization == FetchParameters::kDeferImageLoad);
   }
 
   return cached_image_.Get();
diff --git a/third_party/blink/renderer/core/css/resolver/element_style_resources.cc b/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
index e6aa01d..8ff4e61 100644
--- a/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
+++ b/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
@@ -28,7 +28,10 @@
 #include "third_party/blink/renderer/core/css/css_uri_value.h"
 #include "third_party/blink/renderer/core/css_property_names.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/tree_scope.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/html/lazy_load_image_observer.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/style/content_data.h"
 #include "third_party/blink/renderer/core/style/cursor_data.h"
@@ -47,9 +50,9 @@
 
 namespace blink {
 
-ElementStyleResources::ElementStyleResources(Document& document,
+ElementStyleResources::ElementStyleResources(Element& element,
                                              float device_scale_factor)
-    : document_(&document), device_scale_factor_(device_scale_factor) {}
+    : element_(&element), device_scale_factor_(device_scale_factor) {}
 
 StyleImage* ElementStyleResources::GetStyleImage(CSSPropertyID property,
                                                  const CSSValue& value) {
@@ -93,7 +96,7 @@
     pending_image_properties_.insert(property);
     return StylePendingImage::Create(value);
   }
-  value.RestoreCachedResourceIfNeeded(*document_);
+  value.RestoreCachedResourceIfNeeded(element_->GetDocument());
   return value.CachedImage();
 }
 
@@ -101,7 +104,7 @@
     TreeScope& tree_scope,
     const CSSURIValue& value,
     AllowExternal allow_external) const {
-  if (value.IsLocal(*document_)) {
+  if (value.IsLocal(element_->GetDocument())) {
     SVGTreeScopeResources& tree_scope_resources =
         tree_scope.EnsureSVGTreeScopedResources();
     AtomicString decoded_fragment(
@@ -125,7 +128,7 @@
     ReferenceFilterOperation& reference_operation =
         ToReferenceFilterOperation(*filter_operation);
     if (SVGResource* resource = reference_operation.Resource())
-      resource->Load(*document_);
+      resource->Load(element_->GetDocument());
   }
 }
 
@@ -145,8 +148,8 @@
     FetchParameters::ImageRequestOptimization image_request_optimization,
     CrossOriginAttributeValue cross_origin) {
   if (CSSImageValue* image_value = pending_image->CssImageValue()) {
-    return image_value->CacheImage(*document_, image_request_optimization,
-                                   cross_origin);
+    return image_value->CacheImage(element_->GetDocument(),
+                                   image_request_optimization, cross_origin);
   }
 
   if (CSSPaintValue* paint_value = pending_image->CssPaintValue()) {
@@ -157,14 +160,14 @@
 
   if (CSSImageGeneratorValue* image_generator_value =
           pending_image->CssImageGeneratorValue()) {
-    image_generator_value->LoadSubimages(*document_);
+    image_generator_value->LoadSubimages(element_->GetDocument());
     return StyleGeneratedImage::Create(*image_generator_value);
   }
 
   if (CSSImageSetValue* image_set_value = pending_image->CssImageSetValue()) {
-    return image_set_value->CacheImage(*document_, device_scale_factor_,
-                                       image_request_optimization,
-                                       cross_origin);
+    return image_set_value->CacheImage(
+        element_->GetDocument(), device_scale_factor_,
+        image_request_optimization, cross_origin);
   }
 
   NOTREACHED();
@@ -195,13 +198,28 @@
       case CSSPropertyBackgroundImage: {
         for (FillLayer* background_layer = &style->AccessBackgroundLayers();
              background_layer; background_layer = background_layer->Next()) {
-          if (background_layer->GetImage() &&
-              background_layer->GetImage()->IsPendingImage()) {
-            background_layer->SetImage(LoadPendingImage(
-                style, ToStylePendingImage(background_layer->GetImage()),
-                BackgroundLayerMayBeSprite(*background_layer)
-                    ? FetchParameters::kNone
-                    : FetchParameters::kAllowPlaceholder));
+          StyleImage* background_image = background_layer->GetImage();
+          if (background_image && background_image->IsPendingImage()) {
+            FetchParameters::ImageRequestOptimization
+                image_request_optimization = FetchParameters::kNone;
+            if (!BackgroundLayerMayBeSprite(*background_layer)) {
+              if (element_->GetDocument()
+                      .GetFrame()
+                      ->IsLazyLoadingImageAllowed()) {
+                background_image->SetIsLazyloadPossiblyDeferred(true);
+                image_request_optimization = FetchParameters::kDeferImageLoad;
+              } else {
+                image_request_optimization = FetchParameters::kAllowPlaceholder;
+              }
+            }
+            StyleImage* new_image =
+                LoadPendingImage(style, ToStylePendingImage(background_image),
+                                 image_request_optimization);
+            if (new_image && new_image->IsImageResource() &&
+                ToStyleFetchedImage(new_image)->IsLazyloadPossiblyDeferred()) {
+              LazyLoadImageObserver::StartMonitoring(element_);
+            }
+            background_layer->SetImage(new_image);
           }
         }
         break;
diff --git a/third_party/blink/renderer/core/css/resolver/element_style_resources.h b/third_party/blink/renderer/core/css/resolver/element_style_resources.h
index 314d453..08ebe761 100644
--- a/third_party/blink/renderer/core/css/resolver/element_style_resources.h
+++ b/third_party/blink/renderer/core/css/resolver/element_style_resources.h
@@ -42,7 +42,7 @@
 class CSSURIValue;
 class CSSValue;
 class ComputedStyle;
-class Document;
+class Element;
 class SVGResource;
 class StyleImage;
 class StylePendingImage;
@@ -54,7 +54,7 @@
   STACK_ALLOCATED();
 
  public:
-  ElementStyleResources(Document&, float device_scale_factor);
+  ElementStyleResources(Element&, float device_scale_factor);
 
   StyleImage* GetStyleImage(CSSPropertyID, const CSSValue&);
   StyleImage* CachedOrPendingFromValue(CSSPropertyID, const CSSImageValue&);
@@ -81,7 +81,7 @@
       FetchParameters::ImageRequestOptimization,
       CrossOriginAttributeValue = kCrossOriginAttributeNotSet);
 
-  Member<Document> document_;
+  Member<Element> element_;
   HashSet<CSSPropertyID> pending_image_properties_;
   float device_scale_factor_;
   DISALLOW_COPY_AND_ASSIGN(ElementStyleResources);
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
index 0e90884..27faa263 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
@@ -46,7 +46,7 @@
       apply_property_to_visited_link_style_(false),
       has_dir_auto_attribute_(false),
       font_builder_(&document),
-      element_style_resources_(document, document.DevicePixelRatio()) {
+      element_style_resources_(*GetElement(), document.DevicePixelRatio()) {
   DCHECK(!!parent_style_ == !!layout_parent_style_);
 
   if (!parent_style_) {
diff --git a/third_party/blink/renderer/core/editing/ime/ime_text_span.h b/third_party/blink/renderer/core/editing/ime/ime_text_span.h
index ea3c5047..f3442bd 100644
--- a/third_party/blink/renderer/core/editing/ime/ime_text_span.h
+++ b/third_party/blink/renderer/core/editing/ime/ime_text_span.h
@@ -26,7 +26,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_IME_IME_TEXT_SPAN_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_IME_IME_TEXT_SPAN_H_
 
-#include "services/ui/public/interfaces/ime/ime.mojom-shared.h"
+#include "services/ws/public/mojom/ime/ime.mojom-shared.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
diff --git a/third_party/blink/renderer/core/editing/markers/styleable_marker.h b/third_party/blink/renderer/core/editing/markers/styleable_marker.h
index d7d1881..6d5404e 100644
--- a/third_party/blink/renderer/core/editing/markers/styleable_marker.h
+++ b/third_party/blink/renderer/core/editing/markers/styleable_marker.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_MARKERS_STYLEABLE_MARKER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_MARKERS_STYLEABLE_MARKER_H_
 
-#include "services/ui/public/interfaces/ime/ime.mojom-shared.h"
+#include "services/ws/public/mojom/ime/ime.mojom-shared.h"
 #include "third_party/blink/renderer/core/editing/markers/document_marker.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/core/editing/selection_modifier.cc b/third_party/blink/renderer/core/editing/selection_modifier.cc
index cdd47a7b..743a52d 100644
--- a/third_party/blink/renderer/core/editing/selection_modifier.cc
+++ b/third_party/blink/renderer/core/editing/selection_modifier.cc
@@ -137,23 +137,30 @@
              : TextDirection::kLtr;
 }
 
-static TextDirection DirectionOf(const VisibleSelection& visible_selection) {
-  const InlineBox* start_box = nullptr;
-  const InlineBox* end_box = nullptr;
-  // Cache the VisiblePositions because visibleStart() and visibleEnd()
-  // can cause layout, which has the potential to invalidate lineboxes.
-  const VisiblePosition& start_position = visible_selection.VisibleStart();
-  const VisiblePosition& end_position = visible_selection.VisibleEnd();
-  if (start_position.IsNotNull())
-    start_box = ComputeInlineBoxPosition(start_position).inline_box;
-  if (end_position.IsNotNull())
-    end_box = ComputeInlineBoxPosition(end_position).inline_box;
-  if (start_box && end_box && start_box->Direction() == end_box->Direction())
-    return start_box->Direction();
+namespace {
+
+base::Optional<TextDirection> DirectionAt(const VisiblePosition& position) {
+  if (position.IsNull())
+    return base::nullopt;
+  if (const InlineBox* box = ComputeInlineBoxPosition(position).inline_box)
+    return box->Direction();
+  return base::nullopt;
+}
+
+TextDirection DirectionOf(const VisibleSelection& visible_selection) {
+  base::Optional<TextDirection> maybe_start_direction =
+      DirectionAt(visible_selection.VisibleStart());
+  base::Optional<TextDirection> maybe_end_direction =
+      DirectionAt(visible_selection.VisibleEnd());
+  if (maybe_start_direction.has_value() && maybe_end_direction.has_value() &&
+      maybe_start_direction.value() == maybe_end_direction.value())
+    return maybe_start_direction.value();
 
   return DirectionOfEnclosingBlockOf(visible_selection.Extent());
 }
 
+}  // namespace
+
 TextDirection SelectionModifier::DirectionOfSelection() const {
   return DirectionOf(selection_);
 }
diff --git a/third_party/blink/renderer/core/events/event_type_names.json5 b/third_party/blink/renderer/core/events/event_type_names.json5
index 22ec3a4..4ab32a6 100644
--- a/third_party/blink/renderer/core/events/event_type_names.json5
+++ b/third_party/blink/renderer/core/events/event_type_names.json5
@@ -126,6 +126,7 @@
     "fullscreenerror",
     "gamepadconnected",
     "gamepaddisconnected",
+    "gatheringstatechange",
     "gattserverdisconnected",
     "geofenceenter",
     "geofenceleave",
diff --git a/third_party/blink/renderer/core/html/lazy_load_image_observer.cc b/third_party/blink/renderer/core/html/lazy_load_image_observer.cc
index 4ed22107..410016d 100644
--- a/third_party/blink/renderer/core/html/lazy_load_image_observer.cc
+++ b/third_party/blink/renderer/core/html/lazy_load_image_observer.cc
@@ -7,6 +7,7 @@
 #include "build/build_config.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/html/html_image_element.h"
 #include "third_party/blink/renderer/core/html_element_type_helpers.h"
@@ -59,6 +60,10 @@
     if (auto* image_element = ToHTMLImageElementOrNull(element))
       image_element->LoadDeferredImage();
 
+    // Load the background image if the element has one deferred.
+    if (const ComputedStyle* style = element->GetComputedStyle())
+      style->LoadDeferredImages(element->GetDocument());
+
     lazy_load_intersection_observer_->unobserve(element);
   }
 }
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index 851a055..a90b415 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -480,6 +480,39 @@
       # The window state. Default to normal.
       optional WindowState windowState
 
+  experimental type PermissionType extends string
+    enum
+      accessibilityEvents
+      audioCapture
+      backgroundSync
+      clipboardRead
+      clipboardWrite
+      durableStorage
+      flash
+      geolocation
+      midi
+      midiSysex
+      notifications
+      paymentHandler
+      protectedMediaIdentifier
+      sensors
+      videoCapture
+
+  # Grant specific permissions to the given origin and reject all others.
+  experimental command grantPermissions
+    parameters
+      string origin
+      array of PermissionType permissions
+      # BrowserContext to override permissions. When omitted, default browser context is used.
+      optional Target.BrowserContextID browserContextId
+
+  # Reset all permission management for all origins.
+  experimental command resetPermissions
+    parameters
+      # BrowserContext to reset permissions. When omitted, default browser context is used.
+      optional Target.BrowserContextID browserContextId
+
+
   # Close browser gracefully.
   command close
 
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc
index 2fba8dc..caf0b48 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc
@@ -135,6 +135,15 @@
 
 }  // anonymous namespace
 
+// Minimum time, in milliseconds, between observations. See:
+//   http://szager-chromium.github.io/IntersectionObserver/#dom-intersectionobserver-trackvisibility
+const DOMHighResTimeStamp IntersectionObserver::s_v2_throttle_delay_ = 100;
+static bool v2_throttle_delay_enabled = true;
+
+void IntersectionObserver::SetV2ThrottleDelayEnabledForTesting(bool enabled) {
+  v2_throttle_delay_enabled = enabled;
+}
+
 IntersectionObserver* IntersectionObserver::Create(
     const IntersectionObserverInit& observer_init,
     IntersectionObserverDelegate& delegate,
@@ -196,6 +205,7 @@
       right_margin_(kFixed),
       bottom_margin_(kFixed),
       left_margin_(kFixed),
+      last_run_time_(-s_v2_throttle_delay_),
       root_is_implicit_(root ? 0 : 1),
       track_visibility_(track_visibility ? 1 : 0) {
   switch (root_margin.size()) {
@@ -311,8 +321,13 @@
     return;
   DOMHighResTimeStamp timestamp =
       DOMWindowPerformance::performance(*delegate_dom_window)->now();
+  if (track_visibility_ && v2_throttle_delay_enabled &&
+      timestamp - last_run_time_ < s_v2_throttle_delay_) {
+    return;
+  }
+  last_run_time_ = timestamp;
   for (auto& observation : observations_)
-    observation->ComputeIntersectionObservations(timestamp);
+    observation->ComputeIntersectionObservations(last_run_time_);
 }
 
 void IntersectionObserver::disconnect(ExceptionState& exception_state) {
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer.h b/third_party/blink/renderer/core/intersection_observer/intersection_observer.h
index e9b5f85..543e7e4 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer.h
@@ -97,6 +97,10 @@
 
   void Trace(blink::Visitor*) override;
 
+  // Enable/disable throttling of visibility checking, so we don't have to add
+  // 100ms sleep() calls to tests.
+  static void SetV2ThrottleDelayEnabledForTesting(bool);
+
  private:
   explicit IntersectionObserver(IntersectionObserverDelegate&,
                                 Element*,
@@ -109,6 +113,10 @@
   // deleted; true otherwise.
   bool RootIsValid() const;
 
+  // If trackVisibility is true, don't compute observations more frequently
+  // than this many milliseconds.
+  static const DOMHighResTimeStamp s_v2_throttle_delay_;
+
   const TraceWrapperMember<IntersectionObserverDelegate> delegate_;
   WeakMember<Element> root_;
   HeapLinkedHashSet<WeakMember<IntersectionObservation>> observations_;
@@ -118,6 +126,7 @@
   Length right_margin_;
   Length bottom_margin_;
   Length left_margin_;
+  DOMHighResTimeStamp last_run_time_;
   unsigned root_is_implicit_ : 1;
   unsigned track_visibility_ : 1;
 };
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
index 02b513f..3152c63 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
@@ -66,7 +66,13 @@
                                    public ScopedIntersectionObserverV2ForTest {
  public:
   IntersectionObserverV2Test()
-      : IntersectionObserverTest(), ScopedIntersectionObserverV2ForTest(true) {}
+      : IntersectionObserverTest(), ScopedIntersectionObserverV2ForTest(true) {
+    IntersectionObserver::SetV2ThrottleDelayEnabledForTesting(false);
+  }
+
+  ~IntersectionObserverV2Test() override {
+    IntersectionObserver::SetV2ThrottleDelayEnabledForTesting(true);
+  }
 };
 
 TEST_F(IntersectionObserverTest, ObserveSchedulesFrame) {
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
index 84c3924b..640894b1 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
@@ -233,18 +233,14 @@
       NGBlockNode(this), static_position,
       css_container->IsBox() ? nullptr : css_container);
 
-  NGBoxStrut scrollbar_sizes;
-  if (css_container->IsBox())
-    scrollbar_sizes =
-        NGBlockNode(ToLayoutBox(css_container)).GetScrollbarSizes();
   // We really only want to lay out ourselves here, so we pass |this| to
   // Run(). Otherwise, NGOutOfFlowLayoutPart may also lay out other objects
   // it discovers that are part of the same containing block, but those
   // should get laid out by the actual containing block.
-  NGOutOfFlowLayoutPart(&container_builder,
-                        css_container->CanContainAbsolutePositionObjects(),
-                        css_container->CanContainFixedPositionObjects(),
-                        scrollbar_sizes, *constraint_space, *container_style)
+  NGOutOfFlowLayoutPart(
+      &container_builder, css_container->CanContainAbsolutePositionObjects(),
+      css_container->CanContainFixedPositionObjects(), borders_and_scrollbars,
+      *constraint_space, *container_style)
       .Run(/* only_layout */ this);
   scoped_refptr<NGLayoutResult> result = container_builder.ToBoxFragment();
   // These are the unpositioned OOF descendants of the current OOF block.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index cc6f148..cb72ba0aa 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -349,10 +349,10 @@
 scoped_refptr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() {
   NGBoxStrut borders = ComputeBorders(ConstraintSpace(), Node());
   NGBoxStrut padding = ComputePadding(ConstraintSpace(), Node());
-  border_scrollbar_padding_ =
-      ConstraintSpace().IsAnonymous()
-          ? NGBoxStrut()
-          : borders + padding + Node().GetScrollbarSizes();
+  NGBoxStrut scrollbars = Node().GetScrollbarSizes();
+  border_scrollbar_padding_ = ConstraintSpace().IsAnonymous()
+                                  ? NGBoxStrut()
+                                  : borders + padding + scrollbars;
 
   NGLogicalSize border_box_size = CalculateBorderBoxSize(
       ConstraintSpace(), Node(), CalculateDefaultBlockSize());
@@ -648,7 +648,7 @@
   // layout.
   if (unpositioned_floats_.IsEmpty()) {
     NGOutOfFlowLayoutPart(&container_builder_, Node().IsAbsoluteContainer(),
-                          Node().IsFixedContainer(), Node().GetScrollbarSizes(),
+                          Node().IsFixedContainer(), borders + scrollbars,
                           ConstraintSpace(), Style())
         .Run();
   }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
index 17dfe750..43bbd51 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
@@ -71,8 +71,10 @@
     : NGLayoutAlgorithm(node, space, ToNGBlockBreakToken(break_token)) {}
 
 scoped_refptr<NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
-  NGBoxStrut border_scrollbar_padding =
-      CalculateBorderScrollbarPadding(ConstraintSpace(), Node());
+  NGBoxStrut borders = ComputeBorders(ConstraintSpace(), Node());
+  NGBoxStrut scrollbars = Node().GetScrollbarSizes();
+  NGBoxStrut padding = ComputePadding(ConstraintSpace(), Node());
+  NGBoxStrut border_scrollbar_padding = borders + scrollbars + padding;
   NGLogicalSize border_box_size =
       CalculateBorderBoxSize(ConstraintSpace(), Node());
   NGLogicalSize content_box_size =
@@ -170,7 +172,7 @@
   } while (true);
 
   NGOutOfFlowLayoutPart(&container_builder_, Node().IsAbsoluteContainer(),
-                        Node().IsFixedContainer(), Node().GetScrollbarSizes(),
+                        Node().IsFixedContainer(), borders + scrollbars,
                         ConstraintSpace(), Style())
       .Run();
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index ecc0b55..52862d2 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -25,14 +25,12 @@
     NGFragmentBuilder* container_builder,
     bool contains_absolute,
     bool contains_fixed,
-    const NGBoxStrut& scrollbar_sizes,
+    const NGBoxStrut& borders_and_scrollers,
     const NGConstraintSpace& container_space,
     const ComputedStyle& container_style)
     : container_builder_(container_builder),
       contains_absolute_(contains_absolute),
       contains_fixed_(contains_fixed) {
-  NGBoxStrut borders_and_scrollers =
-      ComputeBorders(container_space, container_style) + scrollbar_sizes;
   NGPhysicalBoxStrut physical_borders = borders_and_scrollers.ConvertToPhysical(
       container_style.GetWritingMode(), container_style.Direction());
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
index 20002f8..49b8ffb 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
@@ -31,10 +31,16 @@
   STACK_ALLOCATED();
 
  public:
+  // The container_builder, borders_and_scrollers, container_space and
+  // container_style parameters are all with respect to the containing block of
+  // the relevant out-of-flow positioned descendants. If the CSS "containing
+  // block" of such an out-of-flow positioned descendant isn't a true block (but
+  // e.g. a relatively positioned inline instead), the containing block here is
+  // the containing block of said non-block.
   NGOutOfFlowLayoutPart(NGFragmentBuilder* container_builder,
                         bool contains_absolute,
                         bool contains_fixed,
-                        const NGBoxStrut& scrollbar_sizes,
+                        const NGBoxStrut& borders_and_scrollers,
                         const NGConstraintSpace& container_space,
                         const ComputedStyle& container_style);
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc
index 6d7320a..c6ed889e 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc
@@ -22,8 +22,10 @@
     : NGLayoutAlgorithm(node, space, ToNGBlockBreakToken(break_token)) {}
 
 scoped_refptr<NGLayoutResult> NGPageLayoutAlgorithm::Layout() {
-  NGBoxStrut border_scrollbar_padding =
-      CalculateBorderScrollbarPadding(ConstraintSpace(), Node());
+  NGBoxStrut borders = ComputeBorders(ConstraintSpace(), Node());
+  NGBoxStrut scrollbars = Node().GetScrollbarSizes();
+  NGBoxStrut padding = ComputePadding(ConstraintSpace(), Node());
+  NGBoxStrut border_scrollbar_padding = borders + scrollbars + padding;
   NGLogicalSize border_box_size =
       CalculateBorderBoxSize(ConstraintSpace(), Node());
   NGLogicalSize content_box_size =
@@ -75,7 +77,7 @@
   container_builder_.SetPadding(ComputePadding(ConstraintSpace(), Style()));
 
   NGOutOfFlowLayoutPart(&container_builder_, Node().IsAbsoluteContainer(),
-                        Node().IsFixedContainer(), Node().GetScrollbarSizes(),
+                        Node().IsFixedContainer(), borders + scrollbars,
                         ConstraintSpace(), Style())
       .Run();
 
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource.cc b/third_party/blink/renderer/core/loader/resource/image_resource.cc
index eb9adda..ba6394f 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource.cc
@@ -135,6 +135,14 @@
                                             initiator_name);
   }
 
+  void LoadDeferredImage(ResourceFetcher* fetcher) override {
+    if (resource_->GetType() == Resource::kImage &&
+        resource_->StillNeedsLoad() &&
+        !fetcher->ShouldDeferImageLoad(resource_->Url())) {
+      fetcher->StartLoad(resource_);
+    }
+  }
+
   const Member<ImageResource> resource_;
 };
 
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
index 7308459..350dd36 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
@@ -65,6 +65,8 @@
       const KURL&,
       const AtomicString& initiator_name) override {}
 
+  void LoadDeferredImage(ResourceFetcher* fetcher) override {}
+
   const KURL url_;
   const ResourceResponse response_;
 };
@@ -629,4 +631,8 @@
   return info_->IsCacheValidator();
 }
 
+void ImageResourceContent::LoadDeferredImage(ResourceFetcher* fetcher) {
+  info_->LoadDeferredImage(fetcher);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.h b/third_party/blink/renderer/core/loader/resource/image_resource_content.h
index 5df45ba..cd281a77 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_content.h
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.h
@@ -178,6 +178,8 @@
   bool IsAcceptableContentType();
   bool IsAcceptableCompressionRatio();
 
+  void LoadDeferredImage(ResourceFetcher* fetcher);
+
  private:
   using CanDeferInvalidation = ImageResourceObserver::CanDeferInvalidation;
 
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_info.h b/third_party/blink/renderer/core/loader/resource/image_resource_info.h
index bd48546a..ab395091 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_info.h
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_info.h
@@ -58,6 +58,8 @@
       const KURL&,
       const AtomicString& initiator_name) = 0;
 
+  virtual void LoadDeferredImage(ResourceFetcher* fetcher) = 0;
+
   void Trace(blink::Visitor* visitor) override {}
 };
 
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index a7b76d96..66308a3 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -2389,6 +2389,14 @@
     if (object_.HasLayer()) {
       // 1. Compute clip in flow thread space.
       fragment_clip = iterator.ClipRectInFlowThread();
+
+      // We skip empty clip fragments, since they can share the same logical top
+      // with the subsequent fragments. Since we skip drawing empty fragments
+      // anyway, it doesn't affect the paint output, but it allows us to use
+      // logical top to uniquely identify fragments in an object.
+      if (fragment_clip->IsEmpty())
+        continue;
+
       // 2. Convert #1 to visual coordinates in the space of the flow thread.
       fragment_clip->MoveBy(pagination_offset);
       // 3. Adjust #2 to visual coordinates in the containing "paint offset"
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index eb37002..061fad04 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
+#include "third_party/blink/renderer/core/layout/layout_flow_thread.h"
 #include "third_party/blink/renderer/core/layout/layout_image.h"
 #include "third_party/blink/renderer/core/layout/layout_table_cell.h"
 #include "third_party/blink/renderer/core/layout/layout_table_section.h"
@@ -5958,4 +5959,39 @@
             fragment.PaintProperties()->OverflowClip());
 }
 
+TEST_P(PaintPropertyTreeBuilderTest, SkipEmptyClipFragments) {
+  SetBodyInnerHTML(R"HTML(
+    <!doctype HTML>
+    <style>h4 { column-span: all; }</style>
+    <div id="container" style="columns:1;">
+      lorem
+      <h4>hi</h4>
+      <div><h4>hello</h4></div>
+      ipsum
+    </div>
+  )HTML");
+
+  const auto* flow_thread = GetDocument()
+                                .getElementById("container")
+                                ->GetLayoutObject()
+                                ->SlowFirstChild();
+  EXPECT_TRUE(flow_thread->IsLayoutFlowThread());
+  EXPECT_TRUE(ToLayoutFlowThread(flow_thread)->IsLayoutMultiColumnFlowThread());
+
+  // FragmentainerIterator would return 3 things:
+  // 1. A fragment that contains "lorem" and is interrupted by the first h4,
+  //    since it's column-span: all.
+  // 2. A fragment that starts at the inner div of height 0 and is immediately
+  //    interrupted by a nested h4.
+  // 3. A fragment that contains "ipsum".
+  //
+  // The second fragment would have an empty clip and the same logical top as
+  // the third fragment. This test ensures that this fragment is not present in
+  // the LayoutMultiColumnFlowThread's fragments.
+  EXPECT_EQ(2u, NumFragments(flow_thread));
+  EXPECT_NE(
+      flow_thread->FirstFragment().LogicalTopInFlowThread(),
+      flow_thread->FirstFragment().NextFragment()->LogicalTopInFlowThread());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index 774f7bf..18f8e54e 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -52,6 +52,7 @@
 #include "third_party/blink/renderer/core/style/quotes_data.h"
 #include "third_party/blink/renderer/core/style/shadow_list.h"
 #include "third_party/blink/renderer/core/style/style_difference.h"
+#include "third_party/blink/renderer/core/style/style_fetched_image.h"
 #include "third_party/blink/renderer/core/style/style_image.h"
 #include "third_party/blink/renderer/core/style/style_inherited_variables.h"
 #include "third_party/blink/renderer/core/style/style_non_inherited_variables.h"
@@ -1019,6 +1020,18 @@
   return kInterpolationDefault;
 }
 
+void ComputedStyle::LoadDeferredImages(Document& document) const {
+  if (HasBackgroundImage()) {
+    for (const FillLayer* background_layer = &BackgroundLayers();
+         background_layer; background_layer = background_layer->Next()) {
+      if (StyleImage* image = background_layer->GetImage()) {
+        if (image->IsImageResource() && image->IsLazyloadPossiblyDeferred())
+          ToStyleFetchedImage(image)->LoadDeferredImage(document);
+      }
+    }
+  }
+}
+
 void ComputedStyle::ApplyTransform(
     TransformationMatrix& result,
     const LayoutSize& border_box_size,
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 9e9779b0..d6d38c2 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -2232,6 +2232,9 @@
     return pseudo == kPseudoIdBefore || pseudo == kPseudoIdAfter;
   }
 
+  // Load the images of CSS properties that were deferred by LazyLoad.
+  void LoadDeferredImages(Document&) const;
+
  private:
   void SetVisitedLinkBackgroundColor(const StyleColor& v) {
     SetVisitedLinkBackgroundColorInternal(v);
diff --git a/third_party/blink/renderer/core/style/style_fetched_image.cc b/third_party/blink/renderer/core/style/style_fetched_image.cc
index 0a94474..9e31e74 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image.cc
+++ b/third_party/blink/renderer/core/style/style_fetched_image.cc
@@ -34,9 +34,12 @@
 namespace blink {
 
 StyleFetchedImage::StyleFetchedImage(const Document& document,
-                                     FetchParameters& params)
+                                     FetchParameters& params,
+                                     bool is_lazyload_possibly_deferred)
     : document_(&document), url_(params.Url()) {
   is_image_resource_ = true;
+  is_lazyload_possibly_deferred_ = is_lazyload_possibly_deferred;
+
   image_ = ImageResourceContent::Fetch(params, document_->Fetcher());
   image_->AddObserver(this);
   // ResourceFetcher is not determined from StyleFetchedImage and it is
@@ -142,6 +145,13 @@
   return image_->GetImage()->CurrentFrameKnownToBeOpaque();
 }
 
+void StyleFetchedImage::LoadDeferredImage(const Document& document) {
+  DCHECK(is_lazyload_possibly_deferred_);
+  is_lazyload_possibly_deferred_ = false;
+  document_ = &document;
+  image_->LoadDeferredImage(document_->Fetcher());
+}
+
 void StyleFetchedImage::Trace(blink::Visitor* visitor) {
   visitor->Trace(image_);
   visitor->Trace(document_);
diff --git a/third_party/blink/renderer/core/style/style_fetched_image.h b/third_party/blink/renderer/core/style/style_fetched_image.h
index 60a4bfc3..b70e265 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image.h
+++ b/third_party/blink/renderer/core/style/style_fetched_image.h
@@ -41,8 +41,9 @@
 
  public:
   static StyleFetchedImage* Create(const Document& document,
-                                   FetchParameters& params) {
-    return new StyleFetchedImage(document, params);
+                                   FetchParameters& params,
+                                   bool is_lazyload_deferred) {
+    return new StyleFetchedImage(document, params, is_lazyload_deferred);
   }
   ~StyleFetchedImage() override;
 
@@ -70,10 +71,14 @@
   bool KnownToBeOpaque(const Document&, const ComputedStyle&) const override;
   ImageResourceContent* CachedImage() const override;
 
+  void LoadDeferredImage(const Document& document);
+
   void Trace(blink::Visitor*) override;
 
  private:
-  StyleFetchedImage(const Document&, FetchParameters&);
+  StyleFetchedImage(const Document&,
+                    FetchParameters&,
+                    bool is_lazyload_deferred);
 
   void Dispose();
 
diff --git a/third_party/blink/renderer/core/style/style_image.h b/third_party/blink/renderer/core/style/style_image.h
index 6aa247d..4f90fd4 100644
--- a/third_party/blink/renderer/core/style/style_image.h
+++ b/third_party/blink/renderer/core/style/style_image.h
@@ -139,6 +139,13 @@
   }
   ALWAYS_INLINE bool IsPaintImage() const { return is_paint_image_; }
 
+  bool IsLazyloadPossiblyDeferred() const {
+    return is_lazyload_possibly_deferred_;
+  }
+  void SetIsLazyloadPossiblyDeferred(bool is_lazyload_possibly_deferred) {
+    is_lazyload_possibly_deferred_ = is_lazyload_possibly_deferred;
+  }
+
   virtual void Trace(blink::Visitor* visitor) {}
 
  protected:
@@ -147,12 +154,14 @@
         is_pending_image_(false),
         is_generated_image_(false),
         is_image_resource_set_(false),
-        is_paint_image_(false) {}
+        is_paint_image_(false),
+        is_lazyload_possibly_deferred_(false) {}
   bool is_image_resource_ : 1;
   bool is_pending_image_ : 1;
   bool is_generated_image_ : 1;
   bool is_image_resource_set_ : 1;
   bool is_paint_image_ : 1;
+  bool is_lazyload_possibly_deferred_ : 1;
 
   FloatSize ApplyZoom(const FloatSize&, float multiplier) const;
   FloatSize ImageSizeForSVGImage(SVGImage*,
diff --git a/third_party/blink/renderer/devtools/front_end/test_runner/TestRunner.js b/third_party/blink/renderer/devtools/front_end/test_runner/TestRunner.js
index b2631c6..034aa81a 100644
--- a/third_party/blink/renderer/devtools/front_end/test_runner/TestRunner.js
+++ b/third_party/blink/renderer/devtools/front_end/test_runner/TestRunner.js
@@ -326,6 +326,7 @@
  * @param {!SDK.Target} target
  */
 TestRunner._setupTestHelpers = function(target) {
+  TestRunner.BrowserAgent = target.browserAgent();
   TestRunner.CSSAgent = target.cssAgent();
   TestRunner.DeviceOrientationAgent = target.deviceOrientationAgent();
   TestRunner.DOMAgent = target.domAgent();
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index e5006e2..6dcb439ef 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -162,6 +162,8 @@
     "//third_party/blink/renderer/modules/webusb",
     "//third_party/blink/renderer/modules/xr",
     "//third_party/icu",
+    "//third_party/webrtc/pc:libjingle_peerconnection",
+    "//third_party/webrtc_overrides:init_webrtc",
     "//third_party/zlib",
   ]
 
diff --git a/third_party/blink/renderer/modules/event_target_modules_names.json5 b/third_party/blink/renderer/modules/event_target_modules_names.json5
index 12fd150..65975c8 100644
--- a/third_party/blink/renderer/modules/event_target_modules_names.json5
+++ b/third_party/blink/renderer/modules/event_target_modules_names.json5
@@ -30,6 +30,7 @@
     "modules/netinfo/NetworkInformation",
     "modules/notifications/Notification",
     "modules/payments/PaymentRequest",
+    "modules/peerconnection/RTCIceTransport",
     "modules/permissions/PermissionStatus",
     "modules/picture_in_picture/HTMLVideoElementPictureInPicture",
     "modules/picture_in_picture/PictureInPictureWindow",
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc
index 6639044e1..6eb8acf 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc
@@ -296,13 +296,12 @@
 
 bool MediaControlOverlayPlayButtonElement::IsMouseEventOnInternalButton(
     const MouseEvent& mouse_event) const {
-  // If no position data available, default to yes.
-  if (!mouse_event.HasPosition())
+  // If we don't have the necessary pieces to calculate whether the event is
+  // within the bounds of the button, default to yes.
+  if (!mouse_event.HasPosition() || !internal_button_ ||
+      !GetDocument().GetLayoutView() || !GetComputedStyle()) {
     return true;
-
-  // If there is no layout view, default to yes.
-  if (!GetDocument().GetLayoutView())
-    return true;
+  }
 
   // Find the zoom-adjusted internal button bounding box.
   DOMRect* box = internal_button_->getBoundingClientRect();
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element_test.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element_test.cc
index 8a268e65..4ce754d0 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element_test.cc
@@ -7,6 +7,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/event_type_names.h"
 #include "third_party/blink/renderer/core/events/mouse_event.h"
 #include "third_party/blink/renderer/core/html/html_html_element.h"
@@ -17,11 +18,28 @@
 
 namespace blink {
 
-class MediaControlOverlayPlayButtonElementTest : public PageTestBase {
+class MediaControlOverlayPlayButtonElementTest
+    : public PageTestBase,
+      private ScopedModernMediaControlsForTest {
  public:
+  MediaControlOverlayPlayButtonElementTest()
+      : ScopedModernMediaControlsForTest(true) {}
+
   void SetUp() final {
-    // Create page and instance of AnimatedArrow to run tests on.
+    // Create page with video element with controls.
     PageTestBase::SetUp();
+    HTMLVideoElement* media_element = HTMLVideoElement::Create(GetDocument());
+    media_element->SetBooleanAttribute(HTMLNames::controlsAttr, true);
+    GetDocument().body()->AppendChild(media_element);
+
+    // Create instance of MediaControlOverlayPlayButtonElement for tests.
+    MediaControlsImpl* media_controls =
+        static_cast<MediaControlsImpl*>(media_element->GetMediaControls());
+    ASSERT_NE(nullptr, media_controls);
+    overlay_play_button_ =
+        new MediaControlOverlayPlayButtonElement(*media_controls);
+
+    // Create instance of AnimatedArrow to run tests on.
     arrow_element_ = new MediaControlOverlayPlayButtonElement::AnimatedArrow(
         "test", GetDocument());
     GetDocument().body()->AppendChild(arrow_element_);
@@ -47,6 +65,16 @@
     GetElementById("arrow-3")->DispatchEvent(*event);
   }
 
+  void RemoveInternalButton() {
+    overlay_play_button_->internal_button_ = nullptr;
+  }
+
+  void EnsureNoComputedStyle() {
+    // Due to the nature of the test setup, we already get a null computedstyle
+    // for free :). Just make sure it's actually not there.
+    ASSERT_EQ(nullptr, overlay_play_button_->GetComputedStyle());
+  }
+
   Document& CreateTestDocumentWithBody() {
     Document* document = Document::CreateForTest();
     HTMLHtmlElement* html = HTMLHtmlElement::Create(*document);
@@ -119,8 +147,19 @@
 }
 
 TEST_F(MediaControlOverlayPlayButtonElementTest,
+       KeepEventInNodeWithoutInternalButtonDoesntCrash) {
+  RemoveInternalButton();
+  SimulateKeepEventInNode();
+}
+
+TEST_F(MediaControlOverlayPlayButtonElementTest,
+       KeepEventInNodeWithoutComputedStyleDoesntCrash) {
+  EnsureNoComputedStyle();
+  SimulateKeepEventInNode();
+}
+
+TEST_F(MediaControlOverlayPlayButtonElementTest,
        KeepEventInNodeWithoutLayoutViewDoesntCrash) {
-  ScopedModernMediaControlsForTest enable_modern_media_controls(true);
   Document& document_without_layout_view = CreateTestDocumentWithBody();
   CreateTestOverlayPlayButton(document_without_layout_view);
   SimulateKeepEventInNode();
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index 433b306..0a6b218 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -592,6 +592,7 @@
           "peerconnection/rtc_data_channel_init.idl",
           "peerconnection/rtc_ice_candidate_init.idl",
           "peerconnection/rtc_ice_candidate_pair.idl",
+          "peerconnection/rtc_ice_gather_options.idl",
           "peerconnection/rtc_ice_parameters.idl",
           "peerconnection/rtc_ice_server.idl",
           "peerconnection/rtc_offer_answer_options.idl",
diff --git a/third_party/blink/renderer/modules/peerconnection/BUILD.gn b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
index 5413f86..7905cec 100644
--- a/third_party/blink/renderer/modules/peerconnection/BUILD.gn
+++ b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
@@ -6,6 +6,12 @@
 
 blink_modules_sources("peerconnection") {
   sources = [
+    "adapters/ice_transport_host.cc",
+    "adapters/ice_transport_host.h",
+    "adapters/ice_transport_proxy.cc",
+    "adapters/ice_transport_proxy.h",
+    "adapters/web_rtc_cross_thread_copier.cc",
+    "adapters/web_rtc_cross_thread_copier.h",
     "rtc_certificate.cc",
     "rtc_certificate.h",
     "rtc_data_channel.cc",
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/README.md b/third_party/blink/renderer/modules/peerconnection/adapters/README.md
new file mode 100644
index 0000000..8c7794b7
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/README.md
@@ -0,0 +1,13 @@
+## WebRTC API Adapters
+
+This directory contains code that wraps components of the WebRTC native API in
+classes that expose an API specialized for implementing the Web API. Some of
+these adapters also allow the WebRTC component to be safely used from a remote
+thread.
+
+### Blink Types
+
+Blink types (like AtomicString, HeapVector, etc.) should not be used inside this
+directory. The bindings in the parent directory are responsible for converting
+the Blink binding types to the corresponding STL/WebRTC types before interacting
+with the adapters.
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.cc b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.cc
new file mode 100644
index 0000000..7b7628e
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.cc
@@ -0,0 +1,94 @@
+// 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 "third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h"
+
+#include "third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+namespace blink {
+
+IceTransportHost::IceTransportHost(
+    scoped_refptr<base::SingleThreadTaskRunner> proxy_thread,
+    base::WeakPtr<IceTransportProxy> proxy,
+    std::unique_ptr<cricket::PortAllocator> port_allocator)
+    : proxy_thread_(std::move(proxy_thread)),
+      port_allocator_(std::move(port_allocator)),
+      proxy_(std::move(proxy)) {
+  DETACH_FROM_THREAD(thread_checker_);
+  DCHECK(proxy_thread_);
+  DCHECK(proxy_);
+  DCHECK(port_allocator_);
+}
+
+IceTransportHost::~IceTransportHost() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void IceTransportHost::Initialize(rtc::Thread* host_thread_rtc_thread) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  // TODO(bugs.webrtc.org/9419): Remove once WebRTC can be built as a component.
+  if (!rtc::ThreadManager::Instance()->CurrentThread()) {
+    rtc::ThreadManager::Instance()->SetCurrentThread(host_thread_rtc_thread);
+  }
+  // These settings are copied from PeerConnection:
+  // https://codesearch.chromium.org/chromium/src/third_party/webrtc/pc/peerconnection.cc?l=4708&rcl=820ebd0f661696043959b5105b2814e0edd8b694
+  port_allocator_->set_step_delay(cricket::kMinimumStepDelay);
+  port_allocator_->set_flags(port_allocator_->flags() |
+                             cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET |
+                             cricket::PORTALLOCATOR_ENABLE_IPV6 |
+                             cricket::PORTALLOCATOR_ENABLE_IPV6_ON_WIFI);
+  port_allocator_->Initialize();
+  transport_ = std::make_unique<cricket::P2PTransportChannel>(
+      "", 0, port_allocator_.get());
+  transport_->SignalGatheringState.connect(
+      this, &IceTransportHost::OnGatheringStateChanged);
+  transport_->SignalCandidateGathered.connect(
+      this, &IceTransportHost::OnCandidateGathered);
+  // The ICE tiebreaker is used to determine which side is controlling/
+  // controlled when both sides start in the same role. The number is randomly
+  // generated so that each peer can calculate a.tiebreaker <= b.tiebreaker
+  // consistently.
+  transport_->SetIceTiebreaker(rtc::CreateRandomId64());
+}
+
+void IceTransportHost::StartGathering(
+    const cricket::IceParameters& local_parameters,
+    const cricket::ServerAddresses& stun_servers,
+    const std::vector<cricket::RelayServerConfig>& turn_servers,
+    int32_t candidate_filter) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  port_allocator_->set_candidate_filter(candidate_filter);
+  port_allocator_->SetConfiguration(stun_servers, turn_servers,
+                                    port_allocator_->candidate_pool_size(),
+                                    port_allocator_->prune_turn_ports());
+
+  transport_->SetIceParameters(local_parameters);
+  transport_->MaybeStartGathering();
+  DCHECK_EQ(transport_->gathering_state(), cricket::kIceGatheringGathering);
+}
+
+void IceTransportHost::OnGatheringStateChanged(
+    cricket::IceTransportInternal* transport) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK_EQ(transport, transport_.get());
+  PostCrossThreadTask(
+      *proxy_thread_, FROM_HERE,
+      CrossThreadBind(&IceTransportProxy::OnGatheringStateChanged, proxy_,
+                      transport_->gathering_state()));
+}
+
+void IceTransportHost::OnCandidateGathered(
+    cricket::IceTransportInternal* transport,
+    const cricket::Candidate& candidate) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK_EQ(transport, transport_.get());
+  PostCrossThreadTask(*proxy_thread_, FROM_HERE,
+                      CrossThreadBind(&IceTransportProxy::OnCandidateGathered,
+                                      proxy_, candidate));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h
new file mode 100644
index 0000000..5b3bd5c7
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h
@@ -0,0 +1,75 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_ICE_TRANSPORT_HOST_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_ICE_TRANSPORT_HOST_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "third_party/webrtc/p2p/base/p2ptransportchannel.h"
+
+namespace rtc {
+class Thread;
+}
+
+namespace blink {
+
+class IceTransportProxy;
+
+// This class is the host side correspondent to the IceTransportProxy. See the
+// IceTransportProxy documentation for background. This class lives on the host
+// thread and proxies calls between the IceTransportProxy and the
+// P2PTransportChannel (which is single-threaded).
+//
+//     proxy thread                               host thread
+// +------------------+   unique_ptr   +------------------------------+
+// |                  |   =========>   |                              |
+// | client <-> Proxy |                | Host <-> P2PTransportChannel |
+// |                  |   <---------   |                              |
+// +------------------+    WeakPtr     +------------------------------+
+//
+// Since the client code controls the Proxy lifetime, the Proxy has a unique_ptr
+// to the Host that lives on the host thread. The unique_ptr has an
+// OnTaskRunnerDeleter so that when the Proxy is destroyed a task will be queued
+// to delete the Host as well (and the P2PTransportChannel with it). The Host
+// needs a pointer back to the Proxy to post callbacks, and by using a WeakPtr
+// any callbacks run on the proxy thread after the proxy has been deleted will
+// be safely dropped.
+//
+// The Host can be constructed on any thread but after that point all methods
+// must be called on the host thread.
+class IceTransportHost final : public sigslot::has_slots<> {
+ public:
+  IceTransportHost(scoped_refptr<base::SingleThreadTaskRunner> proxy_thread,
+                   base::WeakPtr<IceTransportProxy> proxy,
+                   std::unique_ptr<cricket::PortAllocator> port_allocator);
+  ~IceTransportHost() override;
+
+  void Initialize(rtc::Thread* host_thread_rtc_thread);
+
+  void StartGathering(
+      const cricket::IceParameters& local_parameters,
+      const cricket::ServerAddresses& stun_servers,
+      const std::vector<cricket::RelayServerConfig>& turn_servers,
+      int32_t candidate_filter);
+
+ private:
+  // Callbacks from P2PTransportChannel.
+  void OnGatheringStateChanged(cricket::IceTransportInternal* transport);
+  void OnCandidateGathered(cricket::IceTransportInternal* transport,
+                           const cricket::Candidate& candidate);
+
+  const scoped_refptr<base::SingleThreadTaskRunner> proxy_thread_;
+  std::unique_ptr<cricket::PortAllocator> port_allocator_;
+  std::unique_ptr<cricket::P2PTransportChannel> transport_;
+  base::WeakPtr<IceTransportProxy> proxy_;
+
+  THREAD_CHECKER(thread_checker_);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_ICE_TRANSPORT_HOST_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.cc b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.cc
new file mode 100644
index 0000000..084e01a
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.cc
@@ -0,0 +1,74 @@
+// 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 "third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h"
+
+#include "third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+namespace blink {
+
+IceTransportProxy::IceTransportProxy(
+    FrameScheduler* frame_scheduler,
+    scoped_refptr<base::SingleThreadTaskRunner> host_thread,
+    rtc::Thread* host_thread_rtc_thread,
+    Delegate* delegate,
+    std::unique_ptr<cricket::PortAllocator> port_allocator)
+    : host_thread_(std::move(host_thread)),
+      host_(nullptr, base::OnTaskRunnerDeleter(host_thread_)),
+      delegate_(delegate),
+      connection_handle_for_scheduler_(
+          frame_scheduler->OnActiveConnectionCreated()),
+      weak_ptr_factory_(this) {
+  DCHECK(host_thread_);
+  DCHECK(delegate_);
+  scoped_refptr<base::SingleThreadTaskRunner> proxy_thread =
+      frame_scheduler->GetTaskRunner(TaskType::kNetworking);
+  DCHECK(proxy_thread->BelongsToCurrentThread());
+  // Wait to initialize the host until the weak_ptr_factory_ is initialized.
+  // The IceTransportHost is constructed on the proxy thread but should only be
+  // interacted with via PostTask to the host thread. The OnTaskRunnerDeleter
+  // (configured above) will ensure it gets deleted on the host thread.
+  host_.reset(new IceTransportHost(proxy_thread, weak_ptr_factory_.GetWeakPtr(),
+                                   std::move(port_allocator)));
+  PostCrossThreadTask(
+      *host_thread_, FROM_HERE,
+      CrossThreadBind(&IceTransportHost::Initialize,
+                      CrossThreadUnretained(host_.get()),
+                      CrossThreadUnretained(host_thread_rtc_thread)));
+}
+
+IceTransportProxy::~IceTransportProxy() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  // Note: The IceTransportHost will be deleted on the host thread.
+}
+
+void IceTransportProxy::StartGathering(
+    const cricket::IceParameters& local_parameters,
+    const cricket::ServerAddresses& stun_servers,
+    const std::vector<cricket::RelayServerConfig>& turn_servers,
+    int32_t candidate_filter) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  PostCrossThreadTask(
+      *host_thread_, FROM_HERE,
+      CrossThreadBind(&IceTransportHost::StartGathering,
+                      CrossThreadUnretained(host_.get()), local_parameters,
+                      stun_servers, turn_servers, candidate_filter));
+}
+
+void IceTransportProxy::OnGatheringStateChanged(
+    cricket::IceGatheringState new_state) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  delegate_->OnGatheringStateChanged(new_state);
+}
+
+void IceTransportProxy::OnCandidateGathered(
+    const cricket::Candidate& candidate) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  delegate_->OnCandidateGathered(candidate);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h
new file mode 100644
index 0000000..d60a8c2
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h
@@ -0,0 +1,94 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_ICE_TRANSPORT_PROXY_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_ICE_TRANSPORT_PROXY_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/webrtc/p2p/base/p2ptransportchannel.h"
+
+namespace rtc {
+class Thread;
+}
+
+namespace blink {
+
+class IceTransportHost;
+
+// This class allows the ICE implementation (P2PTransportChannel) to run on a
+// thread different from the thread from which it is controlled. All
+// interactions with the ICE implementation happen asynchronously.
+//
+// Terminology:
+// - Proxy thread: Thread from which the P2PTransportChannel is controlled. This
+//       is the thread on which the IceTransportProxy is created.
+// - Host thread: Thread on which the P2PTransportChannel runs. This is usually
+//       the WebRTC worker thread and is specified when creating the
+//       IceTransportProxy.
+//
+// The client must create the IceTransportProxy on the same thread it wishes
+// to control it from. The Proxy will manage all cross-thread interactions; the
+// client should call all methods from the proxy thread and all callbacks will
+// be run on the proxy thread.
+class IceTransportProxy final {
+ public:
+  // Delegate for receiving callbacks from the ICE implementation. These all run
+  // on the proxy thread.
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+
+    virtual void OnGatheringStateChanged(cricket::IceGatheringState new_state) {
+    }
+    virtual void OnCandidateGathered(const cricket::Candidate& candidate) {}
+  };
+
+  // Construct a Proxy with the underlying ICE implementation running on the
+  // given host thread and callbacks serviced by the given delegate.
+  // The P2PTransportChannel will be created with the given PortAllocator.
+  // The delegate must outlive the IceTransportProxy.
+  IceTransportProxy(FrameScheduler* frame_scheduler,
+                    scoped_refptr<base::SingleThreadTaskRunner> host_thread,
+                    rtc::Thread* host_thread_rtc_thread,
+                    Delegate* delegate,
+                    std::unique_ptr<cricket::PortAllocator> port_allocator);
+  ~IceTransportProxy();
+
+  void StartGathering(
+      const cricket::IceParameters& local_parameters,
+      const cricket::ServerAddresses& stun_servers,
+      const std::vector<cricket::RelayServerConfig>& turn_servers,
+      int32_t candidate_filter);
+
+ private:
+  // Callbacks from RTCIceTransportHost.
+  friend class IceTransportHost;
+  void OnGatheringStateChanged(cricket::IceGatheringState new_state);
+  void OnCandidateGathered(const cricket::Candidate& candidate);
+
+  const scoped_refptr<base::SingleThreadTaskRunner> host_thread_;
+  // Since the Host is deleted on the host thread (via OnTaskRunnerDeleter), as
+  // long as this is alive it is safe to post tasks to it (using unretained).
+  std::unique_ptr<IceTransportHost, base::OnTaskRunnerDeleter> host_;
+  Delegate* const delegate_;
+
+  // This handle notifies scheduler about an active connection associated
+  // with a frame. Handle should be destroyed when connection is closed.
+  // This should have the same lifetime as |proxy_|.
+  std::unique_ptr<FrameScheduler::ActiveConnectionHandle>
+      connection_handle_for_scheduler_;
+
+  THREAD_CHECKER(thread_checker_);
+
+  // Must be the last member.
+  base::WeakPtrFactory<IceTransportProxy> weak_ptr_factory_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_ICE_TRANSPORT_PROXY_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.cc b/third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.cc
new file mode 100644
index 0000000..0277157a
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.cc
@@ -0,0 +1,5 @@
+// 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 "third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h"
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h b/third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h
new file mode 100644
index 0000000..7f19fb6
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h
@@ -0,0 +1,55 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_WEB_RTC_CROSS_THREAD_COPIER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_WEB_RTC_CROSS_THREAD_COPIER_H_
+
+// This file defines specializations for the CrossThreadCopier that allow WebRTC
+// types to be passed across threads using their copy constructors.
+
+#include <set>
+#include <vector>
+
+#include "third_party/blink/renderer/platform/cross_thread_copier.h"
+
+namespace cricket {
+class Candidate;
+struct IceParameters;
+struct RelayServerConfig;
+}  // namespace cricket
+
+namespace rtc {
+class SocketAddress;
+}
+
+namespace blink {
+
+template <>
+struct CrossThreadCopier<cricket::IceParameters>
+    : public CrossThreadCopierPassThrough<cricket::IceParameters> {
+  STATIC_ONLY(CrossThreadCopier);
+};
+
+template <>
+struct CrossThreadCopier<std::set<rtc::SocketAddress>>
+    : public CrossThreadCopierPassThrough<std::set<rtc::SocketAddress>> {
+  STATIC_ONLY(CrossThreadCopier);
+};
+
+template <>
+struct CrossThreadCopier<std::vector<cricket::RelayServerConfig>>
+    : public CrossThreadCopierPassThrough<
+          std::vector<cricket::RelayServerConfig>> {
+  STATIC_ONLY(CrossThreadCopier);
+};
+
+template <>
+struct CrossThreadCopier<cricket::Candidate>
+    : public CrossThreadCopierPassThrough<cricket::Candidate> {
+  STATIC_ONLY(CrossThreadCopier);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_WEB_RTC_CROSS_THREAD_COPIER_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_ice_gather_options.idl b/third_party/blink/renderer/modules/peerconnection/rtc_ice_gather_options.idl
new file mode 100644
index 0000000..441eeac
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_ice_gather_options.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/webrtc-ice/#dom-rtcicegatheroptions
+dictionary RTCIceGatherOptions {
+    RTCIceTransportPolicy gatherPolicy = "all";
+    sequence<RTCIceServer> iceServers;
+};
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc
index aef2f13..81b1c073 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc
@@ -4,28 +4,89 @@
 
 #include "third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.h"
 
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_error_util.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_ice_candidate.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_ice_gather_options.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_ice_parameters.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_ice_server.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_ice_event.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_ice_event_init.h"
+#include "third_party/webrtc/api/peerconnectioninterface.h"
+#include "third_party/webrtc/p2p/base/portallocator.h"
+#include "third_party/webrtc/p2p/base/transportdescription.h"
+#include "third_party/webrtc/pc/iceserverparsing.h"
+#include "third_party/webrtc/pc/webrtcsdp.h"
 
 namespace blink {
 
-RTCIceTransport* RTCIceTransport::Create() {
-  return new RTCIceTransport();
+RTCIceTransport* RTCIceTransport::Create(ExecutionContext* context) {
+  return new RTCIceTransport(context);
 }
 
-RTCIceTransport::RTCIceTransport() = default;
+RTCIceTransport::RTCIceTransport(ExecutionContext* context)
+    : ContextLifecycleObserver(context) {
+  Document* document = ToDocument(GetExecutionContext());
+  LocalFrame* frame = document->GetFrame();
+  DCHECK(frame);
+
+  std::unique_ptr<cricket::PortAllocator> port_allocator =
+      Platform::Current()->CreateWebRtcPortAllocator(
+          WebLocalFrame::FrameForCurrentContext());
+  proxy_.reset(new IceTransportProxy(
+      frame->GetFrameScheduler(), Platform::Current()->GetWebRtcWorkerThread(),
+      Platform::Current()->GetWebRtcWorkerThreadRtcThread(), this,
+      std::move(port_allocator)));
+
+  GenerateLocalParameters();
+}
+
+RTCIceTransport::~RTCIceTransport() {
+  DCHECK(!proxy_);
+}
 
 String RTCIceTransport::role() const {
   return String();
 }
 
 String RTCIceTransport::state() const {
-  return "new";
+  switch (state_) {
+    case RTCIceTransportState::kNew:
+      return "new";
+    case RTCIceTransportState::kChecking:
+      return "checking";
+    case RTCIceTransportState::kConnected:
+      return "connected";
+    case RTCIceTransportState::kCompleted:
+      return "completed";
+    case RTCIceTransportState::kDisconnected:
+      return "disconnected";
+    case RTCIceTransportState::kFailed:
+      return "failed";
+    case RTCIceTransportState::kClosed:
+      return "closed";
+  }
+  NOTREACHED();
+  return g_empty_string;
 }
 
 String RTCIceTransport::gatheringState() const {
-  return "new";
+  switch (gathering_state_) {
+    case cricket::kIceGatheringNew:
+      return "new";
+    case cricket::kIceGatheringGathering:
+      return "gathering";
+    case cricket::kIceGatheringComplete:
+      return "complete";
+    default:
+      NOTREACHED();
+      return g_empty_string;
+  }
 }
 
 const HeapVector<Member<RTCIceCandidate>>& RTCIceTransport::getLocalCandidates()
@@ -45,7 +106,7 @@
 
 void RTCIceTransport::getLocalParameters(
     base::Optional<RTCIceParameters>& result) const {
-  result = base::nullopt;
+  result = local_parameters_;
 }
 
 void RTCIceTransport::getRemoteParameters(
@@ -53,11 +114,159 @@
   result = base::nullopt;
 }
 
+static webrtc::PeerConnectionInterface::IceServer ConvertIceServer(
+    const RTCIceServer& ice_server) {
+  webrtc::PeerConnectionInterface::IceServer converted_ice_server;
+  // Prefer standardized 'urls' field over deprecated 'url' field.
+  Vector<String> url_strings;
+  if (ice_server.hasURLs()) {
+    if (ice_server.urls().IsString()) {
+      url_strings.push_back(ice_server.urls().GetAsString());
+    } else if (ice_server.urls().IsStringSequence()) {
+      url_strings = ice_server.urls().GetAsStringSequence();
+    }
+  } else if (ice_server.hasURL()) {
+    url_strings.push_back(ice_server.url());
+  }
+  for (const String& url_string : url_strings) {
+    converted_ice_server.urls.push_back(WebString(url_string).Utf8());
+  }
+  converted_ice_server.username = WebString(ice_server.username()).Utf8();
+  converted_ice_server.password = WebString(ice_server.credential()).Utf8();
+  return converted_ice_server;
+}
+
+static cricket::IceParameters ConvertIceParameters(
+    const RTCIceParameters& ice_parameters) {
+  cricket::IceParameters converted_ice_parameters;
+  converted_ice_parameters.ufrag =
+      WebString(ice_parameters.usernameFragment()).Utf8();
+  converted_ice_parameters.pwd = WebString(ice_parameters.password()).Utf8();
+  return converted_ice_parameters;
+}
+
+static std::vector<webrtc::PeerConnectionInterface::IceServer>
+ConvertIceServers(const HeapVector<RTCIceServer>& ice_servers) {
+  std::vector<webrtc::PeerConnectionInterface::IceServer> converted_ice_servers;
+  for (const RTCIceServer& ice_server : ice_servers) {
+    converted_ice_servers.push_back(ConvertIceServer(ice_server));
+  }
+  return converted_ice_servers;
+}
+
+void RTCIceTransport::gather(const RTCIceGatherOptions& options,
+                             ExceptionState& exception_state) {
+  if (RaiseExceptionIfClosed(exception_state)) {
+    return;
+  }
+  // TODO(github.com/w3c/webrtc-ice/issues/7): Possibly support calling gather()
+  // more than once.
+  if (gathering_state_ != cricket::kIceGatheringNew) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "Can only call gather() once.");
+    return;
+  }
+  std::vector<webrtc::PeerConnectionInterface::IceServer> ice_servers;
+  if (options.hasIceServers()) {
+    ice_servers = ConvertIceServers(options.iceServers());
+  }
+  cricket::ServerAddresses stun_servers;
+  std::vector<cricket::RelayServerConfig> turn_servers;
+  webrtc::RTCErrorType error_type =
+      webrtc::ParseIceServers(ice_servers, &stun_servers, &turn_servers);
+  if (error_type != webrtc::RTCErrorType::NONE) {
+    ThrowExceptionFromRTCError(
+        webrtc::RTCError(error_type, "Invalid ICE server URL(s)."),
+        exception_state);
+    return;
+  }
+  gathering_state_ = cricket::kIceGatheringGathering;
+  uint32_t candidate_filter = cricket::CF_ALL;
+  if (options.gatherPolicy() == "relay") {
+    candidate_filter = cricket::CF_RELAY;
+  } else {
+    DCHECK_EQ(options.gatherPolicy(), "all");
+  }
+  proxy_->StartGathering(ConvertIceParameters(local_parameters_), stun_servers,
+                         turn_servers, candidate_filter);
+}
+
+void RTCIceTransport::stop() {
+  if (IsClosed()) {
+    return;
+  }
+  state_ = RTCIceTransportState::kClosed;
+  proxy_.reset();
+}
+
+void RTCIceTransport::GenerateLocalParameters() {
+  local_parameters_.setUsernameFragment(
+      WebString::FromUTF8(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH)));
+  local_parameters_.setPassword(
+      WebString::FromUTF8(rtc::CreateRandomString(cricket::ICE_PWD_LENGTH)));
+}
+
+void RTCIceTransport::OnGatheringStateChanged(
+    cricket::IceGatheringState new_state) {
+  if (new_state == gathering_state_) {
+    return;
+  }
+  if (new_state == cricket::kIceGatheringComplete) {
+    // Generate a null ICE candidate to signal the end of candidates.
+    DispatchEvent(*RTCPeerConnectionIceEvent::Create(nullptr));
+  }
+  gathering_state_ = new_state;
+  DispatchEvent(*Event::Create(EventTypeNames::gatheringstatechange));
+}
+
+void RTCIceTransport::OnCandidateGathered(
+    const cricket::Candidate& parsed_candidate) {
+  RTCIceCandidate* candidate =
+      RTCIceCandidate::Create(WebRTCICECandidate::Create(
+          WebString::FromUTF8(webrtc::SdpSerializeCandidate(parsed_candidate)),
+          g_empty_string, 0));
+  local_candidates_.push_back(candidate);
+  RTCPeerConnectionIceEventInit event_init;
+  event_init.setCandidate(candidate);
+  DispatchEvent(*RTCPeerConnectionIceEvent::Create(EventTypeNames::icecandidate,
+                                                   event_init));
+}
+
+bool RTCIceTransport::RaiseExceptionIfClosed(
+    ExceptionState& exception_state) const {
+  if (IsClosed()) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        "The RTCIceTransport's state is 'closed'.");
+    return true;
+  }
+  return false;
+}
+
+const AtomicString& RTCIceTransport::InterfaceName() const {
+  return EventTargetNames::RTCIceTransport;
+}
+
+ExecutionContext* RTCIceTransport::GetExecutionContext() const {
+  return ContextLifecycleObserver::GetExecutionContext();
+}
+
+void RTCIceTransport::ContextDestroyed(ExecutionContext*) {
+  stop();
+}
+
+bool RTCIceTransport::HasPendingActivity() const {
+  // Only allow the RTCIceTransport to be garbage collected if the ICE
+  // implementation is not active.
+  return static_cast<bool>(proxy_);
+}
+
 void RTCIceTransport::Trace(blink::Visitor* visitor) {
   visitor->Trace(local_candidates_);
   visitor->Trace(remote_candidates_);
   visitor->Trace(selected_candidate_pair_);
-  ScriptWrappable::Trace(visitor);
+  EventTargetWithInlineData::Trace(visitor);
+  ContextLifecycleObserver::Trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.h b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.h
index d6a3983..b108b2f 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.h
@@ -5,20 +5,52 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_ICE_TRANSPORT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_ICE_TRANSPORT_H_
 
+#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_ice_candidate_pair.h"
+#include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
+#include "third_party/blink/renderer/modules/event_target_modules.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_ice_candidate_pair.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_ice_parameters.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
 
 namespace blink {
 
+class ExceptionState;
 class RTCIceCandidate;
+class RTCIceGatherOptions;
+
+enum class RTCIceTransportState {
+  kNew,
+  kChecking,
+  kConnected,
+  kCompleted,
+  kDisconnected,
+  kFailed,
+  kClosed
+};
 
 // Blink bindings for the RTCIceTransport JavaScript object.
-class MODULES_EXPORT RTCIceTransport final : public ScriptWrappable {
+//
+// This class uses the IceTransportProxy to run and interact with the WebRTC
+// ICE implementation running on the WebRTC worker thread managed by //content
+// (called network_thread here).
+//
+// This object inherits from ActiveScriptWrappable since it must be kept alive
+// while the ICE implementation is active, regardless of the number of
+// JavaScript references held to it.
+class MODULES_EXPORT RTCIceTransport final
+    : public EventTargetWithInlineData,
+      public ActiveScriptWrappable<RTCIceTransport>,
+      public ContextLifecycleObserver,
+      private IceTransportProxy::Delegate {
   DEFINE_WRAPPERTYPEINFO();
+  USING_GARBAGE_COLLECTED_MIXIN(RTCIceTransport);
 
  public:
-  static RTCIceTransport* Create();
+  static RTCIceTransport* Create(ExecutionContext* context);
+
+  ~RTCIceTransport() override;
 
   // rtc_ice_transport.idl
   String role() const;
@@ -30,16 +62,52 @@
       base::Optional<RTCIceCandidatePair>& result) const;
   void getLocalParameters(base::Optional<RTCIceParameters>& result) const;
   void getRemoteParameters(base::Optional<RTCIceParameters>& result) const;
+  void gather(const RTCIceGatherOptions& options,
+              ExceptionState& exception_state);
+  void stop();
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(gatheringstatechange);
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(icecandidate);
+
+  // EventTarget overrides.
+  const AtomicString& InterfaceName() const override;
+  ExecutionContext* GetExecutionContext() const override;
+
+  // ContextLifecycleObserver overrides.
+  void ContextDestroyed(ExecutionContext*) override;
+
+  // ActiveScriptWrappable overrides.
+  bool HasPendingActivity() const override;
 
   // For garbage collection.
   void Trace(blink::Visitor* visitor) override;
 
  private:
-  RTCIceTransport();
+  explicit RTCIceTransport(ExecutionContext* context);
+
+  // IceTransportProxy::Delegate overrides.
+  void OnGatheringStateChanged(cricket::IceGatheringState new_state) override;
+  void OnCandidateGathered(const cricket::Candidate& candidate) override;
+
+  // Fills in |local_parameters_| with a random usernameFragment and a random
+  // password.
+  void GenerateLocalParameters();
+
+  bool IsClosed() const { return state_ == RTCIceTransportState::kClosed; }
+  bool RaiseExceptionIfClosed(ExceptionState& exception_state) const;
+
+  RTCIceTransportState state_ = RTCIceTransportState::kNew;
+  cricket::IceGatheringState gathering_state_ = cricket::kIceGatheringNew;
 
   HeapVector<Member<RTCIceCandidate>> local_candidates_;
   HeapVector<Member<RTCIceCandidate>> remote_candidates_;
+
+  RTCIceParameters local_parameters_;
+
   base::Optional<RTCIceCandidatePair> selected_candidate_pair_;
+
+  // Handle to the WebRTC ICE transport. Created when this binding is
+  // constructed and deleted once network traffic should be stopped.
+  std::unique_ptr<IceTransportProxy> proxy_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.idl b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.idl
index e87051e..e47b60b 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.idl
@@ -28,11 +28,13 @@
 
 // https://w3c.github.io/webrtc-pc/#rtcicetransport
 [
+   ActiveScriptWrappable,
    // Constructor from https://w3c.github.io/webrtc-ice/#rtcicetransport*
    Constructor(),
+   ConstructorCallWith=ExecutionContext,
    Exposed=Window,
    RuntimeEnabled=RTCIceTransportExtension
-] interface RTCIceTransport {
+] interface RTCIceTransport : EventTarget {
     // TODO(github.com/w3c/webrtc-ice/issues/4): role is non-null in the
     // WebRTC-PC specification.
     readonly attribute RTCIceRole? role;
@@ -43,6 +45,13 @@
     RTCIceCandidatePair? getSelectedCandidatePair();
     RTCIceParameters? getLocalParameters();
     RTCIceParameters? getRemoteParameters();
+    attribute EventHandler ongatheringstatechange;
 
-    // TODO(crbug.com/864871): Add events and extension methods.
+    // The following is defined in the WebRTC-ICE extension specification.
+    // https://w3c.github.io/webrtc-ice/#rtcicetransport*
+    [RaisesException] void gather(RTCIceGatherOptions options);
+    void stop();
+    attribute EventHandler onicecandidate;
+
+    // TODO(crbug.com/864871): Implement remaining events and extension methods.
 };
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h b/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h
index 2ea12248..6d520e60 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h
@@ -60,6 +60,9 @@
   enum ImageRequestOptimization {
     kNone = 0,          // No optimization.
     kAllowPlaceholder,  // The image is allowed to be a placeholder.
+    kDeferImageLoad,  // Defer loading the image from network. Full image might
+                      // still load if the request is already-loaded or in
+                      // memory cache.
   };
   struct ResourceWidth {
     DISALLOW_NEW();
@@ -178,7 +181,7 @@
   // Configures the request to load an image placeholder if the request is
   // eligible (e.g. the url's protocol is HTTP, etc.). If this request is
   // non-eligible, this method doesn't modify the ResourceRequest. Calling this
-  // method sets placeholder_image_request_type_ to the appropriate value.
+  // method sets image_request_optimization_ to the appropriate value.
   void SetAllowImagePlaceholder();
 
   // Configures the request to load an image as a placeholder and sets the
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index a7636e51..a9077f92 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -410,9 +410,11 @@
   // - images are disabled
   // - instructed to defer loading images from network
   if (resource->GetType() == Resource::kImage &&
-      ShouldDeferImageLoad(resource->Url()))
+      (ShouldDeferImageLoad(resource->Url()) ||
+       params.GetImageRequestOptimization() ==
+           FetchParameters::kDeferImageLoad)) {
     return false;
-
+  }
   return policy != kUse || resource->StillNeedsLoad();
 }
 
diff --git a/third_party/blink/tools/audit_non_blink_usage.py b/third_party/blink/tools/audit_non_blink_usage.py
index 1c6f3ff3..952bdaf 100755
--- a/third_party/blink/tools/audit_non_blink_usage.py
+++ b/third_party/blink/tools/audit_non_blink_usage.py
@@ -405,9 +405,24 @@
             'third_party/blink/renderer/bindings/modules/v8/serialization',
         ],
         'allowed': [
-            'webrtc::.+',
+            'cricket::.*',
             'rtc::.+',
+            'webrtc::.+',
         ]
+    },
+    {
+        'paths': [
+            'third_party/blink/renderer/modules/peerconnection/adapters/',
+        ],
+        # The code in adapters/ wraps WebRTC APIs using STL/WebRTC types only.
+        # Thus, the restriction that objects should only be created and
+        # destroyed on the same thread can be relaxed since no Blink types (like
+        # AtomicString or HeapVector) are used cross thread. These Blink types
+        # are converted to the STL/WebRTC counterparts in the parent directory.
+        'allowed': [
+            'base::OnTaskRunnerDeleter',
+            'sigslot::.+',
+        ],
     }
 ]
 
diff --git a/third_party/junit/proguard.flags b/third_party/junit/proguard.flags
index 431ea61..4f71db1 100644
--- a/third_party/junit/proguard.flags
+++ b/third_party/junit/proguard.flags
@@ -3,6 +3,6 @@
 -dontwarn org.junit.**
 -dontwarn org.hamcrest.**
 
--keepname,allowoptimization public class junit.framework.** {
+-keepnames,allowoptimization public class junit.framework.** {
     *;
 }
diff --git a/third_party/libaom/README.chromium b/third_party/libaom/README.chromium
index d8a3088..4a5ea60 100644
--- a/third_party/libaom/README.chromium
+++ b/third_party/libaom/README.chromium
@@ -2,9 +2,9 @@
 Short Name: libaom
 URL: https://aomedia.googlesource.com/aom/
 Version: 0
-Date: Monday August 06 2018
+Date: Wednesday August 22 2018
 Branch: master
-Commit: 7a76b645a08ce45ef52dfb7fd719a26c1af1da85
+Commit: 10df8d1b586133d5c04ce8907cf2d23d43765521
 License: BSD
 License File: source/libaom/LICENSE
 Security Critical: yes
diff --git a/third_party/libaom/libaom_srcs.gni b/third_party/libaom/libaom_srcs.gni
index 78199e4d..796d9eb 100644
--- a/third_party/libaom/libaom_srcs.gni
+++ b/third_party/libaom/libaom_srcs.gni
@@ -32,6 +32,7 @@
   "//third_party/libaom/source/libaom/av1/common/arm/selfguided_neon.c",
   "//third_party/libaom/source/libaom/av1/common/arm/av1_inv_txfm_neon.c",
   "//third_party/libaom/source/libaom/av1/common/arm/av1_inv_txfm_neon.h",
+  "//third_party/libaom/source/libaom/av1/common/arm/warp_plane_neon.c",
   "//third_party/libaom/source/libaom/av1/common/cdef_block_neon.c",
 ]
 
@@ -185,6 +186,7 @@
   "//third_party/libaom/source/libaom/av1/encoder/x86/av1_fwd_txfm_avx2.h",
   "//third_party/libaom/source/libaom/av1/encoder/x86/av1_fwd_txfm2d_avx2.c",
   "//third_party/libaom/source/libaom/av1/encoder/x86/wedge_utils_avx2.c",
+  "//third_party/libaom/source/libaom/av1/encoder/x86/encodetxb_avx2.c",
   "//third_party/libaom/source/libaom/av1/encoder/x86/pickrst_avx2.c",
 ]
 
diff --git a/third_party/libaom/source/config/config/aom_version.h b/third_party/libaom/source/config/config/aom_version.h
index c209ba8..86164706 100644
--- a/third_party/libaom/source/config/config/aom_version.h
+++ b/third_party/libaom/source/config/config/aom_version.h
@@ -12,8 +12,8 @@
 #define VERSION_MAJOR 1
 #define VERSION_MINOR 0
 #define VERSION_PATCH 0
-#define VERSION_EXTRA "334-g7a76b645a"
+#define VERSION_EXTRA "422-g10df8d1b58"
 #define VERSION_PACKED \
   ((VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | (VERSION_PATCH))
-#define VERSION_STRING_NOSP "1.0.0-334-g7a76b645a"
-#define VERSION_STRING " 1.0.0-334-g7a76b645a"
+#define VERSION_STRING_NOSP "1.0.0-422-g10df8d1b58"
+#define VERSION_STRING " 1.0.0-422-g10df8d1b58"
diff --git a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.asm b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.asm
index 8fcaa83e..c318b6f0 100644
--- a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.asm
@@ -13,61 +13,63 @@
 ARCH_PPC equ 0
 ARCH_X86 equ 0
 ARCH_X86_64 equ 0
-HAVE_NEON equ 1
-HAVE_DSPR2 equ 0
-HAVE_MIPS32 equ 0
-HAVE_MIPS64 equ 0
-HAVE_MSA equ 0
-HAVE_VSX equ 0
+CONFIG_2PASS_PARTITION_SEARCH_LVL equ 1
+CONFIG_ACCOUNTING equ 0
+CONFIG_ANALYZER equ 0
+CONFIG_AV1_DECODER equ 1
+CONFIG_AV1_ENCODER equ 0
+CONFIG_BIG_ENDIAN equ 0
+CONFIG_BITSTREAM_DEBUG equ 0
+CONFIG_COEFFICIENT_RANGE_CHECKING equ 0
+CONFIG_COLLECT_INTER_MODE_RD_STATS equ 1
+CONFIG_COLLECT_RD_STATS equ 0
+CONFIG_DEBUG equ 0
+CONFIG_DENOISE equ 0
+CONFIG_DIST_8X8 equ 0
+CONFIG_ENTROPY_STATS equ 0
+CONFIG_FILEOPTIONS equ 1
+CONFIG_FIX_GF_LENGTH equ 1
+CONFIG_FP_MB_STATS equ 0
+CONFIG_GCC equ 1
+CONFIG_GCOV equ 0
+CONFIG_GPROF equ 0
+CONFIG_INSPECTION equ 0
+CONFIG_INTERNAL_STATS equ 0
+CONFIG_INTER_STATS_ONLY equ 0
+CONFIG_LIBYUV equ 1
+CONFIG_LOWBITDEPTH equ 1
+CONFIG_MAX_DECODE_PROFILE equ 0
+CONFIG_MISMATCH_DEBUG equ 0
+CONFIG_MULTITHREAD equ 1
+CONFIG_NORMAL_TILE_MODE equ 1
+CONFIG_OS_SUPPORT equ 1
+CONFIG_PIC equ 0
+CONFIG_RD_DEBUG equ 0
+CONFIG_REDUCED_ENCODER_BORDER equ 0
+CONFIG_RUNTIME_CPU_DETECT equ 1
+CONFIG_SHARED equ 0
+CONFIG_SIZE_LIMIT equ 1
+CONFIG_SPATIAL_RESAMPLING equ 1
+CONFIG_STATIC equ 1
+CONFIG_WEBM_IO equ 1
+DECODE_HEIGHT_LIMIT equ 16384
+DECODE_WIDTH_LIMIT equ 16384
 HAVE_AVX equ 0
 HAVE_AVX2 equ 0
+HAVE_DSPR2 equ 0
+HAVE_FEXCEPT equ 1
+HAVE_MIPS32 equ 0
+HAVE_MIPS64 equ 0
 HAVE_MMX equ 0
+HAVE_MSA equ 0
+HAVE_NEON equ 1
+HAVE_PTHREAD_H equ 1
 HAVE_SSE equ 0
 HAVE_SSE2 equ 0
 HAVE_SSE3 equ 0
 HAVE_SSE4_1 equ 0
 HAVE_SSE4_2 equ 0
 HAVE_SSSE3 equ 0
-HAVE_FEXCEPT equ 1
-HAVE_PTHREAD_H equ 1
 HAVE_UNISTD_H equ 1
+HAVE_VSX equ 0
 HAVE_WXWIDGETS equ 0
-CONFIG_AV1_DECODER equ 1
-CONFIG_AV1_ENCODER equ 0
-CONFIG_BIG_ENDIAN equ 0
-CONFIG_GCC equ 1
-CONFIG_GCOV equ 0
-CONFIG_GPROF equ 0
-CONFIG_LIBYUV equ 1
-CONFIG_MULTITHREAD equ 1
-CONFIG_OS_SUPPORT equ 1
-CONFIG_PIC equ 0
-CONFIG_RUNTIME_CPU_DETECT equ 1
-CONFIG_SHARED equ 0
-CONFIG_STATIC equ 1
-CONFIG_WEBM_IO equ 1
-CONFIG_BITSTREAM_DEBUG equ 0
-CONFIG_DEBUG equ 0
-CONFIG_MISMATCH_DEBUG equ 0
-CONFIG_ACCOUNTING equ 0
-CONFIG_ANALYZER equ 0
-CONFIG_COEFFICIENT_RANGE_CHECKING equ 0
-CONFIG_DENOISE equ 0
-CONFIG_FILEOPTIONS equ 1
-CONFIG_FIX_GF_LENGTH equ 1
-CONFIG_INSPECTION equ 0
-CONFIG_INTERNAL_STATS equ 0
-CONFIG_LOWBITDEPTH equ 1
-CONFIG_MAX_DECODE_PROFILE equ 0
-CONFIG_NORMAL_TILE_MODE equ 1
-CONFIG_SIZE_LIMIT equ 1
-CONFIG_SPATIAL_RESAMPLING equ 1
-DECODE_HEIGHT_LIMIT equ 16384
-DECODE_WIDTH_LIMIT equ 16384
-CONFIG_COLLECT_INTER_MODE_RD_STATS equ 1
-CONFIG_COLLECT_RD_STATS equ 0
-CONFIG_DIST_8X8 equ 1
-CONFIG_ENTROPY_STATS equ 0
-CONFIG_FP_MB_STATS equ 0
-CONFIG_INTER_STATS_ONLY equ 0
-CONFIG_RD_DEBUG equ 0
diff --git a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.c b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.c
index 71e65edd..1793ab5 100644
--- a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.c
+++ b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/wtc/chromium.2/src/third_party/libaom/source/libaom/build/cmake/toolchains/armv7-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
+static const char* const cfg = "cmake ../source/libaom -G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"../source/libaom/build/cmake/toolchains/armv7-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.h b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.h
index e13f9d10..20ef3e90 100644
--- a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.h
@@ -10,68 +10,70 @@
  */
 #ifndef AOM_CONFIG_H_
 #define AOM_CONFIG_H_
-#define INLINE inline
 #define ARCH_ARM 1
 #define ARCH_MIPS 0
 #define ARCH_PPC 0
 #define ARCH_X86 0
 #define ARCH_X86_64 0
-#define HAVE_NEON 1
-#define HAVE_DSPR2 0
-#define HAVE_MIPS32 0
-#define HAVE_MIPS64 0
-#define HAVE_MSA 0
-#define HAVE_VSX 0
+#define CONFIG_2PASS_PARTITION_SEARCH_LVL 1
+#define CONFIG_ACCOUNTING 0
+#define CONFIG_ANALYZER 0
+#define CONFIG_AV1_DECODER 1
+#define CONFIG_AV1_ENCODER 0
+#define CONFIG_BIG_ENDIAN 0
+#define CONFIG_BITSTREAM_DEBUG 0
+#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
+#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
+#define CONFIG_COLLECT_RD_STATS 0
+#define CONFIG_DEBUG 0
+#define CONFIG_DENOISE 0
+#define CONFIG_DIST_8X8 0
+#define CONFIG_ENTROPY_STATS 0
+#define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
+#define CONFIG_FP_MB_STATS 0
+#define CONFIG_GCC 1
+#define CONFIG_GCOV 0
+#define CONFIG_GPROF 0
+#define CONFIG_INSPECTION 0
+#define CONFIG_INTERNAL_STATS 0
+#define CONFIG_INTER_STATS_ONLY 0
+#define CONFIG_LIBYUV 1
+#define CONFIG_LOWBITDEPTH 1
+#define CONFIG_MAX_DECODE_PROFILE 0
+#define CONFIG_MISMATCH_DEBUG 0
+#define CONFIG_MULTITHREAD 1
+#define CONFIG_NORMAL_TILE_MODE 1
+#define CONFIG_OS_SUPPORT 1
+#define CONFIG_PIC 0
+#define CONFIG_RD_DEBUG 0
+#define CONFIG_REDUCED_ENCODER_BORDER 0
+#define CONFIG_RUNTIME_CPU_DETECT 1
+#define CONFIG_SHARED 0
+#define CONFIG_SIZE_LIMIT 1
+#define CONFIG_SPATIAL_RESAMPLING 1
+#define CONFIG_STATIC 1
+#define CONFIG_WEBM_IO 1
+#define DECODE_HEIGHT_LIMIT 16384
+#define DECODE_WIDTH_LIMIT 16384
 #define HAVE_AVX 0
 #define HAVE_AVX2 0
+#define HAVE_DSPR2 0
+#define HAVE_FEXCEPT 1
+#define HAVE_MIPS32 0
+#define HAVE_MIPS64 0
 #define HAVE_MMX 0
+#define HAVE_MSA 0
+#define HAVE_NEON 1
+#define HAVE_PTHREAD_H 1
 #define HAVE_SSE 0
 #define HAVE_SSE2 0
 #define HAVE_SSE3 0
 #define HAVE_SSE4_1 0
 #define HAVE_SSE4_2 0
 #define HAVE_SSSE3 0
-#define HAVE_FEXCEPT 1
-#define HAVE_PTHREAD_H 1
 #define HAVE_UNISTD_H 1
+#define HAVE_VSX 0
 #define HAVE_WXWIDGETS 0
-#define CONFIG_AV1_DECODER 1
-#define CONFIG_AV1_ENCODER 0
-#define CONFIG_BIG_ENDIAN 0
-#define CONFIG_GCC 1
-#define CONFIG_GCOV 0
-#define CONFIG_GPROF 0
-#define CONFIG_LIBYUV 1
-#define CONFIG_MULTITHREAD 1
-#define CONFIG_OS_SUPPORT 1
-#define CONFIG_PIC 0
-#define CONFIG_RUNTIME_CPU_DETECT 1
-#define CONFIG_SHARED 0
-#define CONFIG_STATIC 1
-#define CONFIG_WEBM_IO 1
-#define CONFIG_BITSTREAM_DEBUG 0
-#define CONFIG_DEBUG 0
-#define CONFIG_MISMATCH_DEBUG 0
-#define CONFIG_ACCOUNTING 0
-#define CONFIG_ANALYZER 0
-#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
-#define CONFIG_DENOISE 0
-#define CONFIG_FILEOPTIONS 1
-#define CONFIG_FIX_GF_LENGTH 1
-#define CONFIG_INSPECTION 0
-#define CONFIG_INTERNAL_STATS 0
-#define CONFIG_LOWBITDEPTH 1
-#define CONFIG_MAX_DECODE_PROFILE 0
-#define CONFIG_NORMAL_TILE_MODE 1
-#define CONFIG_SIZE_LIMIT 1
-#define CONFIG_SPATIAL_RESAMPLING 1
-#define DECODE_HEIGHT_LIMIT 16384
-#define DECODE_WIDTH_LIMIT 16384
-#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
-#define CONFIG_COLLECT_RD_STATS 0
-#define CONFIG_DIST_8X8 1
-#define CONFIG_ENTROPY_STATS 0
-#define CONFIG_FP_MB_STATS 0
-#define CONFIG_INTER_STATS_ONLY 0
-#define CONFIG_RD_DEBUG 0
-#endif /* AOM_CONFIG_H_ */
+#define INLINE inline
+#endif  // AOM_CONFIG_H_
diff --git a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/av1_rtcd.h b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/av1_rtcd.h
index 11a97ba..0383950 100644
--- a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/av1_rtcd.h
@@ -544,6 +544,36 @@
                                    int bd);
 #define av1_highbd_dr_prediction_z3 av1_highbd_dr_prediction_z3_c
 
+void av1_highbd_inv_txfm_add_c(const tran_low_t* dqcoeff,
+                               uint8_t* dst,
+                               int stride,
+                               const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add av1_highbd_inv_txfm_add_c
+
+void av1_highbd_inv_txfm_add_16x16_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_16x16 av1_highbd_inv_txfm_add_16x16_c
+
+void av1_highbd_inv_txfm_add_32x32_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_32x32 av1_highbd_inv_txfm_add_32x32_c
+
+void av1_highbd_inv_txfm_add_4x4_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_4x4 av1_highbd_inv_txfm_add_4x4_c
+
+void av1_highbd_inv_txfm_add_8x8_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_8x8 av1_highbd_inv_txfm_add_8x8_c
+
 void av1_highbd_iwht4x4_16_add_c(const tran_low_t* input,
                                  uint8_t* dest,
                                  int dest_stride,
@@ -989,7 +1019,42 @@
                        int16_t beta,
                        int16_t gamma,
                        int16_t delta);
-#define av1_warp_affine av1_warp_affine_c
+void av1_warp_affine_neon(const int32_t* mat,
+                          const uint8_t* ref,
+                          int width,
+                          int height,
+                          int stride,
+                          uint8_t* pred,
+                          int p_col,
+                          int p_row,
+                          int p_width,
+                          int p_height,
+                          int p_stride,
+                          int subsampling_x,
+                          int subsampling_y,
+                          ConvolveParams* conv_params,
+                          int16_t alpha,
+                          int16_t beta,
+                          int16_t gamma,
+                          int16_t delta);
+RTCD_EXTERN void (*av1_warp_affine)(const int32_t* mat,
+                                    const uint8_t* ref,
+                                    int width,
+                                    int height,
+                                    int stride,
+                                    uint8_t* pred,
+                                    int p_col,
+                                    int p_row,
+                                    int p_width,
+                                    int p_height,
+                                    int p_stride,
+                                    int subsampling_x,
+                                    int subsampling_y,
+                                    ConvolveParams* conv_params,
+                                    int16_t alpha,
+                                    int16_t beta,
+                                    int16_t gamma,
+                                    int16_t delta);
 
 void av1_wiener_convolve_add_src_c(const uint8_t* src,
                                    ptrdiff_t src_stride,
@@ -1204,6 +1269,9 @@
   av1_selfguided_restoration = av1_selfguided_restoration_c;
   if (flags & HAS_NEON)
     av1_selfguided_restoration = av1_selfguided_restoration_neon;
+  av1_warp_affine = av1_warp_affine_c;
+  if (flags & HAS_NEON)
+    av1_warp_affine = av1_warp_affine_neon;
   av1_wiener_convolve_add_src = av1_wiener_convolve_add_src_c;
   if (flags & HAS_NEON)
     av1_wiener_convolve_add_src = av1_wiener_convolve_add_src_neon;
diff --git a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.asm b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.asm
index e9cdd34..c00fce0c 100644
--- a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.asm
@@ -13,61 +13,63 @@
 ARCH_PPC equ 0
 ARCH_X86 equ 0
 ARCH_X86_64 equ 0
-HAVE_NEON equ 1
-HAVE_DSPR2 equ 0
-HAVE_MIPS32 equ 0
-HAVE_MIPS64 equ 0
-HAVE_MSA equ 0
-HAVE_VSX equ 0
+CONFIG_2PASS_PARTITION_SEARCH_LVL equ 1
+CONFIG_ACCOUNTING equ 0
+CONFIG_ANALYZER equ 0
+CONFIG_AV1_DECODER equ 1
+CONFIG_AV1_ENCODER equ 0
+CONFIG_BIG_ENDIAN equ 0
+CONFIG_BITSTREAM_DEBUG equ 0
+CONFIG_COEFFICIENT_RANGE_CHECKING equ 0
+CONFIG_COLLECT_INTER_MODE_RD_STATS equ 1
+CONFIG_COLLECT_RD_STATS equ 0
+CONFIG_DEBUG equ 0
+CONFIG_DENOISE equ 0
+CONFIG_DIST_8X8 equ 0
+CONFIG_ENTROPY_STATS equ 0
+CONFIG_FILEOPTIONS equ 1
+CONFIG_FIX_GF_LENGTH equ 1
+CONFIG_FP_MB_STATS equ 0
+CONFIG_GCC equ 1
+CONFIG_GCOV equ 0
+CONFIG_GPROF equ 0
+CONFIG_INSPECTION equ 0
+CONFIG_INTERNAL_STATS equ 0
+CONFIG_INTER_STATS_ONLY equ 0
+CONFIG_LIBYUV equ 1
+CONFIG_LOWBITDEPTH equ 1
+CONFIG_MAX_DECODE_PROFILE equ 0
+CONFIG_MISMATCH_DEBUG equ 0
+CONFIG_MULTITHREAD equ 1
+CONFIG_NORMAL_TILE_MODE equ 1
+CONFIG_OS_SUPPORT equ 1
+CONFIG_PIC equ 0
+CONFIG_RD_DEBUG equ 0
+CONFIG_REDUCED_ENCODER_BORDER equ 0
+CONFIG_RUNTIME_CPU_DETECT equ 0
+CONFIG_SHARED equ 0
+CONFIG_SIZE_LIMIT equ 1
+CONFIG_SPATIAL_RESAMPLING equ 1
+CONFIG_STATIC equ 1
+CONFIG_WEBM_IO equ 1
+DECODE_HEIGHT_LIMIT equ 16384
+DECODE_WIDTH_LIMIT equ 16384
 HAVE_AVX equ 0
 HAVE_AVX2 equ 0
+HAVE_DSPR2 equ 0
+HAVE_FEXCEPT equ 1
+HAVE_MIPS32 equ 0
+HAVE_MIPS64 equ 0
 HAVE_MMX equ 0
+HAVE_MSA equ 0
+HAVE_NEON equ 1
+HAVE_PTHREAD_H equ 1
 HAVE_SSE equ 0
 HAVE_SSE2 equ 0
 HAVE_SSE3 equ 0
 HAVE_SSE4_1 equ 0
 HAVE_SSE4_2 equ 0
 HAVE_SSSE3 equ 0
-HAVE_FEXCEPT equ 1
-HAVE_PTHREAD_H equ 1
 HAVE_UNISTD_H equ 1
+HAVE_VSX equ 0
 HAVE_WXWIDGETS equ 0
-CONFIG_AV1_DECODER equ 1
-CONFIG_AV1_ENCODER equ 0
-CONFIG_BIG_ENDIAN equ 0
-CONFIG_GCC equ 1
-CONFIG_GCOV equ 0
-CONFIG_GPROF equ 0
-CONFIG_LIBYUV equ 1
-CONFIG_MULTITHREAD equ 1
-CONFIG_OS_SUPPORT equ 1
-CONFIG_PIC equ 0
-CONFIG_RUNTIME_CPU_DETECT equ 0
-CONFIG_SHARED equ 0
-CONFIG_STATIC equ 1
-CONFIG_WEBM_IO equ 1
-CONFIG_BITSTREAM_DEBUG equ 0
-CONFIG_DEBUG equ 0
-CONFIG_MISMATCH_DEBUG equ 0
-CONFIG_ACCOUNTING equ 0
-CONFIG_ANALYZER equ 0
-CONFIG_COEFFICIENT_RANGE_CHECKING equ 0
-CONFIG_DENOISE equ 0
-CONFIG_FILEOPTIONS equ 1
-CONFIG_FIX_GF_LENGTH equ 1
-CONFIG_INSPECTION equ 0
-CONFIG_INTERNAL_STATS equ 0
-CONFIG_LOWBITDEPTH equ 1
-CONFIG_MAX_DECODE_PROFILE equ 0
-CONFIG_NORMAL_TILE_MODE equ 1
-CONFIG_SIZE_LIMIT equ 1
-CONFIG_SPATIAL_RESAMPLING equ 1
-DECODE_HEIGHT_LIMIT equ 16384
-DECODE_WIDTH_LIMIT equ 16384
-CONFIG_COLLECT_INTER_MODE_RD_STATS equ 1
-CONFIG_COLLECT_RD_STATS equ 0
-CONFIG_DIST_8X8 equ 1
-CONFIG_ENTROPY_STATS equ 0
-CONFIG_FP_MB_STATS equ 0
-CONFIG_INTER_STATS_ONLY equ 0
-CONFIG_RD_DEBUG equ 0
diff --git a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.c b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.c
index 71e65edd..1793ab5 100644
--- a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.c
+++ b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/wtc/chromium.2/src/third_party/libaom/source/libaom/build/cmake/toolchains/armv7-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
+static const char* const cfg = "cmake ../source/libaom -G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"../source/libaom/build/cmake/toolchains/armv7-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.h b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.h
index bc574c1..574ad37 100644
--- a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.h
@@ -10,68 +10,70 @@
  */
 #ifndef AOM_CONFIG_H_
 #define AOM_CONFIG_H_
-#define INLINE inline
 #define ARCH_ARM 1
 #define ARCH_MIPS 0
 #define ARCH_PPC 0
 #define ARCH_X86 0
 #define ARCH_X86_64 0
-#define HAVE_NEON 1
-#define HAVE_DSPR2 0
-#define HAVE_MIPS32 0
-#define HAVE_MIPS64 0
-#define HAVE_MSA 0
-#define HAVE_VSX 0
+#define CONFIG_2PASS_PARTITION_SEARCH_LVL 1
+#define CONFIG_ACCOUNTING 0
+#define CONFIG_ANALYZER 0
+#define CONFIG_AV1_DECODER 1
+#define CONFIG_AV1_ENCODER 0
+#define CONFIG_BIG_ENDIAN 0
+#define CONFIG_BITSTREAM_DEBUG 0
+#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
+#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
+#define CONFIG_COLLECT_RD_STATS 0
+#define CONFIG_DEBUG 0
+#define CONFIG_DENOISE 0
+#define CONFIG_DIST_8X8 0
+#define CONFIG_ENTROPY_STATS 0
+#define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
+#define CONFIG_FP_MB_STATS 0
+#define CONFIG_GCC 1
+#define CONFIG_GCOV 0
+#define CONFIG_GPROF 0
+#define CONFIG_INSPECTION 0
+#define CONFIG_INTERNAL_STATS 0
+#define CONFIG_INTER_STATS_ONLY 0
+#define CONFIG_LIBYUV 1
+#define CONFIG_LOWBITDEPTH 1
+#define CONFIG_MAX_DECODE_PROFILE 0
+#define CONFIG_MISMATCH_DEBUG 0
+#define CONFIG_MULTITHREAD 1
+#define CONFIG_NORMAL_TILE_MODE 1
+#define CONFIG_OS_SUPPORT 1
+#define CONFIG_PIC 0
+#define CONFIG_RD_DEBUG 0
+#define CONFIG_REDUCED_ENCODER_BORDER 0
+#define CONFIG_RUNTIME_CPU_DETECT 0
+#define CONFIG_SHARED 0
+#define CONFIG_SIZE_LIMIT 1
+#define CONFIG_SPATIAL_RESAMPLING 1
+#define CONFIG_STATIC 1
+#define CONFIG_WEBM_IO 1
+#define DECODE_HEIGHT_LIMIT 16384
+#define DECODE_WIDTH_LIMIT 16384
 #define HAVE_AVX 0
 #define HAVE_AVX2 0
+#define HAVE_DSPR2 0
+#define HAVE_FEXCEPT 1
+#define HAVE_MIPS32 0
+#define HAVE_MIPS64 0
 #define HAVE_MMX 0
+#define HAVE_MSA 0
+#define HAVE_NEON 1
+#define HAVE_PTHREAD_H 1
 #define HAVE_SSE 0
 #define HAVE_SSE2 0
 #define HAVE_SSE3 0
 #define HAVE_SSE4_1 0
 #define HAVE_SSE4_2 0
 #define HAVE_SSSE3 0
-#define HAVE_FEXCEPT 1
-#define HAVE_PTHREAD_H 1
 #define HAVE_UNISTD_H 1
+#define HAVE_VSX 0
 #define HAVE_WXWIDGETS 0
-#define CONFIG_AV1_DECODER 1
-#define CONFIG_AV1_ENCODER 0
-#define CONFIG_BIG_ENDIAN 0
-#define CONFIG_GCC 1
-#define CONFIG_GCOV 0
-#define CONFIG_GPROF 0
-#define CONFIG_LIBYUV 1
-#define CONFIG_MULTITHREAD 1
-#define CONFIG_OS_SUPPORT 1
-#define CONFIG_PIC 0
-#define CONFIG_RUNTIME_CPU_DETECT 0
-#define CONFIG_SHARED 0
-#define CONFIG_STATIC 1
-#define CONFIG_WEBM_IO 1
-#define CONFIG_BITSTREAM_DEBUG 0
-#define CONFIG_DEBUG 0
-#define CONFIG_MISMATCH_DEBUG 0
-#define CONFIG_ACCOUNTING 0
-#define CONFIG_ANALYZER 0
-#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
-#define CONFIG_DENOISE 0
-#define CONFIG_FILEOPTIONS 1
-#define CONFIG_FIX_GF_LENGTH 1
-#define CONFIG_INSPECTION 0
-#define CONFIG_INTERNAL_STATS 0
-#define CONFIG_LOWBITDEPTH 1
-#define CONFIG_MAX_DECODE_PROFILE 0
-#define CONFIG_NORMAL_TILE_MODE 1
-#define CONFIG_SIZE_LIMIT 1
-#define CONFIG_SPATIAL_RESAMPLING 1
-#define DECODE_HEIGHT_LIMIT 16384
-#define DECODE_WIDTH_LIMIT 16384
-#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
-#define CONFIG_COLLECT_RD_STATS 0
-#define CONFIG_DIST_8X8 1
-#define CONFIG_ENTROPY_STATS 0
-#define CONFIG_FP_MB_STATS 0
-#define CONFIG_INTER_STATS_ONLY 0
-#define CONFIG_RD_DEBUG 0
-#endif /* AOM_CONFIG_H_ */
+#define INLINE inline
+#endif  // AOM_CONFIG_H_
diff --git a/third_party/libaom/source/config/linux/arm-neon/config/av1_rtcd.h b/third_party/libaom/source/config/linux/arm-neon/config/av1_rtcd.h
index e2f99fa0..efe44ff 100644
--- a/third_party/libaom/source/config/linux/arm-neon/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/linux/arm-neon/config/av1_rtcd.h
@@ -483,6 +483,36 @@
                                    int bd);
 #define av1_highbd_dr_prediction_z3 av1_highbd_dr_prediction_z3_c
 
+void av1_highbd_inv_txfm_add_c(const tran_low_t* dqcoeff,
+                               uint8_t* dst,
+                               int stride,
+                               const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add av1_highbd_inv_txfm_add_c
+
+void av1_highbd_inv_txfm_add_16x16_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_16x16 av1_highbd_inv_txfm_add_16x16_c
+
+void av1_highbd_inv_txfm_add_32x32_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_32x32 av1_highbd_inv_txfm_add_32x32_c
+
+void av1_highbd_inv_txfm_add_4x4_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_4x4 av1_highbd_inv_txfm_add_4x4_c
+
+void av1_highbd_inv_txfm_add_8x8_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_8x8 av1_highbd_inv_txfm_add_8x8_c
+
 void av1_highbd_iwht4x4_16_add_c(const tran_low_t* input,
                                  uint8_t* dest,
                                  int dest_stride,
@@ -872,7 +902,25 @@
                        int16_t beta,
                        int16_t gamma,
                        int16_t delta);
-#define av1_warp_affine av1_warp_affine_c
+void av1_warp_affine_neon(const int32_t* mat,
+                          const uint8_t* ref,
+                          int width,
+                          int height,
+                          int stride,
+                          uint8_t* pred,
+                          int p_col,
+                          int p_row,
+                          int p_width,
+                          int p_height,
+                          int p_stride,
+                          int subsampling_x,
+                          int subsampling_y,
+                          ConvolveParams* conv_params,
+                          int16_t alpha,
+                          int16_t beta,
+                          int16_t gamma,
+                          int16_t delta);
+#define av1_warp_affine av1_warp_affine_neon
 
 void av1_wiener_convolve_add_src_c(const uint8_t* src,
                                    ptrdiff_t src_stride,
diff --git a/third_party/libaom/source/config/linux/arm/config/aom_config.asm b/third_party/libaom/source/config/linux/arm/config/aom_config.asm
index 10494db..c1d2709 100644
--- a/third_party/libaom/source/config/linux/arm/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/arm/config/aom_config.asm
@@ -13,61 +13,63 @@
 ARCH_PPC equ 0
 ARCH_X86 equ 0
 ARCH_X86_64 equ 0
-HAVE_NEON equ 0
-HAVE_DSPR2 equ 0
-HAVE_MIPS32 equ 0
-HAVE_MIPS64 equ 0
-HAVE_MSA equ 0
-HAVE_VSX equ 0
+CONFIG_2PASS_PARTITION_SEARCH_LVL equ 1
+CONFIG_ACCOUNTING equ 0
+CONFIG_ANALYZER equ 0
+CONFIG_AV1_DECODER equ 1
+CONFIG_AV1_ENCODER equ 0
+CONFIG_BIG_ENDIAN equ 0
+CONFIG_BITSTREAM_DEBUG equ 0
+CONFIG_COEFFICIENT_RANGE_CHECKING equ 0
+CONFIG_COLLECT_INTER_MODE_RD_STATS equ 1
+CONFIG_COLLECT_RD_STATS equ 0
+CONFIG_DEBUG equ 0
+CONFIG_DENOISE equ 0
+CONFIG_DIST_8X8 equ 0
+CONFIG_ENTROPY_STATS equ 0
+CONFIG_FILEOPTIONS equ 1
+CONFIG_FIX_GF_LENGTH equ 1
+CONFIG_FP_MB_STATS equ 0
+CONFIG_GCC equ 1
+CONFIG_GCOV equ 0
+CONFIG_GPROF equ 0
+CONFIG_INSPECTION equ 0
+CONFIG_INTERNAL_STATS equ 0
+CONFIG_INTER_STATS_ONLY equ 0
+CONFIG_LIBYUV equ 1
+CONFIG_LOWBITDEPTH equ 1
+CONFIG_MAX_DECODE_PROFILE equ 0
+CONFIG_MISMATCH_DEBUG equ 0
+CONFIG_MULTITHREAD equ 1
+CONFIG_NORMAL_TILE_MODE equ 1
+CONFIG_OS_SUPPORT equ 1
+CONFIG_PIC equ 0
+CONFIG_RD_DEBUG equ 0
+CONFIG_REDUCED_ENCODER_BORDER equ 0
+CONFIG_RUNTIME_CPU_DETECT equ 0
+CONFIG_SHARED equ 0
+CONFIG_SIZE_LIMIT equ 1
+CONFIG_SPATIAL_RESAMPLING equ 1
+CONFIG_STATIC equ 1
+CONFIG_WEBM_IO equ 1
+DECODE_HEIGHT_LIMIT equ 16384
+DECODE_WIDTH_LIMIT equ 16384
 HAVE_AVX equ 0
 HAVE_AVX2 equ 0
+HAVE_DSPR2 equ 0
+HAVE_FEXCEPT equ 1
+HAVE_MIPS32 equ 0
+HAVE_MIPS64 equ 0
 HAVE_MMX equ 0
+HAVE_MSA equ 0
+HAVE_NEON equ 0
+HAVE_PTHREAD_H equ 1
 HAVE_SSE equ 0
 HAVE_SSE2 equ 0
 HAVE_SSE3 equ 0
 HAVE_SSE4_1 equ 0
 HAVE_SSE4_2 equ 0
 HAVE_SSSE3 equ 0
-HAVE_FEXCEPT equ 1
-HAVE_PTHREAD_H equ 1
 HAVE_UNISTD_H equ 1
+HAVE_VSX equ 0
 HAVE_WXWIDGETS equ 0
-CONFIG_AV1_DECODER equ 1
-CONFIG_AV1_ENCODER equ 0
-CONFIG_BIG_ENDIAN equ 0
-CONFIG_GCC equ 1
-CONFIG_GCOV equ 0
-CONFIG_GPROF equ 0
-CONFIG_LIBYUV equ 1
-CONFIG_MULTITHREAD equ 1
-CONFIG_OS_SUPPORT equ 1
-CONFIG_PIC equ 0
-CONFIG_RUNTIME_CPU_DETECT equ 0
-CONFIG_SHARED equ 0
-CONFIG_STATIC equ 1
-CONFIG_WEBM_IO equ 1
-CONFIG_BITSTREAM_DEBUG equ 0
-CONFIG_DEBUG equ 0
-CONFIG_MISMATCH_DEBUG equ 0
-CONFIG_ACCOUNTING equ 0
-CONFIG_ANALYZER equ 0
-CONFIG_COEFFICIENT_RANGE_CHECKING equ 0
-CONFIG_DENOISE equ 0
-CONFIG_FILEOPTIONS equ 1
-CONFIG_FIX_GF_LENGTH equ 1
-CONFIG_INSPECTION equ 0
-CONFIG_INTERNAL_STATS equ 0
-CONFIG_LOWBITDEPTH equ 1
-CONFIG_MAX_DECODE_PROFILE equ 0
-CONFIG_NORMAL_TILE_MODE equ 1
-CONFIG_SIZE_LIMIT equ 1
-CONFIG_SPATIAL_RESAMPLING equ 1
-DECODE_HEIGHT_LIMIT equ 16384
-DECODE_WIDTH_LIMIT equ 16384
-CONFIG_COLLECT_INTER_MODE_RD_STATS equ 1
-CONFIG_COLLECT_RD_STATS equ 0
-CONFIG_DIST_8X8 equ 1
-CONFIG_ENTROPY_STATS equ 0
-CONFIG_FP_MB_STATS equ 0
-CONFIG_INTER_STATS_ONLY equ 0
-CONFIG_RD_DEBUG equ 0
diff --git a/third_party/libaom/source/config/linux/arm/config/aom_config.c b/third_party/libaom/source/config/linux/arm/config/aom_config.c
index 15f45c9..262160d 100644
--- a/third_party/libaom/source/config/linux/arm/config/aom_config.c
+++ b/third_party/libaom/source/config/linux/arm/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/wtc/chromium.2/src/third_party/libaom/source/libaom/build/cmake/toolchains/armv7-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384 -DENABLE_NEON=0";
+static const char* const cfg = "cmake ../source/libaom -G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"../source/libaom/build/cmake/toolchains/armv7-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384 -DENABLE_NEON=0";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/linux/arm/config/aom_config.h b/third_party/libaom/source/config/linux/arm/config/aom_config.h
index bf2001e4..278a1ef 100644
--- a/third_party/libaom/source/config/linux/arm/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/arm/config/aom_config.h
@@ -10,68 +10,70 @@
  */
 #ifndef AOM_CONFIG_H_
 #define AOM_CONFIG_H_
-#define INLINE inline
 #define ARCH_ARM 1
 #define ARCH_MIPS 0
 #define ARCH_PPC 0
 #define ARCH_X86 0
 #define ARCH_X86_64 0
-#define HAVE_NEON 0
-#define HAVE_DSPR2 0
-#define HAVE_MIPS32 0
-#define HAVE_MIPS64 0
-#define HAVE_MSA 0
-#define HAVE_VSX 0
+#define CONFIG_2PASS_PARTITION_SEARCH_LVL 1
+#define CONFIG_ACCOUNTING 0
+#define CONFIG_ANALYZER 0
+#define CONFIG_AV1_DECODER 1
+#define CONFIG_AV1_ENCODER 0
+#define CONFIG_BIG_ENDIAN 0
+#define CONFIG_BITSTREAM_DEBUG 0
+#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
+#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
+#define CONFIG_COLLECT_RD_STATS 0
+#define CONFIG_DEBUG 0
+#define CONFIG_DENOISE 0
+#define CONFIG_DIST_8X8 0
+#define CONFIG_ENTROPY_STATS 0
+#define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
+#define CONFIG_FP_MB_STATS 0
+#define CONFIG_GCC 1
+#define CONFIG_GCOV 0
+#define CONFIG_GPROF 0
+#define CONFIG_INSPECTION 0
+#define CONFIG_INTERNAL_STATS 0
+#define CONFIG_INTER_STATS_ONLY 0
+#define CONFIG_LIBYUV 1
+#define CONFIG_LOWBITDEPTH 1
+#define CONFIG_MAX_DECODE_PROFILE 0
+#define CONFIG_MISMATCH_DEBUG 0
+#define CONFIG_MULTITHREAD 1
+#define CONFIG_NORMAL_TILE_MODE 1
+#define CONFIG_OS_SUPPORT 1
+#define CONFIG_PIC 0
+#define CONFIG_RD_DEBUG 0
+#define CONFIG_REDUCED_ENCODER_BORDER 0
+#define CONFIG_RUNTIME_CPU_DETECT 0
+#define CONFIG_SHARED 0
+#define CONFIG_SIZE_LIMIT 1
+#define CONFIG_SPATIAL_RESAMPLING 1
+#define CONFIG_STATIC 1
+#define CONFIG_WEBM_IO 1
+#define DECODE_HEIGHT_LIMIT 16384
+#define DECODE_WIDTH_LIMIT 16384
 #define HAVE_AVX 0
 #define HAVE_AVX2 0
+#define HAVE_DSPR2 0
+#define HAVE_FEXCEPT 1
+#define HAVE_MIPS32 0
+#define HAVE_MIPS64 0
 #define HAVE_MMX 0
+#define HAVE_MSA 0
+#define HAVE_NEON 0
+#define HAVE_PTHREAD_H 1
 #define HAVE_SSE 0
 #define HAVE_SSE2 0
 #define HAVE_SSE3 0
 #define HAVE_SSE4_1 0
 #define HAVE_SSE4_2 0
 #define HAVE_SSSE3 0
-#define HAVE_FEXCEPT 1
-#define HAVE_PTHREAD_H 1
 #define HAVE_UNISTD_H 1
+#define HAVE_VSX 0
 #define HAVE_WXWIDGETS 0
-#define CONFIG_AV1_DECODER 1
-#define CONFIG_AV1_ENCODER 0
-#define CONFIG_BIG_ENDIAN 0
-#define CONFIG_GCC 1
-#define CONFIG_GCOV 0
-#define CONFIG_GPROF 0
-#define CONFIG_LIBYUV 1
-#define CONFIG_MULTITHREAD 1
-#define CONFIG_OS_SUPPORT 1
-#define CONFIG_PIC 0
-#define CONFIG_RUNTIME_CPU_DETECT 0
-#define CONFIG_SHARED 0
-#define CONFIG_STATIC 1
-#define CONFIG_WEBM_IO 1
-#define CONFIG_BITSTREAM_DEBUG 0
-#define CONFIG_DEBUG 0
-#define CONFIG_MISMATCH_DEBUG 0
-#define CONFIG_ACCOUNTING 0
-#define CONFIG_ANALYZER 0
-#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
-#define CONFIG_DENOISE 0
-#define CONFIG_FILEOPTIONS 1
-#define CONFIG_FIX_GF_LENGTH 1
-#define CONFIG_INSPECTION 0
-#define CONFIG_INTERNAL_STATS 0
-#define CONFIG_LOWBITDEPTH 1
-#define CONFIG_MAX_DECODE_PROFILE 0
-#define CONFIG_NORMAL_TILE_MODE 1
-#define CONFIG_SIZE_LIMIT 1
-#define CONFIG_SPATIAL_RESAMPLING 1
-#define DECODE_HEIGHT_LIMIT 16384
-#define DECODE_WIDTH_LIMIT 16384
-#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
-#define CONFIG_COLLECT_RD_STATS 0
-#define CONFIG_DIST_8X8 1
-#define CONFIG_ENTROPY_STATS 0
-#define CONFIG_FP_MB_STATS 0
-#define CONFIG_INTER_STATS_ONLY 0
-#define CONFIG_RD_DEBUG 0
-#endif /* AOM_CONFIG_H_ */
+#define INLINE inline
+#endif  // AOM_CONFIG_H_
diff --git a/third_party/libaom/source/config/linux/arm/config/av1_rtcd.h b/third_party/libaom/source/config/linux/arm/config/av1_rtcd.h
index a418e4d7..088a39ab 100644
--- a/third_party/libaom/source/config/linux/arm/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/linux/arm/config/av1_rtcd.h
@@ -418,6 +418,36 @@
                                    int bd);
 #define av1_highbd_dr_prediction_z3 av1_highbd_dr_prediction_z3_c
 
+void av1_highbd_inv_txfm_add_c(const tran_low_t* dqcoeff,
+                               uint8_t* dst,
+                               int stride,
+                               const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add av1_highbd_inv_txfm_add_c
+
+void av1_highbd_inv_txfm_add_16x16_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_16x16 av1_highbd_inv_txfm_add_16x16_c
+
+void av1_highbd_inv_txfm_add_32x32_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_32x32 av1_highbd_inv_txfm_add_32x32_c
+
+void av1_highbd_inv_txfm_add_4x4_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_4x4 av1_highbd_inv_txfm_add_4x4_c
+
+void av1_highbd_inv_txfm_add_8x8_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_8x8 av1_highbd_inv_txfm_add_8x8_c
+
 void av1_highbd_iwht4x4_16_add_c(const tran_low_t* input,
                                  uint8_t* dest,
                                  int dest_stride,
diff --git a/third_party/libaom/source/config/linux/arm64/config/aom_config.asm b/third_party/libaom/source/config/linux/arm64/config/aom_config.asm
index e9cdd34..c00fce0c 100644
--- a/third_party/libaom/source/config/linux/arm64/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/arm64/config/aom_config.asm
@@ -13,61 +13,63 @@
 ARCH_PPC equ 0
 ARCH_X86 equ 0
 ARCH_X86_64 equ 0
-HAVE_NEON equ 1
-HAVE_DSPR2 equ 0
-HAVE_MIPS32 equ 0
-HAVE_MIPS64 equ 0
-HAVE_MSA equ 0
-HAVE_VSX equ 0
+CONFIG_2PASS_PARTITION_SEARCH_LVL equ 1
+CONFIG_ACCOUNTING equ 0
+CONFIG_ANALYZER equ 0
+CONFIG_AV1_DECODER equ 1
+CONFIG_AV1_ENCODER equ 0
+CONFIG_BIG_ENDIAN equ 0
+CONFIG_BITSTREAM_DEBUG equ 0
+CONFIG_COEFFICIENT_RANGE_CHECKING equ 0
+CONFIG_COLLECT_INTER_MODE_RD_STATS equ 1
+CONFIG_COLLECT_RD_STATS equ 0
+CONFIG_DEBUG equ 0
+CONFIG_DENOISE equ 0
+CONFIG_DIST_8X8 equ 0
+CONFIG_ENTROPY_STATS equ 0
+CONFIG_FILEOPTIONS equ 1
+CONFIG_FIX_GF_LENGTH equ 1
+CONFIG_FP_MB_STATS equ 0
+CONFIG_GCC equ 1
+CONFIG_GCOV equ 0
+CONFIG_GPROF equ 0
+CONFIG_INSPECTION equ 0
+CONFIG_INTERNAL_STATS equ 0
+CONFIG_INTER_STATS_ONLY equ 0
+CONFIG_LIBYUV equ 1
+CONFIG_LOWBITDEPTH equ 1
+CONFIG_MAX_DECODE_PROFILE equ 0
+CONFIG_MISMATCH_DEBUG equ 0
+CONFIG_MULTITHREAD equ 1
+CONFIG_NORMAL_TILE_MODE equ 1
+CONFIG_OS_SUPPORT equ 1
+CONFIG_PIC equ 0
+CONFIG_RD_DEBUG equ 0
+CONFIG_REDUCED_ENCODER_BORDER equ 0
+CONFIG_RUNTIME_CPU_DETECT equ 0
+CONFIG_SHARED equ 0
+CONFIG_SIZE_LIMIT equ 1
+CONFIG_SPATIAL_RESAMPLING equ 1
+CONFIG_STATIC equ 1
+CONFIG_WEBM_IO equ 1
+DECODE_HEIGHT_LIMIT equ 16384
+DECODE_WIDTH_LIMIT equ 16384
 HAVE_AVX equ 0
 HAVE_AVX2 equ 0
+HAVE_DSPR2 equ 0
+HAVE_FEXCEPT equ 1
+HAVE_MIPS32 equ 0
+HAVE_MIPS64 equ 0
 HAVE_MMX equ 0
+HAVE_MSA equ 0
+HAVE_NEON equ 1
+HAVE_PTHREAD_H equ 1
 HAVE_SSE equ 0
 HAVE_SSE2 equ 0
 HAVE_SSE3 equ 0
 HAVE_SSE4_1 equ 0
 HAVE_SSE4_2 equ 0
 HAVE_SSSE3 equ 0
-HAVE_FEXCEPT equ 1
-HAVE_PTHREAD_H equ 1
 HAVE_UNISTD_H equ 1
+HAVE_VSX equ 0
 HAVE_WXWIDGETS equ 0
-CONFIG_AV1_DECODER equ 1
-CONFIG_AV1_ENCODER equ 0
-CONFIG_BIG_ENDIAN equ 0
-CONFIG_GCC equ 1
-CONFIG_GCOV equ 0
-CONFIG_GPROF equ 0
-CONFIG_LIBYUV equ 1
-CONFIG_MULTITHREAD equ 1
-CONFIG_OS_SUPPORT equ 1
-CONFIG_PIC equ 0
-CONFIG_RUNTIME_CPU_DETECT equ 0
-CONFIG_SHARED equ 0
-CONFIG_STATIC equ 1
-CONFIG_WEBM_IO equ 1
-CONFIG_BITSTREAM_DEBUG equ 0
-CONFIG_DEBUG equ 0
-CONFIG_MISMATCH_DEBUG equ 0
-CONFIG_ACCOUNTING equ 0
-CONFIG_ANALYZER equ 0
-CONFIG_COEFFICIENT_RANGE_CHECKING equ 0
-CONFIG_DENOISE equ 0
-CONFIG_FILEOPTIONS equ 1
-CONFIG_FIX_GF_LENGTH equ 1
-CONFIG_INSPECTION equ 0
-CONFIG_INTERNAL_STATS equ 0
-CONFIG_LOWBITDEPTH equ 1
-CONFIG_MAX_DECODE_PROFILE equ 0
-CONFIG_NORMAL_TILE_MODE equ 1
-CONFIG_SIZE_LIMIT equ 1
-CONFIG_SPATIAL_RESAMPLING equ 1
-DECODE_HEIGHT_LIMIT equ 16384
-DECODE_WIDTH_LIMIT equ 16384
-CONFIG_COLLECT_INTER_MODE_RD_STATS equ 1
-CONFIG_COLLECT_RD_STATS equ 0
-CONFIG_DIST_8X8 equ 1
-CONFIG_ENTROPY_STATS equ 0
-CONFIG_FP_MB_STATS equ 0
-CONFIG_INTER_STATS_ONLY equ 0
-CONFIG_RD_DEBUG equ 0
diff --git a/third_party/libaom/source/config/linux/arm64/config/aom_config.c b/third_party/libaom/source/config/linux/arm64/config/aom_config.c
index 69a750a..4aa0d2d 100644
--- a/third_party/libaom/source/config/linux/arm64/config/aom_config.c
+++ b/third_party/libaom/source/config/linux/arm64/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/wtc/chromium.2/src/third_party/libaom/source/libaom/build/cmake/toolchains/arm64-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
+static const char* const cfg = "cmake ../source/libaom -G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"../source/libaom/build/cmake/toolchains/arm64-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/linux/arm64/config/aom_config.h b/third_party/libaom/source/config/linux/arm64/config/aom_config.h
index bc574c1..574ad37 100644
--- a/third_party/libaom/source/config/linux/arm64/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/arm64/config/aom_config.h
@@ -10,68 +10,70 @@
  */
 #ifndef AOM_CONFIG_H_
 #define AOM_CONFIG_H_
-#define INLINE inline
 #define ARCH_ARM 1
 #define ARCH_MIPS 0
 #define ARCH_PPC 0
 #define ARCH_X86 0
 #define ARCH_X86_64 0
-#define HAVE_NEON 1
-#define HAVE_DSPR2 0
-#define HAVE_MIPS32 0
-#define HAVE_MIPS64 0
-#define HAVE_MSA 0
-#define HAVE_VSX 0
+#define CONFIG_2PASS_PARTITION_SEARCH_LVL 1
+#define CONFIG_ACCOUNTING 0
+#define CONFIG_ANALYZER 0
+#define CONFIG_AV1_DECODER 1
+#define CONFIG_AV1_ENCODER 0
+#define CONFIG_BIG_ENDIAN 0
+#define CONFIG_BITSTREAM_DEBUG 0
+#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
+#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
+#define CONFIG_COLLECT_RD_STATS 0
+#define CONFIG_DEBUG 0
+#define CONFIG_DENOISE 0
+#define CONFIG_DIST_8X8 0
+#define CONFIG_ENTROPY_STATS 0
+#define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
+#define CONFIG_FP_MB_STATS 0
+#define CONFIG_GCC 1
+#define CONFIG_GCOV 0
+#define CONFIG_GPROF 0
+#define CONFIG_INSPECTION 0
+#define CONFIG_INTERNAL_STATS 0
+#define CONFIG_INTER_STATS_ONLY 0
+#define CONFIG_LIBYUV 1
+#define CONFIG_LOWBITDEPTH 1
+#define CONFIG_MAX_DECODE_PROFILE 0
+#define CONFIG_MISMATCH_DEBUG 0
+#define CONFIG_MULTITHREAD 1
+#define CONFIG_NORMAL_TILE_MODE 1
+#define CONFIG_OS_SUPPORT 1
+#define CONFIG_PIC 0
+#define CONFIG_RD_DEBUG 0
+#define CONFIG_REDUCED_ENCODER_BORDER 0
+#define CONFIG_RUNTIME_CPU_DETECT 0
+#define CONFIG_SHARED 0
+#define CONFIG_SIZE_LIMIT 1
+#define CONFIG_SPATIAL_RESAMPLING 1
+#define CONFIG_STATIC 1
+#define CONFIG_WEBM_IO 1
+#define DECODE_HEIGHT_LIMIT 16384
+#define DECODE_WIDTH_LIMIT 16384
 #define HAVE_AVX 0
 #define HAVE_AVX2 0
+#define HAVE_DSPR2 0
+#define HAVE_FEXCEPT 1
+#define HAVE_MIPS32 0
+#define HAVE_MIPS64 0
 #define HAVE_MMX 0
+#define HAVE_MSA 0
+#define HAVE_NEON 1
+#define HAVE_PTHREAD_H 1
 #define HAVE_SSE 0
 #define HAVE_SSE2 0
 #define HAVE_SSE3 0
 #define HAVE_SSE4_1 0
 #define HAVE_SSE4_2 0
 #define HAVE_SSSE3 0
-#define HAVE_FEXCEPT 1
-#define HAVE_PTHREAD_H 1
 #define HAVE_UNISTD_H 1
+#define HAVE_VSX 0
 #define HAVE_WXWIDGETS 0
-#define CONFIG_AV1_DECODER 1
-#define CONFIG_AV1_ENCODER 0
-#define CONFIG_BIG_ENDIAN 0
-#define CONFIG_GCC 1
-#define CONFIG_GCOV 0
-#define CONFIG_GPROF 0
-#define CONFIG_LIBYUV 1
-#define CONFIG_MULTITHREAD 1
-#define CONFIG_OS_SUPPORT 1
-#define CONFIG_PIC 0
-#define CONFIG_RUNTIME_CPU_DETECT 0
-#define CONFIG_SHARED 0
-#define CONFIG_STATIC 1
-#define CONFIG_WEBM_IO 1
-#define CONFIG_BITSTREAM_DEBUG 0
-#define CONFIG_DEBUG 0
-#define CONFIG_MISMATCH_DEBUG 0
-#define CONFIG_ACCOUNTING 0
-#define CONFIG_ANALYZER 0
-#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
-#define CONFIG_DENOISE 0
-#define CONFIG_FILEOPTIONS 1
-#define CONFIG_FIX_GF_LENGTH 1
-#define CONFIG_INSPECTION 0
-#define CONFIG_INTERNAL_STATS 0
-#define CONFIG_LOWBITDEPTH 1
-#define CONFIG_MAX_DECODE_PROFILE 0
-#define CONFIG_NORMAL_TILE_MODE 1
-#define CONFIG_SIZE_LIMIT 1
-#define CONFIG_SPATIAL_RESAMPLING 1
-#define DECODE_HEIGHT_LIMIT 16384
-#define DECODE_WIDTH_LIMIT 16384
-#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
-#define CONFIG_COLLECT_RD_STATS 0
-#define CONFIG_DIST_8X8 1
-#define CONFIG_ENTROPY_STATS 0
-#define CONFIG_FP_MB_STATS 0
-#define CONFIG_INTER_STATS_ONLY 0
-#define CONFIG_RD_DEBUG 0
-#endif /* AOM_CONFIG_H_ */
+#define INLINE inline
+#endif  // AOM_CONFIG_H_
diff --git a/third_party/libaom/source/config/linux/arm64/config/av1_rtcd.h b/third_party/libaom/source/config/linux/arm64/config/av1_rtcd.h
index e2f99fa0..efe44ff 100644
--- a/third_party/libaom/source/config/linux/arm64/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/linux/arm64/config/av1_rtcd.h
@@ -483,6 +483,36 @@
                                    int bd);
 #define av1_highbd_dr_prediction_z3 av1_highbd_dr_prediction_z3_c
 
+void av1_highbd_inv_txfm_add_c(const tran_low_t* dqcoeff,
+                               uint8_t* dst,
+                               int stride,
+                               const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add av1_highbd_inv_txfm_add_c
+
+void av1_highbd_inv_txfm_add_16x16_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_16x16 av1_highbd_inv_txfm_add_16x16_c
+
+void av1_highbd_inv_txfm_add_32x32_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_32x32 av1_highbd_inv_txfm_add_32x32_c
+
+void av1_highbd_inv_txfm_add_4x4_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_4x4 av1_highbd_inv_txfm_add_4x4_c
+
+void av1_highbd_inv_txfm_add_8x8_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_8x8 av1_highbd_inv_txfm_add_8x8_c
+
 void av1_highbd_iwht4x4_16_add_c(const tran_low_t* input,
                                  uint8_t* dest,
                                  int dest_stride,
@@ -872,7 +902,25 @@
                        int16_t beta,
                        int16_t gamma,
                        int16_t delta);
-#define av1_warp_affine av1_warp_affine_c
+void av1_warp_affine_neon(const int32_t* mat,
+                          const uint8_t* ref,
+                          int width,
+                          int height,
+                          int stride,
+                          uint8_t* pred,
+                          int p_col,
+                          int p_row,
+                          int p_width,
+                          int p_height,
+                          int p_stride,
+                          int subsampling_x,
+                          int subsampling_y,
+                          ConvolveParams* conv_params,
+                          int16_t alpha,
+                          int16_t beta,
+                          int16_t gamma,
+                          int16_t delta);
+#define av1_warp_affine av1_warp_affine_neon
 
 void av1_wiener_convolve_add_src_c(const uint8_t* src,
                                    ptrdiff_t src_stride,
diff --git a/third_party/libaom/source/config/linux/generic/config/aom_config.asm b/third_party/libaom/source/config/linux/generic/config/aom_config.asm
index 6df03e5..3ed61ff5 100644
--- a/third_party/libaom/source/config/linux/generic/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/generic/config/aom_config.asm
@@ -13,61 +13,63 @@
 ARCH_PPC equ 0
 ARCH_X86 equ 0
 ARCH_X86_64 equ 0
-HAVE_NEON equ 0
-HAVE_DSPR2 equ 0
-HAVE_MIPS32 equ 0
-HAVE_MIPS64 equ 0
-HAVE_MSA equ 0
-HAVE_VSX equ 0
+CONFIG_2PASS_PARTITION_SEARCH_LVL equ 1
+CONFIG_ACCOUNTING equ 0
+CONFIG_ANALYZER equ 0
+CONFIG_AV1_DECODER equ 1
+CONFIG_AV1_ENCODER equ 0
+CONFIG_BIG_ENDIAN equ 0
+CONFIG_BITSTREAM_DEBUG equ 0
+CONFIG_COEFFICIENT_RANGE_CHECKING equ 0
+CONFIG_COLLECT_INTER_MODE_RD_STATS equ 1
+CONFIG_COLLECT_RD_STATS equ 0
+CONFIG_DEBUG equ 0
+CONFIG_DENOISE equ 0
+CONFIG_DIST_8X8 equ 0
+CONFIG_ENTROPY_STATS equ 0
+CONFIG_FILEOPTIONS equ 1
+CONFIG_FIX_GF_LENGTH equ 1
+CONFIG_FP_MB_STATS equ 0
+CONFIG_GCC equ 1
+CONFIG_GCOV equ 0
+CONFIG_GPROF equ 0
+CONFIG_INSPECTION equ 0
+CONFIG_INTERNAL_STATS equ 0
+CONFIG_INTER_STATS_ONLY equ 0
+CONFIG_LIBYUV equ 1
+CONFIG_LOWBITDEPTH equ 1
+CONFIG_MAX_DECODE_PROFILE equ 0
+CONFIG_MISMATCH_DEBUG equ 0
+CONFIG_MULTITHREAD equ 1
+CONFIG_NORMAL_TILE_MODE equ 1
+CONFIG_OS_SUPPORT equ 1
+CONFIG_PIC equ 0
+CONFIG_RD_DEBUG equ 0
+CONFIG_REDUCED_ENCODER_BORDER equ 0
+CONFIG_RUNTIME_CPU_DETECT equ 1
+CONFIG_SHARED equ 0
+CONFIG_SIZE_LIMIT equ 1
+CONFIG_SPATIAL_RESAMPLING equ 1
+CONFIG_STATIC equ 1
+CONFIG_WEBM_IO equ 1
+DECODE_HEIGHT_LIMIT equ 16384
+DECODE_WIDTH_LIMIT equ 16384
 HAVE_AVX equ 0
 HAVE_AVX2 equ 0
+HAVE_DSPR2 equ 0
+HAVE_FEXCEPT equ 1
+HAVE_MIPS32 equ 0
+HAVE_MIPS64 equ 0
 HAVE_MMX equ 0
+HAVE_MSA equ 0
+HAVE_NEON equ 0
+HAVE_PTHREAD_H equ 1
 HAVE_SSE equ 0
 HAVE_SSE2 equ 0
 HAVE_SSE3 equ 0
 HAVE_SSE4_1 equ 0
 HAVE_SSE4_2 equ 0
 HAVE_SSSE3 equ 0
-HAVE_FEXCEPT equ 1
-HAVE_PTHREAD_H equ 1
 HAVE_UNISTD_H equ 1
+HAVE_VSX equ 0
 HAVE_WXWIDGETS equ 0
-CONFIG_AV1_DECODER equ 1
-CONFIG_AV1_ENCODER equ 0
-CONFIG_BIG_ENDIAN equ 0
-CONFIG_GCC equ 1
-CONFIG_GCOV equ 0
-CONFIG_GPROF equ 0
-CONFIG_LIBYUV equ 1
-CONFIG_MULTITHREAD equ 1
-CONFIG_OS_SUPPORT equ 1
-CONFIG_PIC equ 0
-CONFIG_RUNTIME_CPU_DETECT equ 1
-CONFIG_SHARED equ 0
-CONFIG_STATIC equ 1
-CONFIG_WEBM_IO equ 1
-CONFIG_BITSTREAM_DEBUG equ 0
-CONFIG_DEBUG equ 0
-CONFIG_MISMATCH_DEBUG equ 0
-CONFIG_ACCOUNTING equ 0
-CONFIG_ANALYZER equ 0
-CONFIG_COEFFICIENT_RANGE_CHECKING equ 0
-CONFIG_DENOISE equ 0
-CONFIG_FILEOPTIONS equ 1
-CONFIG_FIX_GF_LENGTH equ 1
-CONFIG_INSPECTION equ 0
-CONFIG_INTERNAL_STATS equ 0
-CONFIG_LOWBITDEPTH equ 1
-CONFIG_MAX_DECODE_PROFILE equ 0
-CONFIG_NORMAL_TILE_MODE equ 1
-CONFIG_SIZE_LIMIT equ 1
-CONFIG_SPATIAL_RESAMPLING equ 1
-DECODE_HEIGHT_LIMIT equ 16384
-DECODE_WIDTH_LIMIT equ 16384
-CONFIG_COLLECT_INTER_MODE_RD_STATS equ 1
-CONFIG_COLLECT_RD_STATS equ 0
-CONFIG_DIST_8X8 equ 1
-CONFIG_ENTROPY_STATS equ 0
-CONFIG_FP_MB_STATS equ 0
-CONFIG_INTER_STATS_ONLY equ 0
-CONFIG_RD_DEBUG equ 0
diff --git a/third_party/libaom/source/config/linux/generic/config/aom_config.c b/third_party/libaom/source/config/linux/generic/config/aom_config.c
index 66acfe1..39a8d79 100644
--- a/third_party/libaom/source/config/linux/generic/config/aom_config.c
+++ b/third_party/libaom/source/config/linux/generic/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DAOM_TARGET_CPU=generic -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
+static const char* const cfg = "cmake ../source/libaom -G \"Unix Makefiles\" -DAOM_TARGET_CPU=generic -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/linux/generic/config/aom_config.h b/third_party/libaom/source/config/linux/generic/config/aom_config.h
index 04dc556..aba9580 100644
--- a/third_party/libaom/source/config/linux/generic/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/generic/config/aom_config.h
@@ -10,68 +10,70 @@
  */
 #ifndef AOM_CONFIG_H_
 #define AOM_CONFIG_H_
-#define INLINE inline
 #define ARCH_ARM 0
 #define ARCH_MIPS 0
 #define ARCH_PPC 0
 #define ARCH_X86 0
 #define ARCH_X86_64 0
-#define HAVE_NEON 0
-#define HAVE_DSPR2 0
-#define HAVE_MIPS32 0
-#define HAVE_MIPS64 0
-#define HAVE_MSA 0
-#define HAVE_VSX 0
+#define CONFIG_2PASS_PARTITION_SEARCH_LVL 1
+#define CONFIG_ACCOUNTING 0
+#define CONFIG_ANALYZER 0
+#define CONFIG_AV1_DECODER 1
+#define CONFIG_AV1_ENCODER 0
+#define CONFIG_BIG_ENDIAN 0
+#define CONFIG_BITSTREAM_DEBUG 0
+#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
+#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
+#define CONFIG_COLLECT_RD_STATS 0
+#define CONFIG_DEBUG 0
+#define CONFIG_DENOISE 0
+#define CONFIG_DIST_8X8 0
+#define CONFIG_ENTROPY_STATS 0
+#define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
+#define CONFIG_FP_MB_STATS 0
+#define CONFIG_GCC 1
+#define CONFIG_GCOV 0
+#define CONFIG_GPROF 0
+#define CONFIG_INSPECTION 0
+#define CONFIG_INTERNAL_STATS 0
+#define CONFIG_INTER_STATS_ONLY 0
+#define CONFIG_LIBYUV 1
+#define CONFIG_LOWBITDEPTH 1
+#define CONFIG_MAX_DECODE_PROFILE 0
+#define CONFIG_MISMATCH_DEBUG 0
+#define CONFIG_MULTITHREAD 1
+#define CONFIG_NORMAL_TILE_MODE 1
+#define CONFIG_OS_SUPPORT 1
+#define CONFIG_PIC 0
+#define CONFIG_RD_DEBUG 0
+#define CONFIG_REDUCED_ENCODER_BORDER 0
+#define CONFIG_RUNTIME_CPU_DETECT 1
+#define CONFIG_SHARED 0
+#define CONFIG_SIZE_LIMIT 1
+#define CONFIG_SPATIAL_RESAMPLING 1
+#define CONFIG_STATIC 1
+#define CONFIG_WEBM_IO 1
+#define DECODE_HEIGHT_LIMIT 16384
+#define DECODE_WIDTH_LIMIT 16384
 #define HAVE_AVX 0
 #define HAVE_AVX2 0
+#define HAVE_DSPR2 0
+#define HAVE_FEXCEPT 1
+#define HAVE_MIPS32 0
+#define HAVE_MIPS64 0
 #define HAVE_MMX 0
+#define HAVE_MSA 0
+#define HAVE_NEON 0
+#define HAVE_PTHREAD_H 1
 #define HAVE_SSE 0
 #define HAVE_SSE2 0
 #define HAVE_SSE3 0
 #define HAVE_SSE4_1 0
 #define HAVE_SSE4_2 0
 #define HAVE_SSSE3 0
-#define HAVE_FEXCEPT 1
-#define HAVE_PTHREAD_H 1
 #define HAVE_UNISTD_H 1
+#define HAVE_VSX 0
 #define HAVE_WXWIDGETS 0
-#define CONFIG_AV1_DECODER 1
-#define CONFIG_AV1_ENCODER 0
-#define CONFIG_BIG_ENDIAN 0
-#define CONFIG_GCC 1
-#define CONFIG_GCOV 0
-#define CONFIG_GPROF 0
-#define CONFIG_LIBYUV 1
-#define CONFIG_MULTITHREAD 1
-#define CONFIG_OS_SUPPORT 1
-#define CONFIG_PIC 0
-#define CONFIG_RUNTIME_CPU_DETECT 1
-#define CONFIG_SHARED 0
-#define CONFIG_STATIC 1
-#define CONFIG_WEBM_IO 1
-#define CONFIG_BITSTREAM_DEBUG 0
-#define CONFIG_DEBUG 0
-#define CONFIG_MISMATCH_DEBUG 0
-#define CONFIG_ACCOUNTING 0
-#define CONFIG_ANALYZER 0
-#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
-#define CONFIG_DENOISE 0
-#define CONFIG_FILEOPTIONS 1
-#define CONFIG_FIX_GF_LENGTH 1
-#define CONFIG_INSPECTION 0
-#define CONFIG_INTERNAL_STATS 0
-#define CONFIG_LOWBITDEPTH 1
-#define CONFIG_MAX_DECODE_PROFILE 0
-#define CONFIG_NORMAL_TILE_MODE 1
-#define CONFIG_SIZE_LIMIT 1
-#define CONFIG_SPATIAL_RESAMPLING 1
-#define DECODE_HEIGHT_LIMIT 16384
-#define DECODE_WIDTH_LIMIT 16384
-#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
-#define CONFIG_COLLECT_RD_STATS 0
-#define CONFIG_DIST_8X8 1
-#define CONFIG_ENTROPY_STATS 0
-#define CONFIG_FP_MB_STATS 0
-#define CONFIG_INTER_STATS_ONLY 0
-#define CONFIG_RD_DEBUG 0
-#endif /* AOM_CONFIG_H_ */
+#define INLINE inline
+#endif  // AOM_CONFIG_H_
diff --git a/third_party/libaom/source/config/linux/generic/config/av1_rtcd.h b/third_party/libaom/source/config/linux/generic/config/av1_rtcd.h
index 9bd2ed9..ade634d 100644
--- a/third_party/libaom/source/config/linux/generic/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/linux/generic/config/av1_rtcd.h
@@ -418,6 +418,36 @@
                                    int bd);
 #define av1_highbd_dr_prediction_z3 av1_highbd_dr_prediction_z3_c
 
+void av1_highbd_inv_txfm_add_c(const tran_low_t* dqcoeff,
+                               uint8_t* dst,
+                               int stride,
+                               const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add av1_highbd_inv_txfm_add_c
+
+void av1_highbd_inv_txfm_add_16x16_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_16x16 av1_highbd_inv_txfm_add_16x16_c
+
+void av1_highbd_inv_txfm_add_32x32_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_32x32 av1_highbd_inv_txfm_add_32x32_c
+
+void av1_highbd_inv_txfm_add_4x4_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_4x4 av1_highbd_inv_txfm_add_4x4_c
+
+void av1_highbd_inv_txfm_add_8x8_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+#define av1_highbd_inv_txfm_add_8x8 av1_highbd_inv_txfm_add_8x8_c
+
 void av1_highbd_iwht4x4_16_add_c(const tran_low_t* input,
                                  uint8_t* dest,
                                  int dest_stride,
diff --git a/third_party/libaom/source/config/linux/ia32/config/aom_config.asm b/third_party/libaom/source/config/linux/ia32/config/aom_config.asm
index 5d7d22d..b55339a 100644
--- a/third_party/libaom/source/config/linux/ia32/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/ia32/config/aom_config.asm
@@ -3,61 +3,63 @@
 %define ARCH_PPC 0
 %define ARCH_X86 1
 %define ARCH_X86_64 0
-%define HAVE_NEON 0
-%define HAVE_DSPR2 0
-%define HAVE_MIPS32 0
-%define HAVE_MIPS64 0
-%define HAVE_MSA 0
-%define HAVE_VSX 0
+%define CONFIG_2PASS_PARTITION_SEARCH_LVL 1
+%define CONFIG_ACCOUNTING 0
+%define CONFIG_ANALYZER 0
+%define CONFIG_AV1_DECODER 1
+%define CONFIG_AV1_ENCODER 0
+%define CONFIG_BIG_ENDIAN 0
+%define CONFIG_BITSTREAM_DEBUG 0
+%define CONFIG_COEFFICIENT_RANGE_CHECKING 0
+%define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
+%define CONFIG_COLLECT_RD_STATS 0
+%define CONFIG_DEBUG 0
+%define CONFIG_DENOISE 0
+%define CONFIG_DIST_8X8 0
+%define CONFIG_ENTROPY_STATS 0
+%define CONFIG_FILEOPTIONS 1
+%define CONFIG_FIX_GF_LENGTH 1
+%define CONFIG_FP_MB_STATS 0
+%define CONFIG_GCC 1
+%define CONFIG_GCOV 0
+%define CONFIG_GPROF 0
+%define CONFIG_INSPECTION 0
+%define CONFIG_INTERNAL_STATS 0
+%define CONFIG_INTER_STATS_ONLY 0
+%define CONFIG_LIBYUV 1
+%define CONFIG_LOWBITDEPTH 1
+%define CONFIG_MAX_DECODE_PROFILE 0
+%define CONFIG_MISMATCH_DEBUG 0
+%define CONFIG_MULTITHREAD 1
+%define CONFIG_NORMAL_TILE_MODE 1
+%define CONFIG_OS_SUPPORT 1
+%define CONFIG_PIC 0
+%define CONFIG_RD_DEBUG 0
+%define CONFIG_REDUCED_ENCODER_BORDER 0
+%define CONFIG_RUNTIME_CPU_DETECT 1
+%define CONFIG_SHARED 0
+%define CONFIG_SIZE_LIMIT 1
+%define CONFIG_SPATIAL_RESAMPLING 1
+%define CONFIG_STATIC 1
+%define CONFIG_WEBM_IO 1
+%define DECODE_HEIGHT_LIMIT 16384
+%define DECODE_WIDTH_LIMIT 16384
 %define HAVE_AVX 1
 %define HAVE_AVX2 1
+%define HAVE_DSPR2 0
+%define HAVE_FEXCEPT 1
+%define HAVE_MIPS32 0
+%define HAVE_MIPS64 0
 %define HAVE_MMX 1
+%define HAVE_MSA 0
+%define HAVE_NEON 0
+%define HAVE_PTHREAD_H 1
 %define HAVE_SSE 1
 %define HAVE_SSE2 1
 %define HAVE_SSE3 1
 %define HAVE_SSE4_1 1
 %define HAVE_SSE4_2 1
 %define HAVE_SSSE3 1
-%define HAVE_FEXCEPT 1
-%define HAVE_PTHREAD_H 1
 %define HAVE_UNISTD_H 1
+%define HAVE_VSX 0
 %define HAVE_WXWIDGETS 0
-%define CONFIG_AV1_DECODER 1
-%define CONFIG_AV1_ENCODER 0
-%define CONFIG_BIG_ENDIAN 0
-%define CONFIG_GCC 1
-%define CONFIG_GCOV 0
-%define CONFIG_GPROF 0
-%define CONFIG_LIBYUV 1
-%define CONFIG_MULTITHREAD 1
-%define CONFIG_OS_SUPPORT 1
-%define CONFIG_PIC 0
-%define CONFIG_RUNTIME_CPU_DETECT 1
-%define CONFIG_SHARED 0
-%define CONFIG_STATIC 1
-%define CONFIG_WEBM_IO 1
-%define CONFIG_BITSTREAM_DEBUG 0
-%define CONFIG_DEBUG 0
-%define CONFIG_MISMATCH_DEBUG 0
-%define CONFIG_ACCOUNTING 0
-%define CONFIG_ANALYZER 0
-%define CONFIG_COEFFICIENT_RANGE_CHECKING 0
-%define CONFIG_DENOISE 0
-%define CONFIG_FILEOPTIONS 1
-%define CONFIG_FIX_GF_LENGTH 1
-%define CONFIG_INSPECTION 0
-%define CONFIG_INTERNAL_STATS 0
-%define CONFIG_LOWBITDEPTH 1
-%define CONFIG_MAX_DECODE_PROFILE 0
-%define CONFIG_NORMAL_TILE_MODE 1
-%define CONFIG_SIZE_LIMIT 1
-%define CONFIG_SPATIAL_RESAMPLING 1
-%define DECODE_HEIGHT_LIMIT 16384
-%define DECODE_WIDTH_LIMIT 16384
-%define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
-%define CONFIG_COLLECT_RD_STATS 0
-%define CONFIG_DIST_8X8 1
-%define CONFIG_ENTROPY_STATS 0
-%define CONFIG_FP_MB_STATS 0
-%define CONFIG_INTER_STATS_ONLY 0
-%define CONFIG_RD_DEBUG 0
diff --git a/third_party/libaom/source/config/linux/ia32/config/aom_config.c b/third_party/libaom/source/config/linux/ia32/config/aom_config.c
index ab01a869..b293f45 100644
--- a/third_party/libaom/source/config/linux/ia32/config/aom_config.c
+++ b/third_party/libaom/source/config/linux/ia32/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/wtc/chromium.2/src/third_party/libaom/source/libaom/build/cmake/toolchains/x86-linux.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
+static const char* const cfg = "cmake ../source/libaom -G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"../source/libaom/build/cmake/toolchains/x86-linux.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/linux/ia32/config/aom_config.h b/third_party/libaom/source/config/linux/ia32/config/aom_config.h
index c06c694..4b4f921 100644
--- a/third_party/libaom/source/config/linux/ia32/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/ia32/config/aom_config.h
@@ -10,68 +10,70 @@
  */
 #ifndef AOM_CONFIG_H_
 #define AOM_CONFIG_H_
-#define INLINE inline
 #define ARCH_ARM 0
 #define ARCH_MIPS 0
 #define ARCH_PPC 0
 #define ARCH_X86 1
 #define ARCH_X86_64 0
-#define HAVE_NEON 0
-#define HAVE_DSPR2 0
-#define HAVE_MIPS32 0
-#define HAVE_MIPS64 0
-#define HAVE_MSA 0
-#define HAVE_VSX 0
+#define CONFIG_2PASS_PARTITION_SEARCH_LVL 1
+#define CONFIG_ACCOUNTING 0
+#define CONFIG_ANALYZER 0
+#define CONFIG_AV1_DECODER 1
+#define CONFIG_AV1_ENCODER 0
+#define CONFIG_BIG_ENDIAN 0
+#define CONFIG_BITSTREAM_DEBUG 0
+#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
+#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
+#define CONFIG_COLLECT_RD_STATS 0
+#define CONFIG_DEBUG 0
+#define CONFIG_DENOISE 0
+#define CONFIG_DIST_8X8 0
+#define CONFIG_ENTROPY_STATS 0
+#define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
+#define CONFIG_FP_MB_STATS 0
+#define CONFIG_GCC 1
+#define CONFIG_GCOV 0
+#define CONFIG_GPROF 0
+#define CONFIG_INSPECTION 0
+#define CONFIG_INTERNAL_STATS 0
+#define CONFIG_INTER_STATS_ONLY 0
+#define CONFIG_LIBYUV 1
+#define CONFIG_LOWBITDEPTH 1
+#define CONFIG_MAX_DECODE_PROFILE 0
+#define CONFIG_MISMATCH_DEBUG 0
+#define CONFIG_MULTITHREAD 1
+#define CONFIG_NORMAL_TILE_MODE 1
+#define CONFIG_OS_SUPPORT 1
+#define CONFIG_PIC 0
+#define CONFIG_RD_DEBUG 0
+#define CONFIG_REDUCED_ENCODER_BORDER 0
+#define CONFIG_RUNTIME_CPU_DETECT 1
+#define CONFIG_SHARED 0
+#define CONFIG_SIZE_LIMIT 1
+#define CONFIG_SPATIAL_RESAMPLING 1
+#define CONFIG_STATIC 1
+#define CONFIG_WEBM_IO 1
+#define DECODE_HEIGHT_LIMIT 16384
+#define DECODE_WIDTH_LIMIT 16384
 #define HAVE_AVX 1
 #define HAVE_AVX2 1
+#define HAVE_DSPR2 0
+#define HAVE_FEXCEPT 1
+#define HAVE_MIPS32 0
+#define HAVE_MIPS64 0
 #define HAVE_MMX 1
+#define HAVE_MSA 0
+#define HAVE_NEON 0
+#define HAVE_PTHREAD_H 1
 #define HAVE_SSE 1
 #define HAVE_SSE2 1
 #define HAVE_SSE3 1
 #define HAVE_SSE4_1 1
 #define HAVE_SSE4_2 1
 #define HAVE_SSSE3 1
-#define HAVE_FEXCEPT 1
-#define HAVE_PTHREAD_H 1
 #define HAVE_UNISTD_H 1
+#define HAVE_VSX 0
 #define HAVE_WXWIDGETS 0
-#define CONFIG_AV1_DECODER 1
-#define CONFIG_AV1_ENCODER 0
-#define CONFIG_BIG_ENDIAN 0
-#define CONFIG_GCC 1
-#define CONFIG_GCOV 0
-#define CONFIG_GPROF 0
-#define CONFIG_LIBYUV 1
-#define CONFIG_MULTITHREAD 1
-#define CONFIG_OS_SUPPORT 1
-#define CONFIG_PIC 0
-#define CONFIG_RUNTIME_CPU_DETECT 1
-#define CONFIG_SHARED 0
-#define CONFIG_STATIC 1
-#define CONFIG_WEBM_IO 1
-#define CONFIG_BITSTREAM_DEBUG 0
-#define CONFIG_DEBUG 0
-#define CONFIG_MISMATCH_DEBUG 0
-#define CONFIG_ACCOUNTING 0
-#define CONFIG_ANALYZER 0
-#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
-#define CONFIG_DENOISE 0
-#define CONFIG_FILEOPTIONS 1
-#define CONFIG_FIX_GF_LENGTH 1
-#define CONFIG_INSPECTION 0
-#define CONFIG_INTERNAL_STATS 0
-#define CONFIG_LOWBITDEPTH 1
-#define CONFIG_MAX_DECODE_PROFILE 0
-#define CONFIG_NORMAL_TILE_MODE 1
-#define CONFIG_SIZE_LIMIT 1
-#define CONFIG_SPATIAL_RESAMPLING 1
-#define DECODE_HEIGHT_LIMIT 16384
-#define DECODE_WIDTH_LIMIT 16384
-#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
-#define CONFIG_COLLECT_RD_STATS 0
-#define CONFIG_DIST_8X8 1
-#define CONFIG_ENTROPY_STATS 0
-#define CONFIG_FP_MB_STATS 0
-#define CONFIG_INTER_STATS_ONLY 0
-#define CONFIG_RD_DEBUG 0
-#endif /* AOM_CONFIG_H_ */
+#define INLINE inline
+#endif  // AOM_CONFIG_H_
diff --git a/third_party/libaom/source/config/linux/ia32/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/linux/ia32/config/aom_dsp_rtcd.h
index 76fdd866..74280b0 100644
--- a/third_party/libaom/source/config/linux/ia32/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/linux/ia32/config/aom_dsp_rtcd.h
@@ -73,6 +73,18 @@
                                int h,
                                int subx,
                                int suby);
+void aom_blend_a64_mask_avx2(uint8_t* dst,
+                             uint32_t dst_stride,
+                             const uint8_t* src0,
+                             uint32_t src0_stride,
+                             const uint8_t* src1,
+                             uint32_t src1_stride,
+                             const uint8_t* mask,
+                             uint32_t mask_stride,
+                             int w,
+                             int h,
+                             int subx,
+                             int suby);
 RTCD_EXTERN void (*aom_blend_a64_mask)(uint8_t* dst,
                                        uint32_t dst_stride,
                                        const uint8_t* src0,
@@ -5960,6 +5972,8 @@
   aom_blend_a64_mask = aom_blend_a64_mask_c;
   if (flags & HAS_SSE4_1)
     aom_blend_a64_mask = aom_blend_a64_mask_sse4_1;
+  if (flags & HAS_AVX2)
+    aom_blend_a64_mask = aom_blend_a64_mask_avx2;
   aom_blend_a64_vmask = aom_blend_a64_vmask_c;
   if (flags & HAS_SSE4_1)
     aom_blend_a64_vmask = aom_blend_a64_vmask_sse4_1;
diff --git a/third_party/libaom/source/config/linux/ia32/config/av1_rtcd.h b/third_party/libaom/source/config/linux/ia32/config/av1_rtcd.h
index d3f9eac..8bc8829 100644
--- a/third_party/libaom/source/config/linux/ia32/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/linux/ia32/config/av1_rtcd.h
@@ -892,6 +892,75 @@
                                    int bd);
 #define av1_highbd_dr_prediction_z3 av1_highbd_dr_prediction_z3_c
 
+void av1_highbd_inv_txfm_add_c(const tran_low_t* dqcoeff,
+                               uint8_t* dst,
+                               int stride,
+                               const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_sse4_1(const tran_low_t* dqcoeff,
+                                    uint8_t* dst,
+                                    int stride,
+                                    const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_avx2(const tran_low_t* dqcoeff,
+                                  uint8_t* dst,
+                                  int stride,
+                                  const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add)(const tran_low_t* dqcoeff,
+                                            uint8_t* dst,
+                                            int stride,
+                                            const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_16x16_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_16x16_sse4_1(const tran_low_t* dqcoeff,
+                                          uint8_t* dst,
+                                          int stride,
+                                          const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_16x16)(const tran_low_t* dqcoeff,
+                                                  uint8_t* dst,
+                                                  int stride,
+                                                  const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_32x32_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_32x32_avx2(const tran_low_t* dqcoeff,
+                                        uint8_t* dst,
+                                        int stride,
+                                        const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_32x32)(const tran_low_t* dqcoeff,
+                                                  uint8_t* dst,
+                                                  int stride,
+                                                  const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_4x4_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_4x4_sse4_1(const tran_low_t* dqcoeff,
+                                        uint8_t* dst,
+                                        int stride,
+                                        const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_4x4)(const tran_low_t* dqcoeff,
+                                                uint8_t* dst,
+                                                int stride,
+                                                const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_8x8_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_8x8_sse4_1(const tran_low_t* dqcoeff,
+                                        uint8_t* dst,
+                                        int stride,
+                                        const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_8x8)(const tran_low_t* dqcoeff,
+                                                uint8_t* dst,
+                                                int stride,
+                                                const TxfmParam* txfm_param);
+
 void av1_highbd_iwht4x4_16_add_c(const tran_low_t* input,
                                  uint8_t* dest,
                                  int dest_stride,
@@ -1346,16 +1415,7 @@
                                 int stride,
                                 TX_TYPE tx_type,
                                 int bd);
-void av1_inv_txfm2d_add_64x64_sse4_1(const int32_t* input,
-                                     uint16_t* output,
-                                     int stride,
-                                     TX_TYPE tx_type,
-                                     int bd);
-RTCD_EXTERN void (*av1_inv_txfm2d_add_64x64)(const int32_t* input,
-                                             uint16_t* output,
-                                             int stride,
-                                             TX_TYPE tx_type,
-                                             int bd);
+#define av1_inv_txfm2d_add_64x64 av1_inv_txfm2d_add_64x64_c
 
 void av1_inv_txfm2d_add_8x16_c(const int32_t* input,
                                uint16_t* output,
@@ -2058,6 +2118,23 @@
     av1_highbd_convolve_y_sr = av1_highbd_convolve_y_sr_ssse3;
   if (flags & HAS_AVX2)
     av1_highbd_convolve_y_sr = av1_highbd_convolve_y_sr_avx2;
+  av1_highbd_inv_txfm_add = av1_highbd_inv_txfm_add_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add = av1_highbd_inv_txfm_add_sse4_1;
+  if (flags & HAS_AVX2)
+    av1_highbd_inv_txfm_add = av1_highbd_inv_txfm_add_avx2;
+  av1_highbd_inv_txfm_add_16x16 = av1_highbd_inv_txfm_add_16x16_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add_16x16 = av1_highbd_inv_txfm_add_16x16_sse4_1;
+  av1_highbd_inv_txfm_add_32x32 = av1_highbd_inv_txfm_add_32x32_c;
+  if (flags & HAS_AVX2)
+    av1_highbd_inv_txfm_add_32x32 = av1_highbd_inv_txfm_add_32x32_avx2;
+  av1_highbd_inv_txfm_add_4x4 = av1_highbd_inv_txfm_add_4x4_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add_4x4 = av1_highbd_inv_txfm_add_4x4_sse4_1;
+  av1_highbd_inv_txfm_add_8x8 = av1_highbd_inv_txfm_add_8x8_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add_8x8 = av1_highbd_inv_txfm_add_8x8_sse4_1;
   av1_highbd_jnt_convolve_2d = av1_highbd_jnt_convolve_2d_c;
   if (flags & HAS_SSE4_1)
     av1_highbd_jnt_convolve_2d = av1_highbd_jnt_convolve_2d_sse4_1;
@@ -2097,9 +2174,6 @@
   av1_inv_txfm2d_add_4x4 = av1_inv_txfm2d_add_4x4_c;
   if (flags & HAS_SSE4_1)
     av1_inv_txfm2d_add_4x4 = av1_inv_txfm2d_add_4x4_sse4_1;
-  av1_inv_txfm2d_add_64x64 = av1_inv_txfm2d_add_64x64_c;
-  if (flags & HAS_SSE4_1)
-    av1_inv_txfm2d_add_64x64 = av1_inv_txfm2d_add_64x64_sse4_1;
   av1_inv_txfm2d_add_8x8 = av1_inv_txfm2d_add_8x8_c;
   if (flags & HAS_SSE4_1)
     av1_inv_txfm2d_add_8x8 = av1_inv_txfm2d_add_8x8_sse4_1;
diff --git a/third_party/libaom/source/config/linux/x64/config/aom_config.asm b/third_party/libaom/source/config/linux/x64/config/aom_config.asm
index 2ce1e7e..3e4b170 100644
--- a/third_party/libaom/source/config/linux/x64/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/x64/config/aom_config.asm
@@ -3,61 +3,63 @@
 %define ARCH_PPC 0
 %define ARCH_X86 0
 %define ARCH_X86_64 1
-%define HAVE_NEON 0
-%define HAVE_DSPR2 0
-%define HAVE_MIPS32 0
-%define HAVE_MIPS64 0
-%define HAVE_MSA 0
-%define HAVE_VSX 0
+%define CONFIG_2PASS_PARTITION_SEARCH_LVL 1
+%define CONFIG_ACCOUNTING 0
+%define CONFIG_ANALYZER 0
+%define CONFIG_AV1_DECODER 1
+%define CONFIG_AV1_ENCODER 0
+%define CONFIG_BIG_ENDIAN 0
+%define CONFIG_BITSTREAM_DEBUG 0
+%define CONFIG_COEFFICIENT_RANGE_CHECKING 0
+%define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
+%define CONFIG_COLLECT_RD_STATS 0
+%define CONFIG_DEBUG 0
+%define CONFIG_DENOISE 0
+%define CONFIG_DIST_8X8 0
+%define CONFIG_ENTROPY_STATS 0
+%define CONFIG_FILEOPTIONS 1
+%define CONFIG_FIX_GF_LENGTH 1
+%define CONFIG_FP_MB_STATS 0
+%define CONFIG_GCC 1
+%define CONFIG_GCOV 0
+%define CONFIG_GPROF 0
+%define CONFIG_INSPECTION 0
+%define CONFIG_INTERNAL_STATS 0
+%define CONFIG_INTER_STATS_ONLY 0
+%define CONFIG_LIBYUV 1
+%define CONFIG_LOWBITDEPTH 1
+%define CONFIG_MAX_DECODE_PROFILE 0
+%define CONFIG_MISMATCH_DEBUG 0
+%define CONFIG_MULTITHREAD 1
+%define CONFIG_NORMAL_TILE_MODE 1
+%define CONFIG_OS_SUPPORT 1
+%define CONFIG_PIC 0
+%define CONFIG_RD_DEBUG 0
+%define CONFIG_REDUCED_ENCODER_BORDER 0
+%define CONFIG_RUNTIME_CPU_DETECT 1
+%define CONFIG_SHARED 0
+%define CONFIG_SIZE_LIMIT 1
+%define CONFIG_SPATIAL_RESAMPLING 1
+%define CONFIG_STATIC 1
+%define CONFIG_WEBM_IO 1
+%define DECODE_HEIGHT_LIMIT 16384
+%define DECODE_WIDTH_LIMIT 16384
 %define HAVE_AVX 1
 %define HAVE_AVX2 1
+%define HAVE_DSPR2 0
+%define HAVE_FEXCEPT 1
+%define HAVE_MIPS32 0
+%define HAVE_MIPS64 0
 %define HAVE_MMX 1
+%define HAVE_MSA 0
+%define HAVE_NEON 0
+%define HAVE_PTHREAD_H 1
 %define HAVE_SSE 1
 %define HAVE_SSE2 1
 %define HAVE_SSE3 1
 %define HAVE_SSE4_1 1
 %define HAVE_SSE4_2 1
 %define HAVE_SSSE3 1
-%define HAVE_FEXCEPT 1
-%define HAVE_PTHREAD_H 1
 %define HAVE_UNISTD_H 1
+%define HAVE_VSX 0
 %define HAVE_WXWIDGETS 0
-%define CONFIG_AV1_DECODER 1
-%define CONFIG_AV1_ENCODER 0
-%define CONFIG_BIG_ENDIAN 0
-%define CONFIG_GCC 1
-%define CONFIG_GCOV 0
-%define CONFIG_GPROF 0
-%define CONFIG_LIBYUV 1
-%define CONFIG_MULTITHREAD 1
-%define CONFIG_OS_SUPPORT 1
-%define CONFIG_PIC 0
-%define CONFIG_RUNTIME_CPU_DETECT 1
-%define CONFIG_SHARED 0
-%define CONFIG_STATIC 1
-%define CONFIG_WEBM_IO 1
-%define CONFIG_BITSTREAM_DEBUG 0
-%define CONFIG_DEBUG 0
-%define CONFIG_MISMATCH_DEBUG 0
-%define CONFIG_ACCOUNTING 0
-%define CONFIG_ANALYZER 0
-%define CONFIG_COEFFICIENT_RANGE_CHECKING 0
-%define CONFIG_DENOISE 0
-%define CONFIG_FILEOPTIONS 1
-%define CONFIG_FIX_GF_LENGTH 1
-%define CONFIG_INSPECTION 0
-%define CONFIG_INTERNAL_STATS 0
-%define CONFIG_LOWBITDEPTH 1
-%define CONFIG_MAX_DECODE_PROFILE 0
-%define CONFIG_NORMAL_TILE_MODE 1
-%define CONFIG_SIZE_LIMIT 1
-%define CONFIG_SPATIAL_RESAMPLING 1
-%define DECODE_HEIGHT_LIMIT 16384
-%define DECODE_WIDTH_LIMIT 16384
-%define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
-%define CONFIG_COLLECT_RD_STATS 0
-%define CONFIG_DIST_8X8 1
-%define CONFIG_ENTROPY_STATS 0
-%define CONFIG_FP_MB_STATS 0
-%define CONFIG_INTER_STATS_ONLY 0
-%define CONFIG_RD_DEBUG 0
diff --git a/third_party/libaom/source/config/linux/x64/config/aom_config.c b/third_party/libaom/source/config/linux/x64/config/aom_config.c
index ff327eb..a31187a 100644
--- a/third_party/libaom/source/config/linux/x64/config/aom_config.c
+++ b/third_party/libaom/source/config/linux/x64/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DAOM_TARGET_CPU=x86_64 -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
+static const char* const cfg = "cmake ../source/libaom -G \"Unix Makefiles\" -DAOM_TARGET_CPU=x86_64 -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/linux/x64/config/aom_config.h b/third_party/libaom/source/config/linux/x64/config/aom_config.h
index 885f89f..ea7a962 100644
--- a/third_party/libaom/source/config/linux/x64/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/x64/config/aom_config.h
@@ -10,68 +10,70 @@
  */
 #ifndef AOM_CONFIG_H_
 #define AOM_CONFIG_H_
-#define INLINE inline
 #define ARCH_ARM 0
 #define ARCH_MIPS 0
 #define ARCH_PPC 0
 #define ARCH_X86 0
 #define ARCH_X86_64 1
-#define HAVE_NEON 0
-#define HAVE_DSPR2 0
-#define HAVE_MIPS32 0
-#define HAVE_MIPS64 0
-#define HAVE_MSA 0
-#define HAVE_VSX 0
+#define CONFIG_2PASS_PARTITION_SEARCH_LVL 1
+#define CONFIG_ACCOUNTING 0
+#define CONFIG_ANALYZER 0
+#define CONFIG_AV1_DECODER 1
+#define CONFIG_AV1_ENCODER 0
+#define CONFIG_BIG_ENDIAN 0
+#define CONFIG_BITSTREAM_DEBUG 0
+#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
+#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
+#define CONFIG_COLLECT_RD_STATS 0
+#define CONFIG_DEBUG 0
+#define CONFIG_DENOISE 0
+#define CONFIG_DIST_8X8 0
+#define CONFIG_ENTROPY_STATS 0
+#define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
+#define CONFIG_FP_MB_STATS 0
+#define CONFIG_GCC 1
+#define CONFIG_GCOV 0
+#define CONFIG_GPROF 0
+#define CONFIG_INSPECTION 0
+#define CONFIG_INTERNAL_STATS 0
+#define CONFIG_INTER_STATS_ONLY 0
+#define CONFIG_LIBYUV 1
+#define CONFIG_LOWBITDEPTH 1
+#define CONFIG_MAX_DECODE_PROFILE 0
+#define CONFIG_MISMATCH_DEBUG 0
+#define CONFIG_MULTITHREAD 1
+#define CONFIG_NORMAL_TILE_MODE 1
+#define CONFIG_OS_SUPPORT 1
+#define CONFIG_PIC 0
+#define CONFIG_RD_DEBUG 0
+#define CONFIG_REDUCED_ENCODER_BORDER 0
+#define CONFIG_RUNTIME_CPU_DETECT 1
+#define CONFIG_SHARED 0
+#define CONFIG_SIZE_LIMIT 1
+#define CONFIG_SPATIAL_RESAMPLING 1
+#define CONFIG_STATIC 1
+#define CONFIG_WEBM_IO 1
+#define DECODE_HEIGHT_LIMIT 16384
+#define DECODE_WIDTH_LIMIT 16384
 #define HAVE_AVX 1
 #define HAVE_AVX2 1
+#define HAVE_DSPR2 0
+#define HAVE_FEXCEPT 1
+#define HAVE_MIPS32 0
+#define HAVE_MIPS64 0
 #define HAVE_MMX 1
+#define HAVE_MSA 0
+#define HAVE_NEON 0
+#define HAVE_PTHREAD_H 1
 #define HAVE_SSE 1
 #define HAVE_SSE2 1
 #define HAVE_SSE3 1
 #define HAVE_SSE4_1 1
 #define HAVE_SSE4_2 1
 #define HAVE_SSSE3 1
-#define HAVE_FEXCEPT 1
-#define HAVE_PTHREAD_H 1
 #define HAVE_UNISTD_H 1
+#define HAVE_VSX 0
 #define HAVE_WXWIDGETS 0
-#define CONFIG_AV1_DECODER 1
-#define CONFIG_AV1_ENCODER 0
-#define CONFIG_BIG_ENDIAN 0
-#define CONFIG_GCC 1
-#define CONFIG_GCOV 0
-#define CONFIG_GPROF 0
-#define CONFIG_LIBYUV 1
-#define CONFIG_MULTITHREAD 1
-#define CONFIG_OS_SUPPORT 1
-#define CONFIG_PIC 0
-#define CONFIG_RUNTIME_CPU_DETECT 1
-#define CONFIG_SHARED 0
-#define CONFIG_STATIC 1
-#define CONFIG_WEBM_IO 1
-#define CONFIG_BITSTREAM_DEBUG 0
-#define CONFIG_DEBUG 0
-#define CONFIG_MISMATCH_DEBUG 0
-#define CONFIG_ACCOUNTING 0
-#define CONFIG_ANALYZER 0
-#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
-#define CONFIG_DENOISE 0
-#define CONFIG_FILEOPTIONS 1
-#define CONFIG_FIX_GF_LENGTH 1
-#define CONFIG_INSPECTION 0
-#define CONFIG_INTERNAL_STATS 0
-#define CONFIG_LOWBITDEPTH 1
-#define CONFIG_MAX_DECODE_PROFILE 0
-#define CONFIG_NORMAL_TILE_MODE 1
-#define CONFIG_SIZE_LIMIT 1
-#define CONFIG_SPATIAL_RESAMPLING 1
-#define DECODE_HEIGHT_LIMIT 16384
-#define DECODE_WIDTH_LIMIT 16384
-#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
-#define CONFIG_COLLECT_RD_STATS 0
-#define CONFIG_DIST_8X8 1
-#define CONFIG_ENTROPY_STATS 0
-#define CONFIG_FP_MB_STATS 0
-#define CONFIG_INTER_STATS_ONLY 0
-#define CONFIG_RD_DEBUG 0
-#endif /* AOM_CONFIG_H_ */
+#define INLINE inline
+#endif  // AOM_CONFIG_H_
diff --git a/third_party/libaom/source/config/linux/x64/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/linux/x64/config/aom_dsp_rtcd.h
index 6b8bde10..b7ec4f5 100644
--- a/third_party/libaom/source/config/linux/x64/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/linux/x64/config/aom_dsp_rtcd.h
@@ -73,6 +73,18 @@
                                int h,
                                int subx,
                                int suby);
+void aom_blend_a64_mask_avx2(uint8_t* dst,
+                             uint32_t dst_stride,
+                             const uint8_t* src0,
+                             uint32_t src0_stride,
+                             const uint8_t* src1,
+                             uint32_t src1_stride,
+                             const uint8_t* mask,
+                             uint32_t mask_stride,
+                             int w,
+                             int h,
+                             int subx,
+                             int suby);
 RTCD_EXTERN void (*aom_blend_a64_mask)(uint8_t* dst,
                                        uint32_t dst_stride,
                                        const uint8_t* src0,
@@ -5344,6 +5356,8 @@
   aom_blend_a64_mask = aom_blend_a64_mask_c;
   if (flags & HAS_SSE4_1)
     aom_blend_a64_mask = aom_blend_a64_mask_sse4_1;
+  if (flags & HAS_AVX2)
+    aom_blend_a64_mask = aom_blend_a64_mask_avx2;
   aom_blend_a64_vmask = aom_blend_a64_vmask_c;
   if (flags & HAS_SSE4_1)
     aom_blend_a64_vmask = aom_blend_a64_vmask_sse4_1;
diff --git a/third_party/libaom/source/config/linux/x64/config/av1_rtcd.h b/third_party/libaom/source/config/linux/x64/config/av1_rtcd.h
index 168825d2..df84b1b 100644
--- a/third_party/libaom/source/config/linux/x64/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/linux/x64/config/av1_rtcd.h
@@ -925,6 +925,75 @@
                                    int bd);
 #define av1_highbd_dr_prediction_z3 av1_highbd_dr_prediction_z3_c
 
+void av1_highbd_inv_txfm_add_c(const tran_low_t* dqcoeff,
+                               uint8_t* dst,
+                               int stride,
+                               const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_sse4_1(const tran_low_t* dqcoeff,
+                                    uint8_t* dst,
+                                    int stride,
+                                    const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_avx2(const tran_low_t* dqcoeff,
+                                  uint8_t* dst,
+                                  int stride,
+                                  const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add)(const tran_low_t* dqcoeff,
+                                            uint8_t* dst,
+                                            int stride,
+                                            const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_16x16_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_16x16_sse4_1(const tran_low_t* dqcoeff,
+                                          uint8_t* dst,
+                                          int stride,
+                                          const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_16x16)(const tran_low_t* dqcoeff,
+                                                  uint8_t* dst,
+                                                  int stride,
+                                                  const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_32x32_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_32x32_avx2(const tran_low_t* dqcoeff,
+                                        uint8_t* dst,
+                                        int stride,
+                                        const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_32x32)(const tran_low_t* dqcoeff,
+                                                  uint8_t* dst,
+                                                  int stride,
+                                                  const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_4x4_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_4x4_sse4_1(const tran_low_t* dqcoeff,
+                                        uint8_t* dst,
+                                        int stride,
+                                        const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_4x4)(const tran_low_t* dqcoeff,
+                                                uint8_t* dst,
+                                                int stride,
+                                                const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_8x8_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_8x8_sse4_1(const tran_low_t* dqcoeff,
+                                        uint8_t* dst,
+                                        int stride,
+                                        const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_8x8)(const tran_low_t* dqcoeff,
+                                                uint8_t* dst,
+                                                int stride,
+                                                const TxfmParam* txfm_param);
+
 void av1_highbd_iwht4x4_16_add_c(const tran_low_t* input,
                                  uint8_t* dest,
                                  int dest_stride,
@@ -1379,16 +1448,7 @@
                                 int stride,
                                 TX_TYPE tx_type,
                                 int bd);
-void av1_inv_txfm2d_add_64x64_sse4_1(const int32_t* input,
-                                     uint16_t* output,
-                                     int stride,
-                                     TX_TYPE tx_type,
-                                     int bd);
-RTCD_EXTERN void (*av1_inv_txfm2d_add_64x64)(const int32_t* input,
-                                             uint16_t* output,
-                                             int stride,
-                                             TX_TYPE tx_type,
-                                             int bd);
+#define av1_inv_txfm2d_add_64x64 av1_inv_txfm2d_add_64x64_c
 
 void av1_inv_txfm2d_add_8x16_c(const int32_t* input,
                                uint16_t* output,
@@ -2081,6 +2141,23 @@
     av1_highbd_convolve_y_sr = av1_highbd_convolve_y_sr_ssse3;
   if (flags & HAS_AVX2)
     av1_highbd_convolve_y_sr = av1_highbd_convolve_y_sr_avx2;
+  av1_highbd_inv_txfm_add = av1_highbd_inv_txfm_add_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add = av1_highbd_inv_txfm_add_sse4_1;
+  if (flags & HAS_AVX2)
+    av1_highbd_inv_txfm_add = av1_highbd_inv_txfm_add_avx2;
+  av1_highbd_inv_txfm_add_16x16 = av1_highbd_inv_txfm_add_16x16_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add_16x16 = av1_highbd_inv_txfm_add_16x16_sse4_1;
+  av1_highbd_inv_txfm_add_32x32 = av1_highbd_inv_txfm_add_32x32_c;
+  if (flags & HAS_AVX2)
+    av1_highbd_inv_txfm_add_32x32 = av1_highbd_inv_txfm_add_32x32_avx2;
+  av1_highbd_inv_txfm_add_4x4 = av1_highbd_inv_txfm_add_4x4_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add_4x4 = av1_highbd_inv_txfm_add_4x4_sse4_1;
+  av1_highbd_inv_txfm_add_8x8 = av1_highbd_inv_txfm_add_8x8_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add_8x8 = av1_highbd_inv_txfm_add_8x8_sse4_1;
   av1_highbd_jnt_convolve_2d = av1_highbd_jnt_convolve_2d_c;
   if (flags & HAS_SSE4_1)
     av1_highbd_jnt_convolve_2d = av1_highbd_jnt_convolve_2d_sse4_1;
@@ -2120,9 +2197,6 @@
   av1_inv_txfm2d_add_4x4 = av1_inv_txfm2d_add_4x4_c;
   if (flags & HAS_SSE4_1)
     av1_inv_txfm2d_add_4x4 = av1_inv_txfm2d_add_4x4_sse4_1;
-  av1_inv_txfm2d_add_64x64 = av1_inv_txfm2d_add_64x64_c;
-  if (flags & HAS_SSE4_1)
-    av1_inv_txfm2d_add_64x64 = av1_inv_txfm2d_add_64x64_sse4_1;
   av1_inv_txfm2d_add_8x8 = av1_inv_txfm2d_add_8x8_c;
   if (flags & HAS_SSE4_1)
     av1_inv_txfm2d_add_8x8 = av1_inv_txfm2d_add_8x8_sse4_1;
diff --git a/third_party/libaom/source/config/win/ia32/config/aom_config.asm b/third_party/libaom/source/config/win/ia32/config/aom_config.asm
index 40b52ef..5761648 100644
--- a/third_party/libaom/source/config/win/ia32/config/aom_config.asm
+++ b/third_party/libaom/source/config/win/ia32/config/aom_config.asm
@@ -3,61 +3,63 @@
 %define ARCH_PPC 0
 %define ARCH_X86 1
 %define ARCH_X86_64 0
-%define HAVE_NEON 0
-%define HAVE_DSPR2 0
-%define HAVE_MIPS32 0
-%define HAVE_MIPS64 0
-%define HAVE_MSA 0
-%define HAVE_VSX 0
+%define CONFIG_2PASS_PARTITION_SEARCH_LVL 1
+%define CONFIG_ACCOUNTING 0
+%define CONFIG_ANALYZER 0
+%define CONFIG_AV1_DECODER 1
+%define CONFIG_AV1_ENCODER 0
+%define CONFIG_BIG_ENDIAN 0
+%define CONFIG_BITSTREAM_DEBUG 0
+%define CONFIG_COEFFICIENT_RANGE_CHECKING 0
+%define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
+%define CONFIG_COLLECT_RD_STATS 0
+%define CONFIG_DEBUG 0
+%define CONFIG_DENOISE 0
+%define CONFIG_DIST_8X8 0
+%define CONFIG_ENTROPY_STATS 0
+%define CONFIG_FILEOPTIONS 1
+%define CONFIG_FIX_GF_LENGTH 1
+%define CONFIG_FP_MB_STATS 0
+%define CONFIG_GCC 0
+%define CONFIG_GCOV 0
+%define CONFIG_GPROF 0
+%define CONFIG_INSPECTION 0
+%define CONFIG_INTERNAL_STATS 0
+%define CONFIG_INTER_STATS_ONLY 0
+%define CONFIG_LIBYUV 1
+%define CONFIG_LOWBITDEPTH 1
+%define CONFIG_MAX_DECODE_PROFILE 0
+%define CONFIG_MISMATCH_DEBUG 0
+%define CONFIG_MULTITHREAD 1
+%define CONFIG_NORMAL_TILE_MODE 1
+%define CONFIG_OS_SUPPORT 1
+%define CONFIG_PIC 0
+%define CONFIG_RD_DEBUG 0
+%define CONFIG_REDUCED_ENCODER_BORDER 0
+%define CONFIG_RUNTIME_CPU_DETECT 1
+%define CONFIG_SHARED 0
+%define CONFIG_SIZE_LIMIT 1
+%define CONFIG_SPATIAL_RESAMPLING 1
+%define CONFIG_STATIC 1
+%define CONFIG_WEBM_IO 1
+%define DECODE_HEIGHT_LIMIT 16384
+%define DECODE_WIDTH_LIMIT 16384
 %define HAVE_AVX 1
 %define HAVE_AVX2 1
+%define HAVE_DSPR2 0
+%define HAVE_FEXCEPT 1
+%define HAVE_MIPS32 0
+%define HAVE_MIPS64 0
 %define HAVE_MMX 1
+%define HAVE_MSA 0
+%define HAVE_NEON 0
+%define HAVE_PTHREAD_H 0
 %define HAVE_SSE 1
 %define HAVE_SSE2 1
 %define HAVE_SSE3 1
 %define HAVE_SSE4_1 1
 %define HAVE_SSE4_2 1
 %define HAVE_SSSE3 1
-%define HAVE_FEXCEPT 1
-%define HAVE_PTHREAD_H 0
 %define HAVE_UNISTD_H 0
+%define HAVE_VSX 0
 %define HAVE_WXWIDGETS 0
-%define CONFIG_AV1_DECODER 1
-%define CONFIG_AV1_ENCODER 0
-%define CONFIG_BIG_ENDIAN 0
-%define CONFIG_GCC 0
-%define CONFIG_GCOV 0
-%define CONFIG_GPROF 0
-%define CONFIG_LIBYUV 1
-%define CONFIG_MULTITHREAD 1
-%define CONFIG_OS_SUPPORT 1
-%define CONFIG_PIC 0
-%define CONFIG_RUNTIME_CPU_DETECT 1
-%define CONFIG_SHARED 0
-%define CONFIG_STATIC 1
-%define CONFIG_WEBM_IO 1
-%define CONFIG_BITSTREAM_DEBUG 0
-%define CONFIG_DEBUG 0
-%define CONFIG_MISMATCH_DEBUG 0
-%define CONFIG_ACCOUNTING 0
-%define CONFIG_ANALYZER 0
-%define CONFIG_COEFFICIENT_RANGE_CHECKING 0
-%define CONFIG_DENOISE 0
-%define CONFIG_FILEOPTIONS 1
-%define CONFIG_FIX_GF_LENGTH 1
-%define CONFIG_INSPECTION 0
-%define CONFIG_INTERNAL_STATS 0
-%define CONFIG_LOWBITDEPTH 1
-%define CONFIG_MAX_DECODE_PROFILE 0
-%define CONFIG_NORMAL_TILE_MODE 1
-%define CONFIG_SIZE_LIMIT 1
-%define CONFIG_SPATIAL_RESAMPLING 1
-%define DECODE_HEIGHT_LIMIT 16384
-%define DECODE_WIDTH_LIMIT 16384
-%define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
-%define CONFIG_COLLECT_RD_STATS 0
-%define CONFIG_DIST_8X8 1
-%define CONFIG_ENTROPY_STATS 0
-%define CONFIG_FP_MB_STATS 0
-%define CONFIG_INTER_STATS_ONLY 0
-%define CONFIG_RD_DEBUG 0
diff --git a/third_party/libaom/source/config/win/ia32/config/aom_config.c b/third_party/libaom/source/config/win/ia32/config/aom_config.c
index ab01a869..b293f45 100644
--- a/third_party/libaom/source/config/win/ia32/config/aom_config.c
+++ b/third_party/libaom/source/config/win/ia32/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/wtc/chromium.2/src/third_party/libaom/source/libaom/build/cmake/toolchains/x86-linux.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
+static const char* const cfg = "cmake ../source/libaom -G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"../source/libaom/build/cmake/toolchains/x86-linux.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/win/ia32/config/aom_config.h b/third_party/libaom/source/config/win/ia32/config/aom_config.h
index a396468..cd6d4a0 100644
--- a/third_party/libaom/source/config/win/ia32/config/aom_config.h
+++ b/third_party/libaom/source/config/win/ia32/config/aom_config.h
@@ -10,68 +10,70 @@
  */
 #ifndef AOM_CONFIG_H_
 #define AOM_CONFIG_H_
-#define INLINE __inline
 #define ARCH_ARM 0
 #define ARCH_MIPS 0
 #define ARCH_PPC 0
 #define ARCH_X86 1
 #define ARCH_X86_64 0
-#define HAVE_NEON 0
-#define HAVE_DSPR2 0
-#define HAVE_MIPS32 0
-#define HAVE_MIPS64 0
-#define HAVE_MSA 0
-#define HAVE_VSX 0
+#define CONFIG_2PASS_PARTITION_SEARCH_LVL 1
+#define CONFIG_ACCOUNTING 0
+#define CONFIG_ANALYZER 0
+#define CONFIG_AV1_DECODER 1
+#define CONFIG_AV1_ENCODER 0
+#define CONFIG_BIG_ENDIAN 0
+#define CONFIG_BITSTREAM_DEBUG 0
+#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
+#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
+#define CONFIG_COLLECT_RD_STATS 0
+#define CONFIG_DEBUG 0
+#define CONFIG_DENOISE 0
+#define CONFIG_DIST_8X8 0
+#define CONFIG_ENTROPY_STATS 0
+#define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
+#define CONFIG_FP_MB_STATS 0
+#define CONFIG_GCC 0
+#define CONFIG_GCOV 0
+#define CONFIG_GPROF 0
+#define CONFIG_INSPECTION 0
+#define CONFIG_INTERNAL_STATS 0
+#define CONFIG_INTER_STATS_ONLY 0
+#define CONFIG_LIBYUV 1
+#define CONFIG_LOWBITDEPTH 1
+#define CONFIG_MAX_DECODE_PROFILE 0
+#define CONFIG_MISMATCH_DEBUG 0
+#define CONFIG_MULTITHREAD 1
+#define CONFIG_NORMAL_TILE_MODE 1
+#define CONFIG_OS_SUPPORT 1
+#define CONFIG_PIC 0
+#define CONFIG_RD_DEBUG 0
+#define CONFIG_REDUCED_ENCODER_BORDER 0
+#define CONFIG_RUNTIME_CPU_DETECT 1
+#define CONFIG_SHARED 0
+#define CONFIG_SIZE_LIMIT 1
+#define CONFIG_SPATIAL_RESAMPLING 1
+#define CONFIG_STATIC 1
+#define CONFIG_WEBM_IO 1
+#define DECODE_HEIGHT_LIMIT 16384
+#define DECODE_WIDTH_LIMIT 16384
 #define HAVE_AVX 1
 #define HAVE_AVX2 1
+#define HAVE_DSPR2 0
+#define HAVE_FEXCEPT 1
+#define HAVE_MIPS32 0
+#define HAVE_MIPS64 0
 #define HAVE_MMX 1
+#define HAVE_MSA 0
+#define HAVE_NEON 0
+#define HAVE_PTHREAD_H 0
 #define HAVE_SSE 1
 #define HAVE_SSE2 1
 #define HAVE_SSE3 1
 #define HAVE_SSE4_1 1
 #define HAVE_SSE4_2 1
 #define HAVE_SSSE3 1
-#define HAVE_FEXCEPT 1
-#define HAVE_PTHREAD_H 0
 #define HAVE_UNISTD_H 0
+#define HAVE_VSX 0
 #define HAVE_WXWIDGETS 0
-#define CONFIG_AV1_DECODER 1
-#define CONFIG_AV1_ENCODER 0
-#define CONFIG_BIG_ENDIAN 0
-#define CONFIG_GCC 0
-#define CONFIG_GCOV 0
-#define CONFIG_GPROF 0
-#define CONFIG_LIBYUV 1
-#define CONFIG_MULTITHREAD 1
-#define CONFIG_OS_SUPPORT 1
-#define CONFIG_PIC 0
-#define CONFIG_RUNTIME_CPU_DETECT 1
-#define CONFIG_SHARED 0
-#define CONFIG_STATIC 1
-#define CONFIG_WEBM_IO 1
-#define CONFIG_BITSTREAM_DEBUG 0
-#define CONFIG_DEBUG 0
-#define CONFIG_MISMATCH_DEBUG 0
-#define CONFIG_ACCOUNTING 0
-#define CONFIG_ANALYZER 0
-#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
-#define CONFIG_DENOISE 0
-#define CONFIG_FILEOPTIONS 1
-#define CONFIG_FIX_GF_LENGTH 1
-#define CONFIG_INSPECTION 0
-#define CONFIG_INTERNAL_STATS 0
-#define CONFIG_LOWBITDEPTH 1
-#define CONFIG_MAX_DECODE_PROFILE 0
-#define CONFIG_NORMAL_TILE_MODE 1
-#define CONFIG_SIZE_LIMIT 1
-#define CONFIG_SPATIAL_RESAMPLING 1
-#define DECODE_HEIGHT_LIMIT 16384
-#define DECODE_WIDTH_LIMIT 16384
-#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
-#define CONFIG_COLLECT_RD_STATS 0
-#define CONFIG_DIST_8X8 1
-#define CONFIG_ENTROPY_STATS 0
-#define CONFIG_FP_MB_STATS 0
-#define CONFIG_INTER_STATS_ONLY 0
-#define CONFIG_RD_DEBUG 0
-#endif /* AOM_CONFIG_H_ */
+#define INLINE __inline
+#endif  // AOM_CONFIG_H_
diff --git a/third_party/libaom/source/config/win/ia32/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/win/ia32/config/aom_dsp_rtcd.h
index 76fdd866..74280b0 100644
--- a/third_party/libaom/source/config/win/ia32/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/win/ia32/config/aom_dsp_rtcd.h
@@ -73,6 +73,18 @@
                                int h,
                                int subx,
                                int suby);
+void aom_blend_a64_mask_avx2(uint8_t* dst,
+                             uint32_t dst_stride,
+                             const uint8_t* src0,
+                             uint32_t src0_stride,
+                             const uint8_t* src1,
+                             uint32_t src1_stride,
+                             const uint8_t* mask,
+                             uint32_t mask_stride,
+                             int w,
+                             int h,
+                             int subx,
+                             int suby);
 RTCD_EXTERN void (*aom_blend_a64_mask)(uint8_t* dst,
                                        uint32_t dst_stride,
                                        const uint8_t* src0,
@@ -5960,6 +5972,8 @@
   aom_blend_a64_mask = aom_blend_a64_mask_c;
   if (flags & HAS_SSE4_1)
     aom_blend_a64_mask = aom_blend_a64_mask_sse4_1;
+  if (flags & HAS_AVX2)
+    aom_blend_a64_mask = aom_blend_a64_mask_avx2;
   aom_blend_a64_vmask = aom_blend_a64_vmask_c;
   if (flags & HAS_SSE4_1)
     aom_blend_a64_vmask = aom_blend_a64_vmask_sse4_1;
diff --git a/third_party/libaom/source/config/win/ia32/config/av1_rtcd.h b/third_party/libaom/source/config/win/ia32/config/av1_rtcd.h
index d3f9eac..8bc8829 100644
--- a/third_party/libaom/source/config/win/ia32/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/win/ia32/config/av1_rtcd.h
@@ -892,6 +892,75 @@
                                    int bd);
 #define av1_highbd_dr_prediction_z3 av1_highbd_dr_prediction_z3_c
 
+void av1_highbd_inv_txfm_add_c(const tran_low_t* dqcoeff,
+                               uint8_t* dst,
+                               int stride,
+                               const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_sse4_1(const tran_low_t* dqcoeff,
+                                    uint8_t* dst,
+                                    int stride,
+                                    const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_avx2(const tran_low_t* dqcoeff,
+                                  uint8_t* dst,
+                                  int stride,
+                                  const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add)(const tran_low_t* dqcoeff,
+                                            uint8_t* dst,
+                                            int stride,
+                                            const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_16x16_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_16x16_sse4_1(const tran_low_t* dqcoeff,
+                                          uint8_t* dst,
+                                          int stride,
+                                          const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_16x16)(const tran_low_t* dqcoeff,
+                                                  uint8_t* dst,
+                                                  int stride,
+                                                  const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_32x32_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_32x32_avx2(const tran_low_t* dqcoeff,
+                                        uint8_t* dst,
+                                        int stride,
+                                        const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_32x32)(const tran_low_t* dqcoeff,
+                                                  uint8_t* dst,
+                                                  int stride,
+                                                  const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_4x4_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_4x4_sse4_1(const tran_low_t* dqcoeff,
+                                        uint8_t* dst,
+                                        int stride,
+                                        const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_4x4)(const tran_low_t* dqcoeff,
+                                                uint8_t* dst,
+                                                int stride,
+                                                const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_8x8_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_8x8_sse4_1(const tran_low_t* dqcoeff,
+                                        uint8_t* dst,
+                                        int stride,
+                                        const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_8x8)(const tran_low_t* dqcoeff,
+                                                uint8_t* dst,
+                                                int stride,
+                                                const TxfmParam* txfm_param);
+
 void av1_highbd_iwht4x4_16_add_c(const tran_low_t* input,
                                  uint8_t* dest,
                                  int dest_stride,
@@ -1346,16 +1415,7 @@
                                 int stride,
                                 TX_TYPE tx_type,
                                 int bd);
-void av1_inv_txfm2d_add_64x64_sse4_1(const int32_t* input,
-                                     uint16_t* output,
-                                     int stride,
-                                     TX_TYPE tx_type,
-                                     int bd);
-RTCD_EXTERN void (*av1_inv_txfm2d_add_64x64)(const int32_t* input,
-                                             uint16_t* output,
-                                             int stride,
-                                             TX_TYPE tx_type,
-                                             int bd);
+#define av1_inv_txfm2d_add_64x64 av1_inv_txfm2d_add_64x64_c
 
 void av1_inv_txfm2d_add_8x16_c(const int32_t* input,
                                uint16_t* output,
@@ -2058,6 +2118,23 @@
     av1_highbd_convolve_y_sr = av1_highbd_convolve_y_sr_ssse3;
   if (flags & HAS_AVX2)
     av1_highbd_convolve_y_sr = av1_highbd_convolve_y_sr_avx2;
+  av1_highbd_inv_txfm_add = av1_highbd_inv_txfm_add_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add = av1_highbd_inv_txfm_add_sse4_1;
+  if (flags & HAS_AVX2)
+    av1_highbd_inv_txfm_add = av1_highbd_inv_txfm_add_avx2;
+  av1_highbd_inv_txfm_add_16x16 = av1_highbd_inv_txfm_add_16x16_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add_16x16 = av1_highbd_inv_txfm_add_16x16_sse4_1;
+  av1_highbd_inv_txfm_add_32x32 = av1_highbd_inv_txfm_add_32x32_c;
+  if (flags & HAS_AVX2)
+    av1_highbd_inv_txfm_add_32x32 = av1_highbd_inv_txfm_add_32x32_avx2;
+  av1_highbd_inv_txfm_add_4x4 = av1_highbd_inv_txfm_add_4x4_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add_4x4 = av1_highbd_inv_txfm_add_4x4_sse4_1;
+  av1_highbd_inv_txfm_add_8x8 = av1_highbd_inv_txfm_add_8x8_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add_8x8 = av1_highbd_inv_txfm_add_8x8_sse4_1;
   av1_highbd_jnt_convolve_2d = av1_highbd_jnt_convolve_2d_c;
   if (flags & HAS_SSE4_1)
     av1_highbd_jnt_convolve_2d = av1_highbd_jnt_convolve_2d_sse4_1;
@@ -2097,9 +2174,6 @@
   av1_inv_txfm2d_add_4x4 = av1_inv_txfm2d_add_4x4_c;
   if (flags & HAS_SSE4_1)
     av1_inv_txfm2d_add_4x4 = av1_inv_txfm2d_add_4x4_sse4_1;
-  av1_inv_txfm2d_add_64x64 = av1_inv_txfm2d_add_64x64_c;
-  if (flags & HAS_SSE4_1)
-    av1_inv_txfm2d_add_64x64 = av1_inv_txfm2d_add_64x64_sse4_1;
   av1_inv_txfm2d_add_8x8 = av1_inv_txfm2d_add_8x8_c;
   if (flags & HAS_SSE4_1)
     av1_inv_txfm2d_add_8x8 = av1_inv_txfm2d_add_8x8_sse4_1;
diff --git a/third_party/libaom/source/config/win/x64/config/aom_config.asm b/third_party/libaom/source/config/win/x64/config/aom_config.asm
index ca2d5631..9c0da7d 100644
--- a/third_party/libaom/source/config/win/x64/config/aom_config.asm
+++ b/third_party/libaom/source/config/win/x64/config/aom_config.asm
@@ -3,61 +3,63 @@
 %define ARCH_PPC 0
 %define ARCH_X86 0
 %define ARCH_X86_64 1
-%define HAVE_NEON 0
-%define HAVE_DSPR2 0
-%define HAVE_MIPS32 0
-%define HAVE_MIPS64 0
-%define HAVE_MSA 0
-%define HAVE_VSX 0
+%define CONFIG_2PASS_PARTITION_SEARCH_LVL 1
+%define CONFIG_ACCOUNTING 0
+%define CONFIG_ANALYZER 0
+%define CONFIG_AV1_DECODER 1
+%define CONFIG_AV1_ENCODER 0
+%define CONFIG_BIG_ENDIAN 0
+%define CONFIG_BITSTREAM_DEBUG 0
+%define CONFIG_COEFFICIENT_RANGE_CHECKING 0
+%define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
+%define CONFIG_COLLECT_RD_STATS 0
+%define CONFIG_DEBUG 0
+%define CONFIG_DENOISE 0
+%define CONFIG_DIST_8X8 0
+%define CONFIG_ENTROPY_STATS 0
+%define CONFIG_FILEOPTIONS 1
+%define CONFIG_FIX_GF_LENGTH 1
+%define CONFIG_FP_MB_STATS 0
+%define CONFIG_GCC 0
+%define CONFIG_GCOV 0
+%define CONFIG_GPROF 0
+%define CONFIG_INSPECTION 0
+%define CONFIG_INTERNAL_STATS 0
+%define CONFIG_INTER_STATS_ONLY 0
+%define CONFIG_LIBYUV 1
+%define CONFIG_LOWBITDEPTH 1
+%define CONFIG_MAX_DECODE_PROFILE 0
+%define CONFIG_MISMATCH_DEBUG 0
+%define CONFIG_MULTITHREAD 1
+%define CONFIG_NORMAL_TILE_MODE 1
+%define CONFIG_OS_SUPPORT 1
+%define CONFIG_PIC 0
+%define CONFIG_RD_DEBUG 0
+%define CONFIG_REDUCED_ENCODER_BORDER 0
+%define CONFIG_RUNTIME_CPU_DETECT 1
+%define CONFIG_SHARED 0
+%define CONFIG_SIZE_LIMIT 1
+%define CONFIG_SPATIAL_RESAMPLING 1
+%define CONFIG_STATIC 1
+%define CONFIG_WEBM_IO 1
+%define DECODE_HEIGHT_LIMIT 16384
+%define DECODE_WIDTH_LIMIT 16384
 %define HAVE_AVX 1
 %define HAVE_AVX2 1
+%define HAVE_DSPR2 0
+%define HAVE_FEXCEPT 1
+%define HAVE_MIPS32 0
+%define HAVE_MIPS64 0
 %define HAVE_MMX 1
+%define HAVE_MSA 0
+%define HAVE_NEON 0
+%define HAVE_PTHREAD_H 0
 %define HAVE_SSE 1
 %define HAVE_SSE2 1
 %define HAVE_SSE3 1
 %define HAVE_SSE4_1 1
 %define HAVE_SSE4_2 1
 %define HAVE_SSSE3 1
-%define HAVE_FEXCEPT 1
-%define HAVE_PTHREAD_H 0
 %define HAVE_UNISTD_H 0
+%define HAVE_VSX 0
 %define HAVE_WXWIDGETS 0
-%define CONFIG_AV1_DECODER 1
-%define CONFIG_AV1_ENCODER 0
-%define CONFIG_BIG_ENDIAN 0
-%define CONFIG_GCC 0
-%define CONFIG_GCOV 0
-%define CONFIG_GPROF 0
-%define CONFIG_LIBYUV 1
-%define CONFIG_MULTITHREAD 1
-%define CONFIG_OS_SUPPORT 1
-%define CONFIG_PIC 0
-%define CONFIG_RUNTIME_CPU_DETECT 1
-%define CONFIG_SHARED 0
-%define CONFIG_STATIC 1
-%define CONFIG_WEBM_IO 1
-%define CONFIG_BITSTREAM_DEBUG 0
-%define CONFIG_DEBUG 0
-%define CONFIG_MISMATCH_DEBUG 0
-%define CONFIG_ACCOUNTING 0
-%define CONFIG_ANALYZER 0
-%define CONFIG_COEFFICIENT_RANGE_CHECKING 0
-%define CONFIG_DENOISE 0
-%define CONFIG_FILEOPTIONS 1
-%define CONFIG_FIX_GF_LENGTH 1
-%define CONFIG_INSPECTION 0
-%define CONFIG_INTERNAL_STATS 0
-%define CONFIG_LOWBITDEPTH 1
-%define CONFIG_MAX_DECODE_PROFILE 0
-%define CONFIG_NORMAL_TILE_MODE 1
-%define CONFIG_SIZE_LIMIT 1
-%define CONFIG_SPATIAL_RESAMPLING 1
-%define DECODE_HEIGHT_LIMIT 16384
-%define DECODE_WIDTH_LIMIT 16384
-%define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
-%define CONFIG_COLLECT_RD_STATS 0
-%define CONFIG_DIST_8X8 1
-%define CONFIG_ENTROPY_STATS 0
-%define CONFIG_FP_MB_STATS 0
-%define CONFIG_INTER_STATS_ONLY 0
-%define CONFIG_RD_DEBUG 0
diff --git a/third_party/libaom/source/config/win/x64/config/aom_config.c b/third_party/libaom/source/config/win/x64/config/aom_config.c
index ff327eb..a31187a 100644
--- a/third_party/libaom/source/config/win/x64/config/aom_config.c
+++ b/third_party/libaom/source/config/win/x64/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DAOM_TARGET_CPU=x86_64 -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
+static const char* const cfg = "cmake ../source/libaom -G \"Unix Makefiles\" -DAOM_TARGET_CPU=x86_64 -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/win/x64/config/aom_config.h b/third_party/libaom/source/config/win/x64/config/aom_config.h
index 4ad2496..a607b37 100644
--- a/third_party/libaom/source/config/win/x64/config/aom_config.h
+++ b/third_party/libaom/source/config/win/x64/config/aom_config.h
@@ -10,68 +10,70 @@
  */
 #ifndef AOM_CONFIG_H_
 #define AOM_CONFIG_H_
-#define INLINE __inline
 #define ARCH_ARM 0
 #define ARCH_MIPS 0
 #define ARCH_PPC 0
 #define ARCH_X86 0
 #define ARCH_X86_64 1
-#define HAVE_NEON 0
-#define HAVE_DSPR2 0
-#define HAVE_MIPS32 0
-#define HAVE_MIPS64 0
-#define HAVE_MSA 0
-#define HAVE_VSX 0
+#define CONFIG_2PASS_PARTITION_SEARCH_LVL 1
+#define CONFIG_ACCOUNTING 0
+#define CONFIG_ANALYZER 0
+#define CONFIG_AV1_DECODER 1
+#define CONFIG_AV1_ENCODER 0
+#define CONFIG_BIG_ENDIAN 0
+#define CONFIG_BITSTREAM_DEBUG 0
+#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
+#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
+#define CONFIG_COLLECT_RD_STATS 0
+#define CONFIG_DEBUG 0
+#define CONFIG_DENOISE 0
+#define CONFIG_DIST_8X8 0
+#define CONFIG_ENTROPY_STATS 0
+#define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
+#define CONFIG_FP_MB_STATS 0
+#define CONFIG_GCC 0
+#define CONFIG_GCOV 0
+#define CONFIG_GPROF 0
+#define CONFIG_INSPECTION 0
+#define CONFIG_INTERNAL_STATS 0
+#define CONFIG_INTER_STATS_ONLY 0
+#define CONFIG_LIBYUV 1
+#define CONFIG_LOWBITDEPTH 1
+#define CONFIG_MAX_DECODE_PROFILE 0
+#define CONFIG_MISMATCH_DEBUG 0
+#define CONFIG_MULTITHREAD 1
+#define CONFIG_NORMAL_TILE_MODE 1
+#define CONFIG_OS_SUPPORT 1
+#define CONFIG_PIC 0
+#define CONFIG_RD_DEBUG 0
+#define CONFIG_REDUCED_ENCODER_BORDER 0
+#define CONFIG_RUNTIME_CPU_DETECT 1
+#define CONFIG_SHARED 0
+#define CONFIG_SIZE_LIMIT 1
+#define CONFIG_SPATIAL_RESAMPLING 1
+#define CONFIG_STATIC 1
+#define CONFIG_WEBM_IO 1
+#define DECODE_HEIGHT_LIMIT 16384
+#define DECODE_WIDTH_LIMIT 16384
 #define HAVE_AVX 1
 #define HAVE_AVX2 1
+#define HAVE_DSPR2 0
+#define HAVE_FEXCEPT 1
+#define HAVE_MIPS32 0
+#define HAVE_MIPS64 0
 #define HAVE_MMX 1
+#define HAVE_MSA 0
+#define HAVE_NEON 0
+#define HAVE_PTHREAD_H 0
 #define HAVE_SSE 1
 #define HAVE_SSE2 1
 #define HAVE_SSE3 1
 #define HAVE_SSE4_1 1
 #define HAVE_SSE4_2 1
 #define HAVE_SSSE3 1
-#define HAVE_FEXCEPT 1
-#define HAVE_PTHREAD_H 0
 #define HAVE_UNISTD_H 0
+#define HAVE_VSX 0
 #define HAVE_WXWIDGETS 0
-#define CONFIG_AV1_DECODER 1
-#define CONFIG_AV1_ENCODER 0
-#define CONFIG_BIG_ENDIAN 0
-#define CONFIG_GCC 0
-#define CONFIG_GCOV 0
-#define CONFIG_GPROF 0
-#define CONFIG_LIBYUV 1
-#define CONFIG_MULTITHREAD 1
-#define CONFIG_OS_SUPPORT 1
-#define CONFIG_PIC 0
-#define CONFIG_RUNTIME_CPU_DETECT 1
-#define CONFIG_SHARED 0
-#define CONFIG_STATIC 1
-#define CONFIG_WEBM_IO 1
-#define CONFIG_BITSTREAM_DEBUG 0
-#define CONFIG_DEBUG 0
-#define CONFIG_MISMATCH_DEBUG 0
-#define CONFIG_ACCOUNTING 0
-#define CONFIG_ANALYZER 0
-#define CONFIG_COEFFICIENT_RANGE_CHECKING 0
-#define CONFIG_DENOISE 0
-#define CONFIG_FILEOPTIONS 1
-#define CONFIG_FIX_GF_LENGTH 1
-#define CONFIG_INSPECTION 0
-#define CONFIG_INTERNAL_STATS 0
-#define CONFIG_LOWBITDEPTH 1
-#define CONFIG_MAX_DECODE_PROFILE 0
-#define CONFIG_NORMAL_TILE_MODE 1
-#define CONFIG_SIZE_LIMIT 1
-#define CONFIG_SPATIAL_RESAMPLING 1
-#define DECODE_HEIGHT_LIMIT 16384
-#define DECODE_WIDTH_LIMIT 16384
-#define CONFIG_COLLECT_INTER_MODE_RD_STATS 1
-#define CONFIG_COLLECT_RD_STATS 0
-#define CONFIG_DIST_8X8 1
-#define CONFIG_ENTROPY_STATS 0
-#define CONFIG_FP_MB_STATS 0
-#define CONFIG_INTER_STATS_ONLY 0
-#define CONFIG_RD_DEBUG 0
-#endif /* AOM_CONFIG_H_ */
+#define INLINE __inline
+#endif  // AOM_CONFIG_H_
diff --git a/third_party/libaom/source/config/win/x64/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/win/x64/config/aom_dsp_rtcd.h
index 6b8bde10..b7ec4f5 100644
--- a/third_party/libaom/source/config/win/x64/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/win/x64/config/aom_dsp_rtcd.h
@@ -73,6 +73,18 @@
                                int h,
                                int subx,
                                int suby);
+void aom_blend_a64_mask_avx2(uint8_t* dst,
+                             uint32_t dst_stride,
+                             const uint8_t* src0,
+                             uint32_t src0_stride,
+                             const uint8_t* src1,
+                             uint32_t src1_stride,
+                             const uint8_t* mask,
+                             uint32_t mask_stride,
+                             int w,
+                             int h,
+                             int subx,
+                             int suby);
 RTCD_EXTERN void (*aom_blend_a64_mask)(uint8_t* dst,
                                        uint32_t dst_stride,
                                        const uint8_t* src0,
@@ -5344,6 +5356,8 @@
   aom_blend_a64_mask = aom_blend_a64_mask_c;
   if (flags & HAS_SSE4_1)
     aom_blend_a64_mask = aom_blend_a64_mask_sse4_1;
+  if (flags & HAS_AVX2)
+    aom_blend_a64_mask = aom_blend_a64_mask_avx2;
   aom_blend_a64_vmask = aom_blend_a64_vmask_c;
   if (flags & HAS_SSE4_1)
     aom_blend_a64_vmask = aom_blend_a64_vmask_sse4_1;
diff --git a/third_party/libaom/source/config/win/x64/config/av1_rtcd.h b/third_party/libaom/source/config/win/x64/config/av1_rtcd.h
index 168825d2..df84b1b 100644
--- a/third_party/libaom/source/config/win/x64/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/win/x64/config/av1_rtcd.h
@@ -925,6 +925,75 @@
                                    int bd);
 #define av1_highbd_dr_prediction_z3 av1_highbd_dr_prediction_z3_c
 
+void av1_highbd_inv_txfm_add_c(const tran_low_t* dqcoeff,
+                               uint8_t* dst,
+                               int stride,
+                               const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_sse4_1(const tran_low_t* dqcoeff,
+                                    uint8_t* dst,
+                                    int stride,
+                                    const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_avx2(const tran_low_t* dqcoeff,
+                                  uint8_t* dst,
+                                  int stride,
+                                  const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add)(const tran_low_t* dqcoeff,
+                                            uint8_t* dst,
+                                            int stride,
+                                            const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_16x16_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_16x16_sse4_1(const tran_low_t* dqcoeff,
+                                          uint8_t* dst,
+                                          int stride,
+                                          const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_16x16)(const tran_low_t* dqcoeff,
+                                                  uint8_t* dst,
+                                                  int stride,
+                                                  const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_32x32_c(const tran_low_t* dqcoeff,
+                                     uint8_t* dst,
+                                     int stride,
+                                     const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_32x32_avx2(const tran_low_t* dqcoeff,
+                                        uint8_t* dst,
+                                        int stride,
+                                        const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_32x32)(const tran_low_t* dqcoeff,
+                                                  uint8_t* dst,
+                                                  int stride,
+                                                  const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_4x4_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_4x4_sse4_1(const tran_low_t* dqcoeff,
+                                        uint8_t* dst,
+                                        int stride,
+                                        const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_4x4)(const tran_low_t* dqcoeff,
+                                                uint8_t* dst,
+                                                int stride,
+                                                const TxfmParam* txfm_param);
+
+void av1_highbd_inv_txfm_add_8x8_c(const tran_low_t* dqcoeff,
+                                   uint8_t* dst,
+                                   int stride,
+                                   const TxfmParam* txfm_param);
+void av1_highbd_inv_txfm_add_8x8_sse4_1(const tran_low_t* dqcoeff,
+                                        uint8_t* dst,
+                                        int stride,
+                                        const TxfmParam* txfm_param);
+RTCD_EXTERN void (*av1_highbd_inv_txfm_add_8x8)(const tran_low_t* dqcoeff,
+                                                uint8_t* dst,
+                                                int stride,
+                                                const TxfmParam* txfm_param);
+
 void av1_highbd_iwht4x4_16_add_c(const tran_low_t* input,
                                  uint8_t* dest,
                                  int dest_stride,
@@ -1379,16 +1448,7 @@
                                 int stride,
                                 TX_TYPE tx_type,
                                 int bd);
-void av1_inv_txfm2d_add_64x64_sse4_1(const int32_t* input,
-                                     uint16_t* output,
-                                     int stride,
-                                     TX_TYPE tx_type,
-                                     int bd);
-RTCD_EXTERN void (*av1_inv_txfm2d_add_64x64)(const int32_t* input,
-                                             uint16_t* output,
-                                             int stride,
-                                             TX_TYPE tx_type,
-                                             int bd);
+#define av1_inv_txfm2d_add_64x64 av1_inv_txfm2d_add_64x64_c
 
 void av1_inv_txfm2d_add_8x16_c(const int32_t* input,
                                uint16_t* output,
@@ -2081,6 +2141,23 @@
     av1_highbd_convolve_y_sr = av1_highbd_convolve_y_sr_ssse3;
   if (flags & HAS_AVX2)
     av1_highbd_convolve_y_sr = av1_highbd_convolve_y_sr_avx2;
+  av1_highbd_inv_txfm_add = av1_highbd_inv_txfm_add_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add = av1_highbd_inv_txfm_add_sse4_1;
+  if (flags & HAS_AVX2)
+    av1_highbd_inv_txfm_add = av1_highbd_inv_txfm_add_avx2;
+  av1_highbd_inv_txfm_add_16x16 = av1_highbd_inv_txfm_add_16x16_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add_16x16 = av1_highbd_inv_txfm_add_16x16_sse4_1;
+  av1_highbd_inv_txfm_add_32x32 = av1_highbd_inv_txfm_add_32x32_c;
+  if (flags & HAS_AVX2)
+    av1_highbd_inv_txfm_add_32x32 = av1_highbd_inv_txfm_add_32x32_avx2;
+  av1_highbd_inv_txfm_add_4x4 = av1_highbd_inv_txfm_add_4x4_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add_4x4 = av1_highbd_inv_txfm_add_4x4_sse4_1;
+  av1_highbd_inv_txfm_add_8x8 = av1_highbd_inv_txfm_add_8x8_c;
+  if (flags & HAS_SSE4_1)
+    av1_highbd_inv_txfm_add_8x8 = av1_highbd_inv_txfm_add_8x8_sse4_1;
   av1_highbd_jnt_convolve_2d = av1_highbd_jnt_convolve_2d_c;
   if (flags & HAS_SSE4_1)
     av1_highbd_jnt_convolve_2d = av1_highbd_jnt_convolve_2d_sse4_1;
@@ -2120,9 +2197,6 @@
   av1_inv_txfm2d_add_4x4 = av1_inv_txfm2d_add_4x4_c;
   if (flags & HAS_SSE4_1)
     av1_inv_txfm2d_add_4x4 = av1_inv_txfm2d_add_4x4_sse4_1;
-  av1_inv_txfm2d_add_64x64 = av1_inv_txfm2d_add_64x64_c;
-  if (flags & HAS_SSE4_1)
-    av1_inv_txfm2d_add_64x64 = av1_inv_txfm2d_add_64x64_sse4_1;
   av1_inv_txfm2d_add_8x8 = av1_inv_txfm2d_add_8x8_c;
   if (flags & HAS_SSE4_1)
     av1_inv_txfm2d_add_8x8 = av1_inv_txfm2d_add_8x8_sse4_1;
diff --git a/third_party/tcmalloc/chromium/src/free_list.h b/third_party/tcmalloc/chromium/src/free_list.h
index 05b5128..715bb96 100644
--- a/third_party/tcmalloc/chromium/src/free_list.h
+++ b/third_party/tcmalloc/chromium/src/free_list.h
@@ -44,9 +44,6 @@
 #include "linked_list.h"
 #include "system-alloc.h"
 
-// Remove to enable singly linked lists (the default for open source tcmalloc).
-#define TCMALLOC_USE_DOUBLYLINKED_FREELIST
-
 namespace tcmalloc {
 
 #if defined(TCMALLOC_USE_DOUBLYLINKED_FREELIST)
diff --git a/third_party/tcmalloc/chromium/src/gperftools/tcmalloc.h b/third_party/tcmalloc/chromium/src/gperftools/tcmalloc.h
index 78b49d5..91b32ae 100644
--- a/third_party/tcmalloc/chromium/src/gperftools/tcmalloc.h
+++ b/third_party/tcmalloc/chromium/src/gperftools/tcmalloc.h
@@ -68,6 +68,12 @@
 
 #endif
 
+// When passed to mallopt() as first argument causes it to return
+// TC_MALLOPT_IS_OVERRIDDEN_BY_TCMALLOC. Used to detect the sanity of the
+// overriding mechanisms at runtime.
+#define TC_MALLOPT_IS_OVERRIDDEN_BY_TCMALLOC 0xbeef42
+
+// Annoying stuff for windows -- makes sure clients can import these functions
 #ifndef PERFTOOLS_DLL_DECL
 #ifdef _WIN32
 #define PERFTOOLS_DLL_DECL __declspec(dllimport)
diff --git a/third_party/tcmalloc/chromium/src/tcmalloc.cc b/third_party/tcmalloc/chromium/src/tcmalloc.cc
index 31129bd7..6c5bd8b 100644
--- a/third_party/tcmalloc/chromium/src/tcmalloc.cc
+++ b/third_party/tcmalloc/chromium/src/tcmalloc.cc
@@ -135,9 +135,6 @@
 
 #include "maybe_emergency_malloc.h"
 
-// Remove to enable huge allocations (> 2GB), the default for gperftools.
-#define TCMALLOC_DISABLE_HUGE_ALLOCATIONS
-
 #if (defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)) && !defined(WIN32_OVERRIDE_ALLOCATORS)
 # define WIN32_DO_PATCHING 1
 #endif
@@ -1661,7 +1658,13 @@
 }
 
 inline int do_mallopt(int cmd, int value) {
-  return 1;     // Indicates error
+  if (cmd == TC_MALLOPT_IS_OVERRIDDEN_BY_TCMALLOC)
+    return TC_MALLOPT_IS_OVERRIDDEN_BY_TCMALLOC;
+
+  // 1 is the success return value according to man mallopt(). However (see the
+  // BUGS section in the manpage), most implementations return always 1.
+  // This code is just complying with that (buggy) expectation.
+  return 1;
 }
 
 #ifdef HAVE_STRUCT_MALLINFO
diff --git a/third_party/tcmalloc/gperftools-2.0/chromium/src/free_list.h b/third_party/tcmalloc/gperftools-2.0/chromium/src/free_list.h
index a5b5a063..5acafdb 100644
--- a/third_party/tcmalloc/gperftools-2.0/chromium/src/free_list.h
+++ b/third_party/tcmalloc/gperftools-2.0/chromium/src/free_list.h
@@ -44,9 +44,6 @@
 #include "linked_list.h"
 #include "system-alloc.h"
 
-// Remove to enable singly linked lists (the default for open source tcmalloc).
-#define TCMALLOC_USE_DOUBLYLINKED_FREELIST
-
 namespace tcmalloc {
 
 #if defined(TCMALLOC_USE_DOUBLYLINKED_FREELIST)
diff --git a/tools/binary_size/diagnose_bloat.py b/tools/binary_size/diagnose_bloat.py
index ed69755..61cb12b3 100755
--- a/tools/binary_size/diagnose_bloat.py
+++ b/tools/binary_size/diagnose_bloat.py
@@ -313,7 +313,6 @@
 
   def _GenGnCmd(self):
     gn_args = 'is_official_build=true'
-    gn_args += ' symbol_level=0'
     # Variables often become unused when experimenting with macros to reduce
     # size, so don't fail on warnings.
     gn_args += ' treat_warnings_as_errors=false'
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 3a4d4b2..d9a889f 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -35,7 +35,7 @@
   CLANG_REVISION = 'HEAD'
 
 # This is incremented when pushing a new build of Clang at the same revision.
-CLANG_SUB_REVISION=2
+CLANG_SUB_REVISION=1
 
 PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION)
 
@@ -307,6 +307,18 @@
     f.write('endif (CHROMIUM_TOOLS_SRC)\n')
 
 
+def DownloadHostGcc(args):
+  """Downloads gcc 4.8.5 and makes sure args.gcc_toolchain is set."""
+  if not sys.platform.startswith('linux') or args.gcc_toolchain:
+    return
+  gcc_dir = os.path.join(LLVM_BUILD_TOOLS_DIR, 'gcc485precise')
+  if not os.path.exists(gcc_dir):
+    print 'Downloading pre-built GCC 4.8.5...'
+    DownloadAndUnpack(
+        CDS_URL + '/tools/gcc485precise.tgz', LLVM_BUILD_TOOLS_DIR)
+  args.gcc_toolchain = gcc_dir
+
+
 def AddSvnToPathOnWin():
   """Download svn.exe and add it to PATH."""
   if sys.platform != 'win32':
@@ -489,6 +501,7 @@
       print 'Removing old lib dir: %s' % old_lib_dir
       RmTree(old_lib_dir)
 
+  DownloadHostGcc(args)
   AddCMakeToPath(args)
   AddGnuWinToPath()
 
@@ -501,6 +514,21 @@
 
   cc, cxx = None, None
   libstdcpp = None
+  if args.gcc_toolchain:  # This option is only used on Linux.
+    # Use the specified gcc installation for building.
+    cc = os.path.join(args.gcc_toolchain, 'bin', 'gcc')
+    cxx = os.path.join(args.gcc_toolchain, 'bin', 'g++')
+
+    if not os.access(cc, os.X_OK):
+      print 'Invalid --gcc-toolchain: "%s"' % args.gcc_toolchain
+      print '"%s" does not appear to be valid.' % cc
+      return 1
+
+    # Set LD_LIBRARY_PATH to make auxiliary targets (tablegen, bootstrap
+    # compiler, etc.) find the .so.
+    libstdcpp = subprocess.check_output(
+        [cxx, '-print-file-name=libstdc++.so.6']).rstrip()
+    os.environ['LD_LIBRARY_PATH'] = os.path.dirname(libstdcpp)
 
   cflags = []
   cxxflags = []
@@ -550,6 +578,11 @@
       cc = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'clang')
       cxx = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'clang++')
 
+    if args.gcc_toolchain:
+      # Tell the bootstrap compiler to use a specific gcc prefix to search
+      # for standard library headers and shared object files.
+      cflags = ['--gcc-toolchain=' + args.gcc_toolchain]
+      cxxflags = ['--gcc-toolchain=' + args.gcc_toolchain]
     print 'Building final compiler'
 
   # LLVM uses C++11 starting in llvm 3.5. On Linux, this means libstdc++4.7+ is
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index e627168..c05f5f69 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -365,7 +365,9 @@
       'Android Compile Perf': 'official_goma_minimal_symbols_android',
       'Android arm64 Compile Perf': 'official_goma_minimal_symbols_android_arm64',
       'android-builder-perf': 'official_goma_minimal_symbols_android',
+      # TODO(crbug.com/828467): Remove 'Linux Builder Perf'
       'Linux Builder Perf': 'official_goma_perf',
+      'linux-builder-perf': 'official_goma_perf',
       'Mac Builder Perf': 'official_goma',
       # TODO(crbug.com/828472): Remove 'Win Builder Perf'
       'Win Builder Perf': 'official_goma_x86',
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 2dadca2..ef680cf 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -27713,7 +27713,7 @@
   <int value="0" label="Load succeeded"/>
   <int value="1" label="Load failed - Invalid path"/>
   <int value="2" label="Load failed - File read error"/>
-  <int value="3" label="Load failed - Ruleset verification error"/>
+  <int value="3" label="Load failed - Checksum mismatch"/>
   <int value="4" label="Load failed - Version mismatch"/>
 </enum>
 
@@ -39721,7 +39721,7 @@
   <int value="8" label="HEADERS_AND_FOOTERS"/>
   <int value="9" label="CSS_BACKGROUND"/>
   <int value="10" label="SELECTION_ONLY"/>
-  <int value="11" label="EXTERNAL_PDF_PREVIEW"/>
+  <int value="11" label="EXTERNAL_PDF_PREVIEW_UNUSED"/>
   <int value="12" label="PAGE_RANGE"/>
   <int value="13" label="DEFAULT_MEDIA"/>
   <int value="14" label="NON_DEFAULT_MEDIA"/>
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn
index 8c5bf69..610662d 100644
--- a/ui/aura/BUILD.gn
+++ b/ui/aura/BUILD.gn
@@ -169,7 +169,7 @@
     "//services/service_manager/public/cpp",
     "//services/ui/common:mus_common",
     "//services/ui/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//ui/base",
     "//ui/base/ime",
@@ -289,7 +289,7 @@
     "//components/viz/test:test_support",
     "//services/service_manager/public/cpp",
     "//services/ui/public/cpp/input_devices",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//testing/gtest",
     "//ui/base:test_support",
diff --git a/ui/aura/DEPS b/ui/aura/DEPS
index a6548b4..d35921d 100644
--- a/ui/aura/DEPS
+++ b/ui/aura/DEPS
@@ -12,7 +12,7 @@
   "+services/service_manager/public/cpp",
   "+services/service_manager/public/mojom",
   "+services/ui/common",
-  "+services/ui/public/interfaces",
+  "+services/ws/public/mojom",
   "+skia/ext",
   "+third_party/skia",
   "+ui/base",
diff --git a/ui/aura/client/aura_constants.cc b/ui/aura/client/aura_constants.cc
index cc951f6..2db8df97 100644
--- a/ui/aura/client/aura_constants.cc
+++ b/ui/aura/client/aura_constants.cc
@@ -4,7 +4,7 @@
 
 #include "ui/aura/client/aura_constants.h"
 
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/base/class_property.h"
 #include "ui/gfx/geometry/rect.h"
 
diff --git a/ui/aura/env.cc b/ui/aura/env.cc
index d55f03a6..bb7fa75 100644
--- a/ui/aura/env.cc
+++ b/ui/aura/env.cc
@@ -6,7 +6,7 @@
 
 #include "base/command_line.h"
 #include "base/lazy_instance.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/env_input_state_controller.h"
 #include "ui/aura/env_observer.h"
diff --git a/ui/aura/event_injector.cc b/ui/aura/event_injector.cc
index 86153a39..d8a6189 100644
--- a/ui/aura/event_injector.cc
+++ b/ui/aura/event_injector.cc
@@ -5,7 +5,7 @@
 #include "ui/aura/event_injector.h"
 
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/aura/env.h"
 #include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/window.h"
diff --git a/ui/aura/event_injector.h b/ui/aura/event_injector.h
index bc336047f..52c17e5b 100644
--- a/ui/aura/event_injector.h
+++ b/ui/aura/event_injector.h
@@ -5,7 +5,7 @@
 #ifndef UI_AURA_EVENT_INJECTOR_H_
 #define UI_AURA_EVENT_INJECTOR_H_
 
-#include "services/ui/public/interfaces/event_injector.mojom.h"
+#include "services/ws/public/mojom/event_injector.mojom.h"
 #include "ui/aura/aura_export.h"
 
 namespace ui {
diff --git a/ui/aura/hit_test_data_provider_aura.cc b/ui/aura/hit_test_data_provider_aura.cc
index 7b5463a..12c9a3c 100644
--- a/ui/aura/hit_test_data_provider_aura.cc
+++ b/ui/aura/hit_test_data_provider_aura.cc
@@ -6,7 +6,7 @@
 
 #include "base/containers/adapters.h"
 #include "components/viz/common/hit_test/hit_test_region_list.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_targeter.h"
 
diff --git a/ui/aura/mus/DEPS b/ui/aura/mus/DEPS
index 9e4ff45..da03f65 100644
--- a/ui/aura/mus/DEPS
+++ b/ui/aura/mus/DEPS
@@ -15,6 +15,7 @@
   "+services/ui/common/accelerator_util.h",
   "+services/ui/common/task_runner_test_base.h",
   "+services/ui/public",
+  "+services/ws/public",
   "+ui/gl/gl_bindings.h",
 ]
 
diff --git a/ui/aura/mus/capture_synchronizer.cc b/ui/aura/mus/capture_synchronizer.cc
index 20234b5..c04f6588 100644
--- a/ui/aura/mus/capture_synchronizer.cc
+++ b/ui/aura/mus/capture_synchronizer.cc
@@ -5,7 +5,7 @@
 #include "ui/aura/mus/capture_synchronizer.h"
 
 #include "base/auto_reset.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/mus/capture_synchronizer_delegate.h"
 #include "ui/aura/mus/window_mus.h"
diff --git a/ui/aura/mus/drag_drop_controller_mus.cc b/ui/aura/mus/drag_drop_controller_mus.cc
index 276734f..aca8c9d 100644
--- a/ui/aura/mus/drag_drop_controller_mus.cc
+++ b/ui/aura/mus/drag_drop_controller_mus.cc
@@ -11,8 +11,8 @@
 #include "base/auto_reset.h"
 #include "base/run_loop.h"
 #include "mojo/public/cpp/bindings/map.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/drag_drop_client_observer.h"
 #include "ui/aura/client/drag_drop_delegate.h"
 #include "ui/aura/env.h"
diff --git a/ui/aura/mus/focus_synchronizer.cc b/ui/aura/mus/focus_synchronizer.cc
index 4638ce4..8d4a349 100644
--- a/ui/aura/mus/focus_synchronizer.cc
+++ b/ui/aura/mus/focus_synchronizer.cc
@@ -5,7 +5,7 @@
 #include "ui/aura/mus/focus_synchronizer.h"
 
 #include "base/auto_reset.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/focus_client.h"
 #include "ui/aura/mus/focus_synchronizer_delegate.h"
diff --git a/ui/aura/mus/input_method_mus.cc b/ui/aura/mus/input_method_mus.cc
index 030a132d..017ef18 100644
--- a/ui/aura/mus/input_method_mus.cc
+++ b/ui/aura/mus/input_method_mus.cc
@@ -6,9 +6,9 @@
 
 #include <utility>
 
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/mus/input_method_mus_delegate.h"
 #include "ui/aura/mus/text_input_client_impl.h"
 #include "ui/base/ime/text_input_client.h"
diff --git a/ui/aura/mus/input_method_mus.h b/ui/aura/mus/input_method_mus.h
index 207aa202..c7c5cc79 100644
--- a/ui/aura/mus/input_method_mus.h
+++ b/ui/aura/mus/input_method_mus.h
@@ -9,7 +9,7 @@
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 #include "ui/aura/aura_export.h"
 #include "ui/base/ime/input_method_base.h"
 
diff --git a/ui/aura/mus/input_method_mus_delegate.h b/ui/aura/mus/input_method_mus_delegate.h
index 789c5900..22df549 100644
--- a/ui/aura/mus/input_method_mus_delegate.h
+++ b/ui/aura/mus/input_method_mus_delegate.h
@@ -5,7 +5,7 @@
 #ifndef UI_AURA_MUS_INPUT_METHOD_MUS_DELEGATE_H_
 #define UI_AURA_MUS_INPUT_METHOD_MUS_DELEGATE_H_
 
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 #include "ui/aura/aura_export.h"
 
 namespace aura {
diff --git a/ui/aura/mus/input_method_mus_unittest.cc b/ui/aura/mus/input_method_mus_unittest.cc
index c448bff6a..747accd 100644
--- a/ui/aura/mus/input_method_mus_unittest.cc
+++ b/ui/aura/mus/input_method_mus_unittest.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 #include "ui/aura/test/aura_test_base.h"
 #include "ui/aura/test/mus/input_method_mus_test_api.h"
 #include "ui/base/ime/dummy_text_input_client.h"
diff --git a/ui/aura/mus/mus_context_factory.h b/ui/aura/mus/mus_context_factory.h
index 08340f3..fa61c409 100644
--- a/ui/aura/mus/mus_context_factory.h
+++ b/ui/aura/mus/mus_context_factory.h
@@ -13,7 +13,7 @@
 #include "components/viz/common/display/renderer_settings.h"
 #include "components/viz/common/gpu/context_provider.h"
 #include "services/ui/public/cpp/raster_thread_helper.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/aura_export.h"
 #include "ui/compositor/compositor.h"
 
diff --git a/ui/aura/mus/property_converter.cc b/ui/aura/mus/property_converter.cc
index 39eddf4..774dd79 100644
--- a/ui/aura/mus/property_converter.cc
+++ b/ui/aura/mus/property_converter.cc
@@ -7,8 +7,8 @@
 #include "base/unguessable_token.h"
 #include "mojo/public/cpp/bindings/type_converter.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/base/class_property.h"
 
diff --git a/ui/aura/mus/property_utils.cc b/ui/aura/mus/property_utils.cc
index a23bff6..f7241e3 100644
--- a/ui/aura/mus/property_utils.cc
+++ b/ui/aura/mus/property_utils.cc
@@ -5,8 +5,8 @@
 #include "ui/aura/mus/property_utils.h"
 
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/window_types.h"
 #include "ui/aura/window.h"
diff --git a/ui/aura/mus/system_input_injector_mus.cc b/ui/aura/mus/system_input_injector_mus.cc
index 7402b740..4c3cda7a 100644
--- a/ui/aura/mus/system_input_injector_mus.cc
+++ b/ui/aura/mus/system_input_injector_mus.cc
@@ -5,7 +5,7 @@
 #include "ui/aura/mus/system_input_injector_mus.h"
 
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
 
diff --git a/ui/aura/mus/system_input_injector_mus.h b/ui/aura/mus/system_input_injector_mus.h
index 01ea4ab1..597301a 100644
--- a/ui/aura/mus/system_input_injector_mus.h
+++ b/ui/aura/mus/system_input_injector_mus.h
@@ -5,7 +5,7 @@
 #ifndef UI_AURA_MUS_SYSTEM_INPUT_INJECTOR_MUS_H_
 #define UI_AURA_MUS_SYSTEM_INPUT_INJECTOR_MUS_H_
 
-#include "services/ui/public/interfaces/remoting_event_injector.mojom.h"
+#include "services/ws/public/mojom/remoting_event_injector.mojom.h"
 #include "ui/aura/aura_export.h"
 #include "ui/events/event_modifiers.h"
 #include "ui/events/system_input_injector.h"
diff --git a/ui/aura/mus/text_input_client_impl.cc b/ui/aura/mus/text_input_client_impl.cc
index 514a69a..d7dadcf 100644
--- a/ui/aura/mus/text_input_client_impl.cc
+++ b/ui/aura/mus/text_input_client_impl.cc
@@ -5,7 +5,7 @@
 #include "ui/aura/mus/text_input_client_impl.h"
 
 #include "base/strings/utf_string_conversions.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 #include "ui/aura/mus/input_method_mus.h"
 #include "ui/base/ime/text_input_client.h"
 
diff --git a/ui/aura/mus/text_input_client_impl.h b/ui/aura/mus/text_input_client_impl.h
index 5f5b98d..135f2996 100644
--- a/ui/aura/mus/text_input_client_impl.h
+++ b/ui/aura/mus/text_input_client_impl.h
@@ -6,7 +6,7 @@
 #define UI_AURA_MUS_TEXT_INPUT_CLIENT_IMPL_H_
 
 #include "mojo/public/cpp/bindings/binding.h"
-#include "services/ui/public/interfaces/ime/ime.mojom.h"
+#include "services/ws/public/mojom/ime/ime.mojom.h"
 #include "ui/base/ime/composition_text.h"
 #include "ui/base/ime/input_method_delegate.h"
 
diff --git a/ui/aura/mus/user_activity_forwarder.h b/ui/aura/mus/user_activity_forwarder.h
index 58d2852..c9d5ccb6 100644
--- a/ui/aura/mus/user_activity_forwarder.h
+++ b/ui/aura/mus/user_activity_forwarder.h
@@ -8,7 +8,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "services/ui/public/interfaces/user_activity_monitor.mojom.h"
+#include "services/ws/public/mojom/user_activity_monitor.mojom.h"
 #include "ui/aura/aura_export.h"
 
 namespace ui {
diff --git a/ui/aura/mus/user_activity_forwarder_unittest.cc b/ui/aura/mus/user_activity_forwarder_unittest.cc
index 249cc0a..262859f 100644
--- a/ui/aura/mus/user_activity_forwarder_unittest.cc
+++ b/ui/aura/mus/user_activity_forwarder_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/time/time.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "services/ui/common/task_runner_test_base.h"
-#include "services/ui/public/interfaces/user_activity_monitor.mojom.h"
+#include "services/ws/public/mojom/user_activity_monitor.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/user_activity/user_activity_detector.h"
 
diff --git a/ui/aura/mus/window_mus.h b/ui/aura/mus/window_mus.h
index ade1846..f36eb725 100644
--- a/ui/aura/mus/window_mus.h
+++ b/ui/aura/mus/window_mus.h
@@ -10,7 +10,7 @@
 #include <string>
 #include <vector>
 
-#include "services/ui/public/interfaces/cursor/cursor.mojom.h"
+#include "services/ws/public/mojom/cursor/cursor.mojom.h"
 #include "ui/aura/aura_export.h"
 #include "ui/aura/mus/mus_types.h"
 
diff --git a/ui/aura/mus/window_port_mus.h b/ui/aura/mus/window_port_mus.h
index aeebd6ff..4a361b7 100644
--- a/ui/aura/mus/window_port_mus.h
+++ b/ui/aura/mus/window_port_mus.h
@@ -16,9 +16,9 @@
 #include "base/optional.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/common/surfaces/surface_info.h"
-#include "services/ui/public/interfaces/cursor/cursor.mojom.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/cursor/cursor.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/aura_export.h"
 #include "ui/aura/mus/mus_types.h"
 #include "ui/aura/mus/window_mus.h"
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index fd2d3f5b..e0d498f 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -26,7 +26,7 @@
 #include "services/ui/common/util.h"
 #include "services/ui/public/cpp/gpu/gpu.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/drag_drop_client.h"
 #include "ui/aura/client/transient_window_client.h"
diff --git a/ui/aura/mus/window_tree_client.h b/ui/aura/mus/window_tree_client.h
index 1ac3147..e3c2245 100644
--- a/ui/aura/mus/window_tree_client.h
+++ b/ui/aura/mus/window_tree_client.h
@@ -23,8 +23,8 @@
 #include "base/single_thread_task_runner.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "services/ui/public/interfaces/screen_provider_observer.mojom.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/screen_provider_observer.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/aura_export.h"
 #include "ui/aura/client/transient_window_client_observer.h"
 #include "ui/aura/mus/capture_synchronizer_delegate.h"
diff --git a/ui/aura/mus/window_tree_client_delegate.h b/ui/aura/mus/window_tree_client_delegate.h
index d1746b6..59b19f8 100644
--- a/ui/aura/mus/window_tree_client_delegate.h
+++ b/ui/aura/mus/window_tree_client_delegate.h
@@ -12,8 +12,8 @@
 #include <vector>
 
 #include "services/service_manager/public/mojom/interface_provider.mojom.h"
-#include "services/ui/public/interfaces/screen_provider_observer.mojom.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/screen_provider_observer.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/aura_export.h"
 
 namespace aura {
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc
index 8387522..e32a3a7 100644
--- a/ui/aura/mus/window_tree_client_unittest.cc
+++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -16,7 +16,7 @@
 #include "components/viz/common/surfaces/surface_info.h"
 #include "mojo/public/cpp/bindings/map.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/capture_client.h"
diff --git a/ui/aura/mus/window_tree_host_mus.h b/ui/aura/mus/window_tree_host_mus.h
index 51392d4..09b3b0a 100644
--- a/ui/aura/mus/window_tree_host_mus.h
+++ b/ui/aura/mus/window_tree_host_mus.h
@@ -12,7 +12,7 @@
 
 #include "base/macros.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/aura_export.h"
 #include "ui/aura/mus/input_method_mus_delegate.h"
 #include "ui/aura/window_tree_host_platform.h"
diff --git a/ui/aura/mus/window_tree_host_mus_init_params.cc b/ui/aura/mus/window_tree_host_mus_init_params.cc
index 6c876d4..215396a 100644
--- a/ui/aura/mus/window_tree_host_mus_init_params.cc
+++ b/ui/aura/mus/window_tree_host_mus_init_params.cc
@@ -5,7 +5,7 @@
 #include "ui/aura/mus/window_tree_host_mus_init_params.h"
 
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/mus/window_port_mus.h"
 #include "ui/aura/mus/window_tree_client.h"
 #include "ui/display/display.h"
diff --git a/ui/aura/mus/window_tree_host_mus_init_params.h b/ui/aura/mus/window_tree_host_mus_init_params.h
index 1793bbd..5a7b8c7 100644
--- a/ui/aura/mus/window_tree_host_mus_init_params.h
+++ b/ui/aura/mus/window_tree_host_mus_init_params.h
@@ -13,7 +13,7 @@
 #include <vector>
 
 #include "components/viz/common/surfaces/frame_sink_id.h"
-#include "services/ui/public/interfaces/window_manager_constants.mojom.h"
+#include "services/ws/public/mojom/window_manager_constants.mojom.h"
 #include "ui/aura/aura_export.h"
 
 namespace aura {
diff --git a/ui/aura/test/mus/test_window_tree.h b/ui/aura/test/mus/test_window_tree.h
index 293c076..f8d28290 100644
--- a/ui/aura/test/mus/test_window_tree.h
+++ b/ui/aura/test/mus/test_window_tree.h
@@ -11,7 +11,7 @@
 
 #include "base/macros.h"
 #include "services/ui/common/types.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/mus/mus_types.h"
 
 namespace aura {
diff --git a/ui/aura/test/mus/window_tree_client_private.h b/ui/aura/test/mus/window_tree_client_private.h
index 43600c94..cde70cfa 100644
--- a/ui/aura/test/mus/window_tree_client_private.h
+++ b/ui/aura/test/mus/window_tree_client_private.h
@@ -10,7 +10,7 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/mus/mus_types.h"
 #include "ui/aura/mus/window_tree_client.h"
 
diff --git a/ui/aura/test/ui_controls_factory_ozone.cc b/ui/aura/test/ui_controls_factory_ozone.cc
index 934d828..2c1e77c 100644
--- a/ui/aura/test/ui_controls_factory_ozone.cc
+++ b/ui/aura/test/ui_controls_factory_ozone.cc
@@ -9,8 +9,8 @@
 #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/event_injector.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/event_injector.mojom.h"
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/mus/window_tree_client.h"
diff --git a/ui/aura/window.cc b/ui/aura/window.cc
index be708fc..6683d19 100644
--- a/ui/aura/window.cc
+++ b/ui/aura/window.cc
@@ -19,7 +19,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "cc/trees/layer_tree_frame_sink.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/client/cursor_client.h"
diff --git a/ui/aura/window_event_dispatcher.cc b/ui/aura/window_event_dispatcher.cc
index 46ebe7f..a5cd2e0 100644
--- a/ui/aura/window_event_dispatcher.cc
+++ b/ui/aura/window_event_dispatcher.cc
@@ -11,7 +11,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/event_client.h"
diff --git a/ui/aura/window_event_dispatcher_unittest.cc b/ui/aura/window_event_dispatcher_unittest.cc
index a2d54866..585a7e3 100644
--- a/ui/aura/window_event_dispatcher_unittest.cc
+++ b/ui/aura/window_event_dispatcher_unittest.cc
@@ -17,7 +17,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/client/event_client.h"
diff --git a/ui/aura/window_targeter.cc b/ui/aura/window_targeter.cc
index 824159b..f19441f 100644
--- a/ui/aura/window_targeter.cc
+++ b/ui/aura/window_targeter.cc
@@ -4,7 +4,7 @@
 
 #include "ui/aura/window_targeter.h"
 
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/client/event_client.h"
 #include "ui/aura/client/focus_client.h"
diff --git a/ui/aura/window_unittest.cc b/ui/aura/window_unittest.cc
index 40bb14c5..7ae11eeb 100644
--- a/ui/aura/window_unittest.cc
+++ b/ui/aura/window_unittest.cc
@@ -17,7 +17,7 @@
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "cc/trees/layer_tree_frame_sink.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/client/focus_change_observer.h"
diff --git a/ui/chromeos/BUILD.gn b/ui/chromeos/BUILD.gn
index 844d4384..0aa67a24 100644
--- a/ui/chromeos/BUILD.gn
+++ b/ui/chromeos/BUILD.gn
@@ -35,7 +35,7 @@
     "//components/onc",
     "//mojo/public/cpp/bindings",
     "//services/ui/public/cpp",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//ui/aura",
     "//ui/base",
diff --git a/ui/chromeos/DEPS b/ui/chromeos/DEPS
index e70987f..dcedf5f 100644
--- a/ui/chromeos/DEPS
+++ b/ui/chromeos/DEPS
@@ -6,7 +6,7 @@
   "+grit/ui_chromeos_strings.h",
   "+mojo/public/cpp/bindings",
   "+services/ui/public/cpp",
-  "+services/ui/public/interfaces",
+  "+services/ws/public/mojom",
   "+third_party/cros_system_api",
   "+third_party/skia",
   "+ui/accessibility",
diff --git a/ui/chromeos/ime/candidate_window_view.cc b/ui/chromeos/ime/candidate_window_view.cc
index 4a504ccf..e00687b3 100644
--- a/ui/chromeos/ime/candidate_window_view.cc
+++ b/ui/chromeos/ime/candidate_window_view.cc
@@ -12,7 +12,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "mojo/public/cpp/bindings/type_converter.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/chromeos/ime/candidate_view.h"
 #include "ui/chromeos/ime/candidate_window_constants.h"
 #include "ui/display/display.h"
diff --git a/ui/events/blink/web_input_event.cc b/ui/events/blink/web_input_event.cc
index 7acf014..e693fc2e 100644
--- a/ui/events/blink/web_input_event.cc
+++ b/ui/events/blink/web_input_event.cc
@@ -502,6 +502,8 @@
 
   if (event.flags() & ui::EF_PRECISION_SCROLLING_DELTA)
     webkit_event.has_precise_scrolling_deltas = true;
+  if (event.flags() & ui::EF_SCROLL_BY_PAGE)
+    webkit_event.scroll_by_page = true;
 
   webkit_event.wheel_ticks_x =
       webkit_event.delta_x / MouseWheelEvent::kWheelDelta;
diff --git a/ui/events/event_constants.h b/ui/events/event_constants.h
index e2c373e..ee1a4ce 100644
--- a/ui/events/event_constants.h
+++ b/ui/events/event_constants.h
@@ -153,6 +153,9 @@
   EF_PRECISION_SCROLLING_DELTA =     // Indicates this mouse event is from high
   1 << 21,                           // precision touchpad and will come with a
                                      // high precision delta.
+  EF_SCROLL_BY_PAGE = 1 << 22,       // Indicates this mouse event is generated
+                                     // when users is requesting to scroll by
+                                     // pages.
 };
 
 // Result of dispatching an event.
diff --git a/ui/events/mojo/DEPS b/ui/events/mojo/DEPS
index c58363c..c5cf7e45a 100644
--- a/ui/events/mojo/DEPS
+++ b/ui/events/mojo/DEPS
@@ -1,6 +1,6 @@
 include_rules = [
   "+mojo/public",
-  "+services/ui/public/interfaces",
+  "+services/ws/public/mojom",
   "+ui/events",
   "+ui/latency/mojo",
 ]
diff --git a/ui/file_manager/file_manager/background/js/launcher_search.js b/ui/file_manager/file_manager/background/js/launcher_search.js
index 590d8c0..f7dca40 100644
--- a/ui/file_manager/file_manager/background/js/launcher_search.js
+++ b/ui/file_manager/file_manager/background/js/launcher_search.js
@@ -160,8 +160,8 @@
             undefined, /* App ID */
             LaunchType.FOCUS_SAME_OR_CREATE);
       } else {
-        entry = util.toFilesAppEntry(entry);
-        if (entry.type_name === undefined) {
+        // getFileTasks supports only native entries.
+        if (!util.isNativeEntry(entry)) {
           return;
         }
         // If the file is not directory, try to execute default task.
diff --git a/ui/file_manager/file_manager/background/js/test_util_base.js b/ui/file_manager/file_manager/background/js/test_util_base.js
index e37f0ff..cdaba39 100644
--- a/ui/file_manager/file_manager/background/js/test_util_base.js
+++ b/ui/file_manager/file_manager/background/js/test_util_base.js
@@ -538,6 +538,22 @@
 };
 
 /**
+ * Opens the file URL. It emulates the interaction that Launcher search does
+ * from a search result, it triggers the background page's event listener that
+ * listens to evens from launcher_search_provider API.
+ *
+ * @param {string} fileURL File URL to open by Files app background dialog.
+ * @suppress {accessControls|missingProperties} Closure disallow calling private
+ * launcherSearch_, but here we just want to emulate the behaviour, so we don't
+ * need to make this attribute public. Also the interface
+ * "FileBrowserBackground" doesn't define the attributes "launcherSearch_" so we
+ * need to suppress missingProperties.
+ */
+test.util.sync.launcherSearchOpenResult = function(fileURL) {
+  window.background.launcherSearch_.onOpenResult_(fileURL);
+};
+
+/**
  * Gets file entries just under the volume.
  *
  * @param {VolumeManagerCommon.VolumeType} volumeType Volume type.
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js
index 36bd4f4..86e07ec 100644
--- a/ui/file_manager/file_manager/common/js/util.js
+++ b/ui/file_manager/file_manager/common/js/util.js
@@ -1475,3 +1475,17 @@
 util.toFilesAppEntry = function(entry) {
   return /** @type {FilesAppEntry} */ (entry);
 };
+
+/**
+ * Returns true if entry is FileSystemEntry or FileSystemDirectoryEntry, it
+ * returns false if it's FakeEntry or any one of the FilesAppEntry types.
+ * TODO(lucmult): Wrap Entry in a FilesAppEntry derived class and remove
+ * this function. https://crbug.com/835203.
+ * @param {Entry|FilesAppEntry|FakeEntry} entry
+ * @return {boolean}
+ */
+util.isNativeEntry = function(entry) {
+  entry = util.toFilesAppEntry(entry);
+  // Only FilesAppEntry types has |type_name| attribute.
+  return entry.type_name === undefined;
+};
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index 14755c9..13d6cc7 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -831,9 +831,16 @@
        * @param {!CommandHandlerDeps} fileManager CommandHandlerDeps to use.
        */
       canExecute: function(event, fileManager) {
-        event.canExecute =
+        var hasAndroidFilesVolumeInfo =
             !!fileManager.volumeManager.getCurrentProfileVolumeInfo(
                 VolumeManagerCommon.VolumeType.ANDROID_FILES);
+        var currentRootType = fileManager.directoryModel.getCurrentRootType();
+        var isInMyFiles =
+            currentRootType == VolumeManagerCommon.RootType.MY_FILES ||
+            currentRootType == VolumeManagerCommon.RootType.DOWNLOADS ||
+            currentRootType == VolumeManagerCommon.RootType.CROSTINI ||
+            currentRootType == VolumeManagerCommon.RootType.ANDROID_FILES;
+        event.canExecute = hasAndroidFilesVolumeInfo && isInMyFiles;
         event.command.setHidden(!event.canExecute);
         event.command.checked =
             fileManager.fileFilter.isAllAndroidFoldersVisible();
diff --git a/ui/file_manager/file_manager/foreground/js/file_tasks.js b/ui/file_manager/file_manager/foreground/js/file_tasks.js
index 391a53a..8a79bb7 100644
--- a/ui/file_manager/file_manager/foreground/js/file_tasks.js
+++ b/ui/file_manager/file_manager/foreground/js/file_tasks.js
@@ -168,12 +168,8 @@
     volumeManager, metadataModel, directoryModel, ui, entries, mimeTypes,
     taskHistory) {
   var tasksPromise = new Promise(function(fulfill) {
-    // Filter entries to only contain native entries, only FilesAppEntry types
-    // have type_name property defined.
-    entries = entries.filter(entry => {
-      entry = util.toFilesAppEntry(entry);
-      return entry.type_name === undefined;
-    });
+    // getFileTasks supports only native entries.
+    entries = entries.filter(util.isNativeEntry);
     if (entries.length === 0) {
       fulfill([]);
       return;
diff --git a/ui/file_manager/gallery/js/BUILD.gn b/ui/file_manager/gallery/js/BUILD.gn
index 85460c65..02347f8e 100644
--- a/ui/file_manager/gallery/js/BUILD.gn
+++ b/ui/file_manager/gallery/js/BUILD.gn
@@ -126,6 +126,15 @@
   externs_list = [ "../../externs/entry_location.js" ]
 }
 
+js_library("gallery_item_unittest") {
+  deps = [
+    ":gallery_item",
+    ":mock_gallery_item",
+    "../../file_manager/common/js:unittest_util",
+    "//ui/webui/resources/js:webui_resource_test",
+  ]
+}
+
 js_library("gallery_util") {
   deps = [
     "../../file_manager/common/js:file_type",
@@ -221,6 +230,7 @@
     ":dimmable_ui_controller_unittest",
     ":entry_list_watcher_unittest",
     ":gallery_data_model_unittest",
+    ":gallery_item_unittest",
     ":gallery_util_unittest",
     ":ribbon_unittest",
     ":slide_mode_unittest",
diff --git a/ui/file_manager/gallery/js/gallery_item_unittest.html b/ui/file_manager/gallery/js/gallery_item_unittest.html
deleted file mode 100644
index f89d886..0000000
--- a/ui/file_manager/gallery/js/gallery_item_unittest.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<!-- Copyright 2015 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.
-  -->
-<script>
-// Define mock Gallery class to define GalleryItem class.
-var Gallery = function() {};
-</script>
-<!-- Should be loaded before volume_manager.js -->
-<script src="../../file_manager/common/js/volume_manager_common.js"></script>
-<!-- Others -->
-<script src="../../../webui/resources/js/assert.js"></script>
-<script src="../../../webui/resources/js/cr.js"></script>
-<script src="../../../webui/resources/js/cr/event_target.js"></script>
-<script src="../../../webui/resources/js/cr/ui/array_data_model.js"></script>
-<script src="../../../webui/resources/js/load_time_data.js"></script>
-<script src="../../file_manager/common/js/file_type.js"></script>
-<script src="../../file_manager/common/js/mock_entry.js"></script>
-<script src="../../file_manager/common/js/unittest_util.js"></script>
-<script src="../../file_manager/common/js/util.js"></script>
-<script src="../../file_manager/foreground/js/metadata/thumbnail_model.js"></script>
-<script src="../../file_manager/foreground/js/metadata/mock_metadata.js"></script>
-<script src="gallery_item.js"></script>
-<script src="gallery_util.js"></script>
-
-<script src="gallery_item_unittest.js"></script>
diff --git a/ui/file_manager/gallery/js/gallery_item_unittest.js b/ui/file_manager/gallery/js/gallery_item_unittest.js
index fc82a8b..a3e14d86 100644
--- a/ui/file_manager/gallery/js/gallery_item_unittest.js
+++ b/ui/file_manager/gallery/js/gallery_item_unittest.js
@@ -15,12 +15,6 @@
 };
 
 /**
- * Mock of ImageEncoder. Since some test changes the behavior of ImageEncoder,
- * this is initialized in setUp().
- */
-var ImageEncoder;
-
-/**
  * Load time data.
  */
 loadTimeData.data = {
@@ -29,10 +23,9 @@
 };
 
 function setUp() {
-  ImageEncoder = {
-    encodeMetadata: function() {},
-    getBlob: function() {}
-  };
+  // Replace the real ImageEncoder with a mock.
+  ImageEncoder = /** @lends{ImageEncoder} */ (
+      {encodeMetadata: function() {}, getBlob: function() {}});
 }
 
 function getMockMetadataModel() {
@@ -40,23 +33,49 @@
 }
 
 /**
+ * Creates a mock result for GalleryItem#saveToFile.
+ *
+ * @param{!GalleryItem} item
+ * @param{!MetadataModel} metadataModel
+ * @param{boolean} overwrite
+ * @return {Promise}
+ */
+function makeMockSavePromise(item, metadataModel, overwrite) {
+  let canvas =
+      assertInstanceof(document.createElement('canvas'), HTMLCanvasElement);
+  let mockVolumeManager = /**@type{!VolumeManagerWrapper} */ ({
+    getLocationInfo: function() {
+      return {};
+    },
+    getVolumeInfo: function() {
+      return {};
+    }
+  });
+  return new Promise(item.saveToFile.bind(
+      item, mockVolumeManager, metadataModel,
+      /** @type{!DirectoryEntry} */ ({}),  // fallbackDir.
+      canvas, overwrite /* overwrite */));
+}
+
+/**
  * Tests for GalleryItem#saveToFile.
  */
 function testSaveToFile(callback) {
   var fileSystem = new MockFileSystem('volumeId');
   fileSystem.populate(['/test.jpg']);
-  var entry = fileSystem.entries['/test.jpg'];
+  var entry = assertInstanceof(fileSystem.entries['/test.jpg'], MockFileEntry);
   entry.createWriter = function(callback) {
-    callback({
+    let mockWriter = /**@lends {FileWriter} */ ({
       write: function() {
         Promise.resolve().then(function() {
-          this.onwriteend();
-        }.bind(this));
+          mockWriter.onwriteend();
+        });
       },
       truncate: function() {
-        this.write();
+        mockWriter.write();
       }
     });
+    callback(mockWriter);
   };
   var entryChanged = false;
   var metadataModel = getMockMetadataModel();
@@ -64,28 +83,16 @@
     entryChanged = true;
   };
 
-  var item = new GalleryItem(
-      entry,
-      {isReadOnly: false},
-      {size: 100},
-      {},
-      /* original */ true);
+  var item =
+      new MockGalleryItem(entry, null, {size: 100}, null, true /*original */);
   assertEquals(100, item.getMetadataItem().size);
   assertFalse(entryChanged);
   reportPromise(
-      new Promise(item.saveToFile.bind(
-          item,
-          {
-            getLocationInfo: function() { return {}; },
-            getVolumeInfo: function() { return {}; }
-          },
-          metadataModel,
-          /* fallbackDir */ null,
-          document.createElement('canvas'),
-          true /* overwrite */)).then(function() {
-            assertEquals(200, item.getMetadataItem().size);
-            assertTrue(entryChanged);
-          }), callback);
+      makeMockSavePromise(item, metadataModel, true).then(function() {
+        assertEquals(200, item.getMetadataItem().size);
+        assertTrue(entryChanged);
+      }),
+      callback);
 }
 
 /**
@@ -95,42 +102,32 @@
 function testSaveToFileWriteFailCase(callback) {
   var fileSystem = new MockFileSystem('volumeId');
   fileSystem.populate(['/test.jpg']);
-  var entry = fileSystem.entries['/test.jpg'];
+  var entry = assertInstanceof(fileSystem.entries['/test.jpg'], MockFileEntry);
 
   entry.createWriter = function(callback) {
-    callback({
+    let mockWriter = /**@lends {FileWriter} */ ({
       write: function() {
         Promise.resolve().then(function() {
-          this.onerror(new Error());
-        }.bind(this));
+          mockWriter.onerror(new Error());
+        });
       },
       truncate: function() {
         Promise.resolve().then(function() {
-          this.onwriteend();
-        }.bind(this));
+          mockWriter.onwriteend();
+        });
       }
     });
+    callback(mockWriter);
   };
 
-  var item = new GalleryItem(
-      entry,
-      {isReadOnly: false},
-      {size: 100},
-      {},
-      /* original */ true);
+  var item =
+      new MockGalleryItem(entry, null, {size: 100}, null, true /*original */);
   reportPromise(
-      new Promise(item.saveToFile.bind(
-          item,
-          {
-            getLocationInfo: function() { return {}; },
-            getVolumeInfo: function() { return {}; }
-          },
-          getMockMetadataModel(),
-          /* fallbackDir */ null,
-          document.createElement('canvas'),
-          true /* overwrite */)).then(function(result) {
+      makeMockSavePromise(item, getMockMetadataModel(), true)
+          .then(function(result) {
             assertFalse(result);
-          }), callback);
+          }),
+      callback);
 }
 
 /**
@@ -145,46 +142,36 @@
 
   var fileSystem = new MockFileSystem('volumeId');
   fileSystem.populate(['/test.jpg']);
-  var entry = fileSystem.entries['/test.jpg'];
+  var entry = assertInstanceof(fileSystem.entries['/test.jpg'], MockFileEntry);
 
   var writeOperationRun = false;
   entry.createWriter = function(callback) {
-    callback({
+    let mockWriter = /**@lends {FileWriter} */ ({
       write: function() {
         Promise.resolve().then(function() {
           writeOperationRun = true;
-          this.onwriteend();
-        }.bind(this));
+          mockWriter.onwriteend();
+        });
       },
       truncate: function() {
         Promise.resolve().then(function() {
           writeOperationRun = true;
-          this.onwriteend();
-        }.bind(this));
+          mockWriter.onwriteend();
+        });
       }
     });
+    callback(mockWriter);
   };
 
-  var item = new GalleryItem(
-      entry,
-      {isReadOnly: false},
-      {size: 100},
-      {},
-      /* original */ true);
+  var item =
+      new MockGalleryItem(entry, null, {size: 100}, null, true /*original */);
   reportPromise(
-      new Promise(item.saveToFile.bind(
-          item,
-          {
-            getLocationInfo: function() { return {}; },
-            getVolumeInfo: function() { return {}; }
-          },
-          getMockMetadataModel(),
-          /* fallbackDir */ null,
-          document.createElement('canvas'),
-          true /* overwrite*/)).then(function(result) {
+      makeMockSavePromise(item, getMockMetadataModel(), true)
+          .then(function(result) {
             assertFalse(result);
             assertFalse(writeOperationRun);
-          }), callback);
+          }),
+      callback);
 }
 
 function testSaveToFileRaw(callback) {
@@ -196,16 +183,17 @@
       fileSystem.populate(['/test - Edited.jpg']);
       var entry = fileSystem.entries['/test - Edited.jpg'];
       entry.createWriter = function(callback) {
-        callback({
+        let mockWriter = /**@lends {FileWriter} */ ({
           write: function() {
             Promise.resolve().then(function() {
-              this.onwriteend();
-            }.bind(this));
+              mockWriter.onwriteend();
+            });
           },
           truncate: function() {
-            this.write();
+            mockWriter.write();
           }
         });
+        callback(mockWriter);
       };
     }
     MockDirectoryEntry.prototype.getFile.apply(this, arguments);
@@ -216,30 +204,19 @@
     entryChanged = true;
   };
 
-  var item = new GalleryItem(
-      fileSystem.entries['/test.arw'],
-      {isReadOnly: false},
-      {size: 100},
-      {},
-      /* original */ true);
+  var item = new MockGalleryItem(
+      assertInstanceof(fileSystem.entries['/test.arw'], MockFileEntry), null,
+      {size: 100}, null, true /*original */);
   assertEquals(100, item.getMetadataItem().size);
   assertFalse(entryChanged);
   reportPromise(
-      new Promise(item.saveToFile.bind(
-          item,
-          {
-            getLocationInfo: function() { return {}; },
-            getVolumeInfo: function() { return {}; }
-          },
-          metadataModel,
-          /* fallbackDir */ null,
-          document.createElement('canvas'),
-          false /* not overwrite */)).then(function(success) {
-            assertTrue(success);
-            assertEquals(200, item.getMetadataItem().size);
-            assertTrue(entryChanged);
-            assertFalse(item.isOriginal());
-          }), callback);
+      makeMockSavePromise(item, metadataModel, false).then(function(success) {
+        assertTrue(success);
+        assertEquals(200, item.getMetadataItem().size);
+        assertTrue(entryChanged);
+        assertFalse(item.isOriginal());
+      }),
+      callback);
 }
 
 function testIsWritableFile() {
@@ -262,59 +239,52 @@
     }
   };
 
-  var getGalleryItem = function(path, fileSystem, isReadOnly) {
-    return new GalleryItem(new MockEntry(fileSystem, path),
-        {isReadOnly: isReadOnly},
-        {size: 100},
-        {},
-        true /* original */);
-  };
-
   // Jpeg file on downloads.
-  assertTrue(getGalleryItem(
-      '/test.jpg', downloads, false /* not read only */).
-      isWritableFile(volumeManager));
+  assertTrue(
+      MockGalleryItem
+          .makeWithPath('/test.jpg', downloads, false /* not read only */)
+          .isWritableFile(volumeManager));
 
   // Png file on downloads.
-  assertTrue(getGalleryItem(
-      '/test.png', downloads, false /* not read only */).
-      isWritableFile(volumeManager));
+  assertTrue(
+      MockGalleryItem
+          .makeWithPath('/test.png', downloads, false /* not read only */)
+          .isWritableFile(volumeManager));
 
   // Webp file on downloads.
-  assertFalse(getGalleryItem(
-      '/test.webp', downloads, false /* not read only */).
-      isWritableFile(volumeManager));
+  assertFalse(
+      MockGalleryItem
+          .makeWithPath('/test.webp', downloads, false /* not read only */)
+          .isWritableFile(volumeManager));
 
   // Jpeg file on non-writable volume.
-  assertFalse(getGalleryItem(
-      '/test.jpg', removable, true /* read only */).
-      isWritableFile(volumeManager));
+  assertFalse(
+      MockGalleryItem.makeWithPath('/test.jpg', removable, true /* read only */)
+          .isWritableFile(volumeManager));
 
   // Jpeg file on mtp volume.
-  assertFalse(getGalleryItem(
-      '/test.jpg', mtp, false /* not read only */).
-      isWritableFile(volumeManager));
+  assertFalse(
+      MockGalleryItem.makeWithPath('/test.jpg', mtp, false /* not read only */)
+          .isWritableFile(volumeManager));
 }
 
 function testIsEditableFile() {
   var downloads = new MockFileSystem('downloads');
-  var getGalleryItem = function(path, fileSystem, isReadOnly) {
-    return new GalleryItem(
-        new MockEntry(fileSystem, path), {isReadOnly: isReadOnly}, {size: 100},
-        {}, true /* original */);
+  var getGalleryItem = function(path, isReadOnly) {
+    return MockGalleryItem.makeWithPath(path, downloads, isReadOnly);
   };
 
   // Images and raw files are editable, even if read-only (a copy is made).
-  assertTrue(getGalleryItem('/test.jpg', downloads, false).isEditable());
-  assertTrue(getGalleryItem('/test.png', downloads, false).isEditable());
-  assertTrue(getGalleryItem('/test.webp', downloads, false).isEditable());
-  assertTrue(getGalleryItem('/test.arw', downloads, false).isEditable());
-  assertTrue(getGalleryItem('/test.jpg', downloads, true).isEditable());
+  assertTrue(getGalleryItem('/test.jpg', false).isEditable());
+  assertTrue(getGalleryItem('/test.png', false).isEditable());
+  assertTrue(getGalleryItem('/test.webp', false).isEditable());
+  assertTrue(getGalleryItem('/test.arw', false).isEditable());
+  assertTrue(getGalleryItem('/test.jpg', true).isEditable());
 
   // Video files are not editable.
-  assertFalse(getGalleryItem('/test.avi', downloads, false).isEditable());
-  assertFalse(getGalleryItem('/test.mkv', downloads, false).isEditable());
-  assertFalse(getGalleryItem('/test.mp4', downloads, false).isEditable());
-  assertFalse(getGalleryItem('/test.mov', downloads, false).isEditable());
-  assertFalse(getGalleryItem('/test.webm', downloads, false).isEditable());
+  assertFalse(getGalleryItem('/test.avi', false).isEditable());
+  assertFalse(getGalleryItem('/test.mkv', false).isEditable());
+  assertFalse(getGalleryItem('/test.mp4', false).isEditable());
+  assertFalse(getGalleryItem('/test.mov', false).isEditable());
+  assertFalse(getGalleryItem('/test.webm', false).isEditable());
 }
diff --git a/ui/file_manager/gallery/js/image_editor/image_encoder.js b/ui/file_manager/gallery/js/image_editor/image_encoder.js
index 0d11929b..3d1cc8e 100644
--- a/ui/file_manager/gallery/js/image_editor/image_encoder.js
+++ b/ui/file_manager/gallery/js/image_editor/image_encoder.js
@@ -4,6 +4,7 @@
 
 /**
  * A namespace class for image encoding functions. All methods are static.
+ * @constructor
  */
 function ImageEncoder() {}
 
diff --git a/ui/file_manager/gallery/js/mock_gallery_item.js b/ui/file_manager/gallery/js/mock_gallery_item.js
index 954f347c..79777c5 100644
--- a/ui/file_manager/gallery/js/mock_gallery_item.js
+++ b/ui/file_manager/gallery/js/mock_gallery_item.js
@@ -22,6 +22,21 @@
       opt_thumbnailMetadataItem || null, opt_original || false);
 }
 
+/**
+ * Helper to construct a MockGalleryItem with a given |path| and dummy metadata.
+ *
+ * @param {!string} path
+ * @param {!FileSystem} fileSystem
+ * @param {boolean} isReadOnly
+ * @return MockGalleryItem
+ */
+MockGalleryItem.makeWithPath = function(path, fileSystem, isReadOnly) {
+  return new MockGalleryItem(
+      new MockFileEntry(fileSystem, path),
+      /** @type {EntryLocation} */ ({isReadOnly: isReadOnly}), {size: 100},
+      null, true /* original */);
+};
+
 MockGalleryItem.prototype = {
   __proto__: GalleryItem.prototype
 };
diff --git a/ui/file_manager/integration_tests/file_manager/create_new_folder.js b/ui/file_manager/integration_tests/file_manager/create_new_folder.js
index ff7263fc..b8104fe 100644
--- a/ui/file_manager/integration_tests/file_manager/create_new_folder.js
+++ b/ui/file_manager/integration_tests/file_manager/create_new_folder.js
@@ -9,8 +9,8 @@
  * When we are not in guest mode, we fill Google Drive with the basic entry set
  * which causes an extra tree-item to be added.
  */
-var TREEITEM_DRIVE = '#directory-tree [entry-label="My Drive"] ';
-var TREEITEM_DOWNLOADS = '#directory-tree [entry-label="Downloads"] ';
+const TREEITEM_DRIVE = '#directory-tree [entry-label="My Drive"]';
+const TREEITEM_DOWNLOADS = '#directory-tree [entry-label="Downloads"]';
 
 /**
  * Selects the first item in the file list.
diff --git a/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js b/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
index 17b46b29..2a241cb4 100644
--- a/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
+++ b/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
@@ -4,26 +4,27 @@
 
 'use strict';
 
+(function(){
+
 /**
- * Constants for selectors.
+ * Directory tree selector constants.
  */
+const TREEITEM_A = TREEITEM_DRIVE + ' [entry-label="A"] ';
+const TREEITEM_B = TREEITEM_A + '[entry-label="B"] ';
+const TREEITEM_C = TREEITEM_B + '[entry-label="C"] ';
 
-var TREEITEM_DRIVE = '#directory-tree [entry-label="My Drive"] ';
-var TREEITEM_A = TREEITEM_DRIVE + ' [entry-label="A"] ';
-var TREEITEM_B = TREEITEM_A + '[entry-label="B"] ';
-var TREEITEM_C = TREEITEM_B + '[entry-label="C"] ';
-var TREEITEM_D = TREEITEM_DRIVE + '[entry-label="D"] ';
-var TREEITEM_E = TREEITEM_D + '[entry-label="E"] ';
-var EXPAND_ICON = '> .tree-row > .expand-icon';
-var ITEM_ICON = '> .tree-row > .item-icon';
-var EXPANDED_SUBTREE = '> .tree-children[expanded]';
+const TREEITEM_D = TREEITEM_DRIVE + ' [entry-label="D"] ';
+const TREEITEM_E = TREEITEM_D + '[entry-label="E"] ';
+
+const EXPAND_ICON = '> .tree-row > .expand-icon';
+const ITEM_ICON = '> .tree-row > .item-icon';
+const EXPANDED_SUBTREE = '> .tree-children[expanded]';
 
 /**
- * Entry set which is used for this test.
+ * Entry set used for the folder shortcut tests.
  * @type {Array<TestEntryInfo>}
- * @const
  */
-var ENTRY_SET = [
+const FOLDER_ENTRY_SET = [
   ENTRIES.directoryA,
   ENTRIES.directoryB,
   ENTRIES.directoryC,
@@ -33,11 +34,10 @@
 ];
 
 /**
- * Constants for each folders.
+ * Constants for each folder.
  * @type {Array<Object>}
- * @const
  */
-var DIRECTORY = {
+const DIRECTORY = {
   Drive: {
     contents: [
       ENTRIES.directoryA.getExpectedRow(), ENTRIES.directoryD.getExpectedRow()
@@ -253,7 +253,7 @@
   StepsRunner.run([
     // Set up each window.
     function() {
-      addEntries(['drive'], ENTRY_SET, this.next);
+      addEntries(['drive'], FOLDER_ENTRY_SET, this.next);
     },
     function(result) {
       chrome.test.assertTrue(result);
@@ -347,7 +347,7 @@
   StepsRunner.run([
     // Set up each window.
     function() {
-      addEntries(['drive'], ENTRY_SET, this.next);
+      addEntries(['drive'], FOLDER_ENTRY_SET, this.next);
     },
     function(result) {
       chrome.test.assertTrue(result);
@@ -414,3 +414,4 @@
   ]);
 };
 
+})();
diff --git a/ui/file_manager/integration_tests/file_manager/launcher_search.js b/ui/file_manager/integration_tests/file_manager/launcher_search.js
new file mode 100644
index 0000000..5e526c38
--- /dev/null
+++ b/ui/file_manager/integration_tests/file_manager/launcher_search.js
@@ -0,0 +1,62 @@
+// 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.
+
+/**
+ * @fileoverview Tests for interface we expose to Launcher app's search feature.
+ */
+
+'use strict';
+
+(function() {
+
+/**
+ * Tests opening an image using the Launcher app's search feature.
+ */
+testcase.launcherOpenSearchResult = function() {
+  let galleryAppId;
+  const imageName = ENTRIES.desktop.nameText;
+  StepsRunner.run([
+    // Create an image file in Drive.
+    function() {
+      addEntries(['drive'], [ENTRIES.desktop]).then(this.next);
+    },
+    // Get the image file URL.
+    function() {
+      remoteCall
+          .callRemoteTestUtil(
+              'getFilesUnderVolume', null, ['drive', [imageName]])
+          .then(this.next);
+    },
+    // Request Files app to open the image URL.
+    // This emulates the Launcher interaction with Files app.
+    function(fileURLs) {
+      chrome.test.assertEq(1, fileURLs.length);
+      remoteCall.callRemoteTestUtil(
+          'launcherSearchOpenResult', null, [fileURLs[0]], this.next);
+    },
+    // Files app opens Gallery for images, so check Gallery app has been
+    // launched.
+    function() {
+      galleryApp.waitForWindow('gallery.html').then(this.next);
+    },
+    // Check the image is displayed.
+    function(windowId) {
+      galleryAppId = windowId;
+      const imageNameNoExtension = imageName.split('.')[0];
+      galleryApp.waitForSlideImage(galleryAppId, 0, 0, imageNameNoExtension)
+          .then(this.next);
+    },
+    // Check that the previous step succeeded.
+    function(imageElement) {
+      chrome.test.assertTrue(
+          !!imageElement, 'Failed to find the slide image element');
+      this.next();
+    },
+    function() {
+      checkIfNoErrorsOccured(this.next);
+    }
+  ]);
+};
+
+})();
diff --git a/ui/file_manager/integration_tests/file_manager_test_manifest.json b/ui/file_manager/integration_tests/file_manager_test_manifest.json
index 5ec7057f..96dbf5d2 100644
--- a/ui/file_manager/integration_tests/file_manager_test_manifest.json
+++ b/ui/file_manager/integration_tests/file_manager_test_manifest.json
@@ -29,6 +29,7 @@
       "file_manager/grid_view.js",
       "file_manager/install_linux_package_dialog.js",
       "file_manager/keyboard_operations.js",
+      "file_manager/launcher_search.js",
       "file_manager/my_files.js",
       "file_manager/open_audio_files.js",
       "file_manager/open_image_files.js",
diff --git a/ui/gl/gl_image_shared_memory_unittest.cc b/ui/gl/gl_image_shared_memory_unittest.cc
index 26eb38b..b0490ea 100644
--- a/ui/gl/gl_image_shared_memory_unittest.cc
+++ b/ui/gl/gl_image_shared_memory_unittest.cc
@@ -30,7 +30,7 @@
     GLImageTestSupport::SetBufferDataToColor(
         size.width(), size.height(),
         static_cast<int>(RowSizeForBufferFormat(size.width(), format, 0)), 0,
-        format, color, reinterpret_cast<uint8_t*>(shared_memory.memory()));
+        format, color, static_cast<uint8_t*>(shared_memory.memory()));
     scoped_refptr<GLImageSharedMemory> image(new GLImageSharedMemory(
         size, GLImageMemory::GetInternalFormatForTesting(format)));
     rv = image->Initialize(
@@ -97,7 +97,7 @@
     GLImageTestSupport::SetBufferDataToColor(
         size.width(), size.height(), static_cast<int>(stride), 0,
         gfx::BufferFormat::RGBA_8888, color,
-        reinterpret_cast<uint8_t*>(shared_memory.memory()) + buffer_offset);
+        static_cast<uint8_t*>(shared_memory.memory()) + buffer_offset);
     scoped_refptr<GLImageSharedMemory> image(
         new GLImageSharedMemory(size, GL_RGBA));
     rv = image->Initialize(
diff --git a/ui/ozone/platform/drm/BUILD.gn b/ui/ozone/platform/drm/BUILD.gn
index 192df8e..832a6c0 100644
--- a/ui/ozone/platform/drm/BUILD.gn
+++ b/ui/ozone/platform/drm/BUILD.gn
@@ -130,7 +130,7 @@
     "//ipc",
     "//mojo/public/cpp/system",
     "//services/service_manager/public/cpp",
-    "//services/ui/public/interfaces:constants",
+    "//services/ws/public/mojom:constants",
     "//skia",
     "//third_party/libdrm",
     "//third_party/libsync",
diff --git a/ui/ozone/platform/drm/DEPS b/ui/ozone/platform/drm/DEPS
index 69f499a..e45cbfe8 100644
--- a/ui/ozone/platform/drm/DEPS
+++ b/ui/ozone/platform/drm/DEPS
@@ -2,6 +2,7 @@
   "+mojo/public",
   "+services/service_manager",
   "+services/ui",
+  "+services/ws",
   "+ui/base/ui_base_features.h",
   "+ui/base/ui_base_switches.h",
   "+ui/base/ui_features.h",  # UI features doesn't bring in all of ui/base.
diff --git a/ui/ozone/platform/drm/host/drm_device_connector.cc b/ui/ozone/platform/drm/host/drm_device_connector.cc
index dfdd37c..3258512 100644
--- a/ui/ozone/platform/drm/host/drm_device_connector.cc
+++ b/ui/ozone/platform/drm/host/drm_device_connector.cc
@@ -7,7 +7,7 @@
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/ozone/platform/drm/host/host_drm_device.h"
 #include "ui/ozone/public/gpu_platform_support_host.h"
diff --git a/ui/ozone/platform/drm/host/host_cursor_proxy.cc b/ui/ozone/platform/drm/host/host_cursor_proxy.cc
index fa2880d..2a6e342 100644
--- a/ui/ozone/platform/drm/host/host_cursor_proxy.cc
+++ b/ui/ozone/platform/drm/host/host_cursor_proxy.cc
@@ -5,7 +5,7 @@
 #include "ui/ozone/platform/drm/host/host_cursor_proxy.h"
 
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/ozone/public/gpu_platform_support_host.h"
 
 namespace ui {
diff --git a/ui/ozone/platform/drm/host/host_drm_device.cc b/ui/ozone/platform/drm/host/host_drm_device.cc
index e595e30..35446c8e 100644
--- a/ui/ozone/platform/drm/host/host_drm_device.cc
+++ b/ui/ozone/platform/drm/host/host_drm_device.cc
@@ -10,7 +10,7 @@
 #include "base/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/ws/public/mojom/constants.mojom.h"
 #include "ui/display/types/display_snapshot.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/host/drm_device_connector.h"
diff --git a/ui/ozone/platform/scenic/scenic_window_canvas.cc b/ui/ozone/platform/scenic/scenic_window_canvas.cc
index dc99452..1c4e3632 100644
--- a/ui/ozone/platform/scenic/scenic_window_canvas.cc
+++ b/ui/ozone/platform/scenic/scenic_window_canvas.cc
@@ -43,7 +43,7 @@
   int stride = surface->width() * SkColorTypeBytesPerPixel(kN32_SkColorType);
   for (SkRegion::Iterator i(dirty_region); !i.done(); i.next()) {
     uint8_t* dst_ptr =
-        reinterpret_cast<uint8_t*>(memory.memory()) +
+        static_cast<uint8_t*>(memory.memory()) +
         i.rect().x() * SkColorTypeBytesPerPixel(kN32_SkColorType) +
         i.rect().y() * stride;
     frame.surface->readPixels(
@@ -197,4 +197,4 @@
   return nullptr;
 }
 
-}  // namespace ui
\ No newline at end of file
+}  // namespace ui
diff --git a/ui/surface/transport_dib_posix.cc b/ui/surface/transport_dib_posix.cc
index 1776ecc..604268c6 100644
--- a/ui/surface/transport_dib_posix.cc
+++ b/ui/surface/transport_dib_posix.cc
@@ -51,9 +51,9 @@
                                                           bool opaque) {
   if ((!memory() && !Map()) || !VerifyCanvasSize(w, h))
     return NULL;
-  return skia::CreatePlatformCanvasWithPixels(
-      w, h, opaque, reinterpret_cast<uint8_t*>(memory()),
-      skia::RETURN_NULL_ON_FAILURE);
+  return skia::CreatePlatformCanvasWithPixels(w, h, opaque,
+                                              static_cast<uint8_t*>(memory()),
+                                              skia::RETURN_NULL_ON_FAILURE);
 }
 
 bool TransportDIB::Map() {
diff --git a/ui/touch_selection/BUILD.gn b/ui/touch_selection/BUILD.gn
index bd12693..4ee20fd 100644
--- a/ui/touch_selection/BUILD.gn
+++ b/ui/touch_selection/BUILD.gn
@@ -39,7 +39,7 @@
 
   if (use_aura) {
     deps += [
-      "//services/ui/public/interfaces",
+      "//services/ws/public/mojom",
       "//skia:skia",
       "//ui/aura",
       "//ui/aura_extra",
diff --git a/ui/touch_selection/DEPS b/ui/touch_selection/DEPS
index e81e69a..2304260d 100644
--- a/ui/touch_selection/DEPS
+++ b/ui/touch_selection/DEPS
@@ -1,5 +1,5 @@
 include_rules = [
-  "+services/ui/public/interfaces",
+  "+services/ws/public/mojom",
   "+ui/aura",
   "+ui/aura_extra",
   "+ui/base",
diff --git a/ui/touch_selection/touch_handle_drawable_aura.cc b/ui/touch_selection/touch_handle_drawable_aura.cc
index ed2914f..0f87885d 100644
--- a/ui/touch_selection/touch_handle_drawable_aura.cc
+++ b/ui/touch_selection/touch_handle_drawable_aura.cc
@@ -4,7 +4,7 @@
 
 #include "ui/touch_selection/touch_handle_drawable_aura.h"
 
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_targeter.h"
 #include "ui/aura_extra/image_window_delegate.h"
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 6a129ed..751d3cb 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -476,7 +476,7 @@
     "//base:i18n",
     "//base/third_party/dynamic_annotations",
     "//cc/paint",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//third_party/icu",
     "//ui/accessibility",
@@ -653,7 +653,7 @@
       "widget/window_reorderer.cc",
     ]
     deps += [
-      "//services/ui/public/interfaces",
+      "//services/ws/public/mojom",
       "//ui/aura",
       "//ui/platform_window",
       "//ui/touch_selection",
@@ -1025,7 +1025,7 @@
     "//cc",
     "//cc/paint",
     "//components/vector_icons",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//testing/gtest",
     "//third_party/icu",
diff --git a/ui/views/DEPS b/ui/views/DEPS
index 39d2ec74..f591ea6 100644
--- a/ui/views/DEPS
+++ b/ui/views/DEPS
@@ -2,7 +2,7 @@
   "+cc/paint",
   "+components/crash/core/common/crash_key.h",
   "+components/vector_icons",
-  "+services/ui/public/interfaces",
+  "+services/ws/public/mojom",
   "+skia/ext",
   "+third_party/iaccessible2",
   "+third_party/skia",
diff --git a/ui/views/mus/BUILD.gn b/ui/views/mus/BUILD.gn
index 0c9092d..39b4158 100644
--- a/ui/views/mus/BUILD.gn
+++ b/ui/views/mus/BUILD.gn
@@ -61,7 +61,7 @@
     "//services/service_manager/public/mojom",
     "//services/ui/public/cpp",
     "//services/ui/public/cpp/input_devices",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//third_party/icu",
     "//ui/accessibility",
@@ -171,7 +171,7 @@
     "//base/test:test_support",
     "//cc",
     "//net",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//testing/gtest",
     "//third_party/icu",
diff --git a/ui/views/mus/drag_interactive_uitest.cc b/ui/views/mus/drag_interactive_uitest.cc
index ab446a9..86a8b589 100644
--- a/ui/views/mus/drag_interactive_uitest.cc
+++ b/ui/views/mus/drag_interactive_uitest.cc
@@ -4,10 +4,10 @@
 
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/event_injector.mojom.h"
-#include "services/ui/public/interfaces/window_server_test.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/event_injector.mojom.h"
+#include "services/ws/public/mojom/window_server_test.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/mus/in_flight_change.h"
 #include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/mus/window_tree_host_mus.h"
diff --git a/ui/views/mus/mus_client.cc b/ui/views/mus/mus_client.cc
index a6cb5997..531367c 100644
--- a/ui/views/mus/mus_client.cc
+++ b/ui/views/mus/mus_client.cc
@@ -12,8 +12,8 @@
 #include "services/ui/public/cpp/gpu/gpu.h"
 #include "services/ui/public/cpp/input_devices/input_device_client.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/env.h"
 #include "ui/aura/mus/capture_synchronizer.h"
 #include "ui/aura/mus/mus_context_factory.h"
diff --git a/ui/views/mus/screen_mus.h b/ui/views/mus/screen_mus.h
index a86e458..2788d5999 100644
--- a/ui/views/mus/screen_mus.h
+++ b/ui/views/mus/screen_mus.h
@@ -5,7 +5,7 @@
 #ifndef UI_VIEWS_MUS_SCREEN_MUS_H_
 #define UI_VIEWS_MUS_SCREEN_MUS_H_
 
-#include "services/ui/public/interfaces/screen_provider_observer.mojom.h"
+#include "services/ws/public/mojom/screen_provider_observer.mojom.h"
 #include "ui/display/screen_base.h"
 #include "ui/views/mus/mus_export.h"
 
diff --git a/ui/views/mus/window_manager_constants_converters.h b/ui/views/mus/window_manager_constants_converters.h
index e9493eb..7741950f 100644
--- a/ui/views/mus/window_manager_constants_converters.h
+++ b/ui/views/mus/window_manager_constants_converters.h
@@ -5,7 +5,7 @@
 #ifndef UI_VIEWS_MUS_WINDOW_MANAGER_CONSTANTS_CONVERTERS_H_
 #define UI_VIEWS_MUS_WINDOW_MANAGER_CONSTANTS_CONVERTERS_H_
 
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/views/mus/mus_export.h"
 #include "ui/views/widget/widget.h"
 
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
index 4f647af1..793ddac7 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -8,7 +8,7 @@
 #include "base/macros.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/drag_drop_client.h"
diff --git a/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc b/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc
index 546411e..9cbd2e9 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc
@@ -9,7 +9,7 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
diff --git a/ui/views/widget/desktop_aura/window_event_filter.cc b/ui/views/widget/desktop_aura/window_event_filter.cc
index e2d9e3e..aae1923d 100644
--- a/ui/views/widget/desktop_aura/window_event_filter.cc
+++ b/ui/views/widget/desktop_aura/window_event_filter.cc
@@ -4,7 +4,7 @@
 
 #include "ui/views/widget/desktop_aura/window_event_filter.h"
 
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
diff --git a/ui/views/widget/desktop_aura/x11_window_event_filter.cc b/ui/views/widget/desktop_aura/x11_window_event_filter.cc
index 3d5ecdc..bd73d72 100644
--- a/ui/views/widget/desktop_aura/x11_window_event_filter.cc
+++ b/ui/views/widget/desktop_aura/x11_window_event_filter.cc
@@ -4,7 +4,7 @@
 
 #include "ui/views/widget/desktop_aura/x11_window_event_filter.h"
 
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc
index 2d64236..93903ae 100644
--- a/ui/views/widget/native_widget_aura.cc
+++ b/ui/views/widget/native_widget_aura.cc
@@ -10,8 +10,8 @@
 #include "base/strings/string_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/client/cursor_client.h"
diff --git a/ui/views/widget/native_widget_aura_unittest.cc b/ui/views/widget/native_widget_aura_unittest.cc
index b1ef3a6..4508d35 100644
--- a/ui/views/widget/native_widget_aura_unittest.cc
+++ b/ui/views/widget/native_widget_aura_unittest.cc
@@ -9,7 +9,7 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/env.h"
diff --git a/ui/views/widget/widget_delegate.cc b/ui/views/widget/widget_delegate.cc
index f1a5004..8e1b8594 100644
--- a/ui/views/widget/widget_delegate.cc
+++ b/ui/views/widget/widget_delegate.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/views/view.h"
 #include "ui/views/views_delegate.h"
diff --git a/ui/wm/BUILD.gn b/ui/wm/BUILD.gn
index c1bb78d1..7c334d8e 100644
--- a/ui/wm/BUILD.gn
+++ b/ui/wm/BUILD.gn
@@ -66,7 +66,7 @@
 
   deps = [
     "//base",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//ui/aura",
     "//ui/base",
@@ -112,7 +112,7 @@
     "//services/service_manager/public/cpp",
     "//services/ui/public/cpp",
     "//services/ui/public/cpp/input_devices",
-    "//services/ui/public/interfaces",
+    "//services/ws/public/mojom",
     "//skia",
     "//ui/aura",
     "//ui/aura:test_support",
diff --git a/ui/wm/core/DEPS b/ui/wm/core/DEPS
index 255fd3b2..2d58140f 100644
--- a/ui/wm/core/DEPS
+++ b/ui/wm/core/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+third_party/skia",
   "+services/ui/public",
+  "+services/ws/public",
   "+ui/aura",
   "+ui/base/accelerators",
   "+ui/base/cursor",
diff --git a/ui/wm/core/easy_resize_window_targeter.cc b/ui/wm/core/easy_resize_window_targeter.cc
index 568460a..72876118 100644
--- a/ui/wm/core/easy_resize_window_targeter.cc
+++ b/ui/wm/core/easy_resize_window_targeter.cc
@@ -6,7 +6,7 @@
 
 #include <algorithm>
 
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/transient_window_client.h"
 #include "ui/aura/env.h"
diff --git a/ui/wm/test/DEPS b/ui/wm/test/DEPS
index 72e640ce..8e6ec86 100644
--- a/ui/wm/test/DEPS
+++ b/ui/wm/test/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+services/service_manager/public",
   "+services/ui/public",
+  "+services/ws/public",
   "+ui/aura",
   "+ui/base/resource/resource_bundle.h",
   "+ui/base/ui_base_paths.h",
diff --git a/ui/wm/test/wm_test_helper.cc b/ui/wm/test/wm_test_helper.cc
index 9cc892a..fb9f62c6 100644
--- a/ui/wm/test/wm_test_helper.cc
+++ b/ui/wm/test/wm_test_helper.cc
@@ -12,8 +12,8 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/public/cpp/input_devices/input_device_client.h"
 #include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/public/interfaces/window_manager.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/window_manager.mojom.h"
 #include "ui/aura/client/default_capture_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/mus/property_converter.h"
diff --git a/webrunner/browser/context_impl_browsertest.cc b/webrunner/browser/context_impl_browsertest.cc
index f1ebebb8..0a66bc7e 100644
--- a/webrunner/browser/context_impl_browsertest.cc
+++ b/webrunner/browser/context_impl_browsertest.cc
@@ -519,7 +519,8 @@
   MOCK_METHOD0(NavigationStopped, void());
 };
 
-IN_PROC_BROWSER_TEST_F(ContextImplTest, Stop) {
+// TODO(crbug.com/876894): Disabled due to flakiness.
+IN_PROC_BROWSER_TEST_F(ContextImplTest, DISABLED_Stop) {
   chromium::web::FramePtr frame = CreateFrame();
 
   chromium::web::NavigationControllerPtr controller;