diff --git a/DEPS b/DEPS
index 003680d3..0998f50 100644
--- a/DEPS
+++ b/DEPS
@@ -129,11 +129,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '1208002548e8ce4b0f4f61ddebd200562ce64470',
+  'skia_revision': '99d792276740293109edb22a26fea50490e7eeaf',
   # 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': '790137c150aceab523179662ca3534b5050ef81b',
+  'v8_revision': '0ec288d449bc14033c44dd5d0bbcd3f6d64ec9f1',
   # 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.
@@ -141,7 +141,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '76a9a97fb2a19a325ef90c10900718bfbdb4b1d3',
+  'angle_revision': 'e6b23e45b380bee1a2dfda06e4728d24d4d4ad8b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -149,7 +149,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': '44034bca7d3eda87b7e7f38d9cf695fde9a312dd',
+  'pdfium_revision': '9cf260b9d7f4491833bd5fd997a286ce6926678e',
   # 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.
@@ -196,7 +196,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'f8d4d2df537739c5f101da3d702b0a8ff6537909',
+  'catapult_revision': 'dc0c991440dbeb33c04e4ad9f52e22c173817abd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -268,7 +268,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'b4b3ea05ebde1a1bb6372f8506cd762d040d7978',
+  'dawn_revision': 'f4c3f4562eeeaeab99a19d4a293d062936c5a2ee',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -475,7 +475,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '2cacdff921fb067d93cf476a10f7decb1a9fb766',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '82cfc2ceebd3d1bfbaf6b5129d08f2b1f4054508',
       'condition': 'checkout_ios',
   },
 
@@ -490,7 +490,7 @@
   },
 
   'src/ios/third_party/material_roboto_font_loader_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-foundation/material-roboto-font-loader-ios.git' + '@' + '4aa51e906e5671c71d24e991f1f10d782a58409f',
+      'url': Var('chromium_git') + '/external/github.com/material-foundation/material-roboto-font-loader-ios.git' + '@' + 'bc63eabbbd1e14cee0779b05827e08db2e413553',
       'condition': 'checkout_ios',
   },
 
@@ -805,7 +805,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'c62d4b70f2c79cd0a5720d81570bcb0324070cd2',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'f21ce663029c3172c162dae0a199f5be5a0c9991',
       'condition': 'checkout_linux',
   },
 
@@ -830,7 +830,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '77b3499b880955840580bffbb167124c9ff83b6b',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6379cd39a3336ab8716c77b1c68ab37ff406d060',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1172,7 +1172,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '73904e05a2db53359bff5514130a9e16ac886bfe',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '911b1a37708e3ded201d9664602f9670a0acb572',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index dcba1f5..e8bf5f2 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -66,6 +66,7 @@
     "system/unified/unified_system_tray.h",
     "touch/touch_observer_hud.h",
     "wm/client_controlled_state.h",
+    "wm/desks/desks_util.h",
     "wm/drag_window_resizer.h",
     "wm/mru_window_tracker.h",
     "wm/overview/overview_controller.h",
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index e3abe52..4c60aa9 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -1089,9 +1089,11 @@
   return deprecated_accelerators_.count(accelerator) != 0;
 }
 
-bool AcceleratorController::PerformActionIfEnabled(AcceleratorAction action) {
-  if (CanPerformAction(action, ui::Accelerator())) {
-    PerformAction(action, ui::Accelerator());
+bool AcceleratorController::PerformActionIfEnabled(
+    AcceleratorAction action,
+    const ui::Accelerator& accelerator) {
+  if (CanPerformAction(action, accelerator)) {
+    PerformAction(action, accelerator);
     return true;
   }
   return false;
@@ -1407,6 +1409,12 @@
   if (restriction != RESTRICTION_NONE)
     return;
 
+  // TODO(minch): For VOLUME_DOWN and VOLUME_UP. Do the calculation based on
+  // accelerator.source_device_id() and
+  // ui::InputDeviceManager::GetInstance()->GetOtherInputDevices() to see
+  // whether we need to flip its action on current screen orientation for side
+  // volume button. http://crbug.com/937907.
+
   // If your accelerator invokes more than one line of code, please either
   // implement it in your module's controller code or pull it into a HandleFoo()
   // function above.
diff --git a/ash/accelerators/accelerator_controller.h b/ash/accelerators/accelerator_controller.h
index 7b74911..40ea43a1 100644
--- a/ash/accelerators/accelerator_controller.h
+++ b/ash/accelerators/accelerator_controller.h
@@ -105,7 +105,9 @@
 
   // Performs the specified action if it is enabled. Returns whether the action
   // was performed successfully.
-  bool PerformActionIfEnabled(AcceleratorAction action);
+  bool PerformActionIfEnabled(
+      AcceleratorAction action,
+      const ui::Accelerator& accelerator = ui::Accelerator());
 
   // Returns the restriction for the current context.
   AcceleratorProcessingRestriction GetCurrentAcceleratorRestriction();
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 40bfb7a..8bbf6044 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -760,6 +760,11 @@
   return app_list::AppListViewState::CLOSED;
 }
 
+void AppListControllerImpl::SetStateTransitionAnimationCallback(
+    StateTransitionAnimationCallback callback) {
+  state_transition_animation_callback_ = std::move(callback);
+}
+
 void AppListControllerImpl::SetAppListModelForTest(
     std::unique_ptr<app_list::AppListModel> model) {
   model_->RemoveObserver(this);
@@ -1025,6 +1030,12 @@
              mojom::VoiceInteractionState::NOT_READY;
 }
 
+void AppListControllerImpl::OnStateTransitionAnimationCompleted(
+    app_list::AppListViewState state) {
+  if (!state_transition_animation_callback_.is_null())
+    state_transition_animation_callback_.Run(state);
+}
+
 void AppListControllerImpl::AddObserver(AppListControllerObserver* observer) {
   observers_.AddObserver(observer);
 }
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index 5d8dd12..c60cb726 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -200,6 +200,8 @@
   void OnSearchResultVisibilityChanged(const std::string& id,
                                        bool visibility) override;
   bool IsAssistantAllowedAndEnabled() const override;
+  void OnStateTransitionAnimationCompleted(
+      app_list::AppListViewState state) override;
 
   void AddObserver(AppListControllerObserver* observer);
   void RemoveObserver(AppListControllerObserver* obsever);
@@ -286,6 +288,12 @@
 
   void SetAppListModelForTest(std::unique_ptr<app_list::AppListModel> model);
 
+  using StateTransitionAnimationCallback =
+      base::RepeatingCallback<void(app_list::AppListViewState)>;
+
+  void SetStateTransitionAnimationCallback(
+      StateTransitionAnimationCallback callback);
+
  private:
   syncer::StringOrdinal GetOemFolderPos();
   std::unique_ptr<app_list::AppListItem> CreateAppListItem(
@@ -336,6 +344,8 @@
   // each profile has its own AppListModelUpdater to manipulate app list items.
   int profile_id_ = kAppListInvalidProfileID;
 
+  StateTransitionAnimationCallback state_transition_animation_callback_;
+
   base::ObserverList<AppListControllerObserver> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(AppListControllerImpl);
diff --git a/ash/app_list/app_list_view_delegate.h b/ash/app_list/app_list_view_delegate.h
index 0ac383a..ac9f3cf0 100644
--- a/ash/app_list/app_list_view_delegate.h
+++ b/ash/app_list/app_list_view_delegate.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "ash/app_list/app_list_metrics.h"
+#include "ash/app_list/model/app_list_view_state.h"
 #include "ash/assistant/ui/assistant_view_delegate.h"
 #include "ash/public/cpp/ash_public_export.h"
 #include "ash/public/interfaces/app_list.mojom.h"
@@ -167,6 +168,9 @@
 
   // Returns if the Assistant feature is allowed and enabled.
   virtual bool IsAssistantAllowedAndEnabled() const = 0;
+
+  // Called when the app list view animation is completed.
+  virtual void OnStateTransitionAnimationCompleted(AppListViewState state) = 0;
 };
 
 }  // namespace app_list
diff --git a/ash/app_list/test/app_list_test_view_delegate.cc b/ash/app_list/test/app_list_test_view_delegate.cc
index 7539368..d4bb588 100644
--- a/ash/app_list/test/app_list_test_view_delegate.cc
+++ b/ash/app_list/test/app_list_test_view_delegate.cc
@@ -137,6 +137,9 @@
   return false;
 }
 
+void AppListTestViewDelegate::OnStateTransitionAnimationCompleted(
+    AppListViewState state) {}
+
 bool AppListTestViewDelegate::IsCommandIdChecked(int command_id) const {
   return true;
 }
diff --git a/ash/app_list/test/app_list_test_view_delegate.h b/ash/app_list/test/app_list_test_view_delegate.h
index 597fe5f..c11ed62e 100644
--- a/ash/app_list/test/app_list_test_view_delegate.h
+++ b/ash/app_list/test/app_list_test_view_delegate.h
@@ -101,6 +101,7 @@
   void OnSearchResultVisibilityChanged(const std::string& id,
                                        bool visibility) override;
   bool IsAssistantAllowedAndEnabled() const override;
+  void OnStateTransitionAnimationCompleted(AppListViewState state) override;
 
   // Do a bulk replacement of the items in the model.
   void ReplaceTestModel(int item_count);
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index a7aac57..59836967 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -188,7 +188,8 @@
 class AppListView::StateAnimationMetricsReporter
     : public ui::AnimationMetricsReporter {
  public:
-  StateAnimationMetricsReporter() = default;
+  explicit StateAnimationMetricsReporter(AppListView* view) : view_(view) {}
+
   ~StateAnimationMetricsReporter() override = default;
 
   void Start(bool is_in_tablet_mode) {
@@ -209,6 +210,7 @@
       UMA_HISTOGRAM_PERCENTAGE(
           "Apps.StateTransition.AnimationSmoothness.ClamshellMode", value);
     }
+    view_->OnStateTransitionAnimationCompleted();
 #if defined(DCHECK)
     started_ = false;
 #endif
@@ -219,6 +221,7 @@
   bool started_ = false;
 #endif
   bool is_in_tablet_mode_ = false;
+  AppListView* view_;
 
   DISALLOW_COPY_AND_ASSIGN(StateAnimationMetricsReporter);
 };
@@ -323,7 +326,9 @@
 ////////////////////////////////////////////////////////////////////////////////
 // AppListView::TestApi
 
-AppListView::TestApi::TestApi(AppListView* view) : view_(view) {}
+AppListView::TestApi::TestApi(AppListView* view) : view_(view) {
+  DCHECK(view_);
+}
 
 AppListView::TestApi::~TestApi() = default;
 
@@ -344,7 +349,7 @@
       transition_animation_observer_(
           std::make_unique<TransitionAnimationObserver>(this)),
       state_animation_metrics_reporter_(
-          std::make_unique<StateAnimationMetricsReporter>()),
+          std::make_unique<StateAnimationMetricsReporter>(this)),
       weak_ptr_factory_(this) {
   CHECK(delegate);
 }
@@ -1842,4 +1847,8 @@
   app_list_background_shield_->SetTransform(transform);
 }
 
+void AppListView::OnStateTransitionAnimationCompleted() {
+  delegate_->OnStateTransitionAnimationCompleted(app_list_state_);
+}
+
 }  // namespace app_list
diff --git a/ash/app_list/views/app_list_view.h b/ash/app_list/views/app_list_view.h
index 05151e1f..d93b9e6 100644
--- a/ash/app_list/views/app_list_view.h
+++ b/ash/app_list/views/app_list_view.h
@@ -255,6 +255,9 @@
                              const gfx::Rect& new_bounds,
                              ui::PropertyChangeReason reason) override;
 
+  // Called when state transition animation is completed.
+  void OnStateTransitionAnimationCompleted();
+
   views::Widget* get_fullscreen_widget_for_test() const {
     return fullscreen_widget_;
   }
diff --git a/ash/assistant/assistant_screen_context_controller_unittest.cc b/ash/assistant/assistant_screen_context_controller_unittest.cc
index 41d62b17..4f05369 100644
--- a/ash/assistant/assistant_screen_context_controller_unittest.cc
+++ b/ash/assistant/assistant_screen_context_controller_unittest.cc
@@ -11,6 +11,7 @@
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/desks/desks_util.h"
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/test/scoped_feature_list.h"
@@ -69,9 +70,9 @@
 // Verify that incognito windows are blocked in screenshot.
 TEST_F(AssistantScreenContextControllerTest, Screenshot) {
   std::unique_ptr<aura::Window> window1 = CreateToplevelTestWindow(
-      gfx::Rect(0, 0, 200, 200), kShellWindowId_DefaultContainer);
+      gfx::Rect(0, 0, 200, 200), desks_util::GetActiveDeskContainerId());
   std::unique_ptr<aura::Window> window2 = CreateToplevelTestWindow(
-      gfx::Rect(30, 30, 100, 100), kShellWindowId_DefaultContainer);
+      gfx::Rect(30, 30, 100, 100), desks_util::GetActiveDeskContainerId());
 
   ui::Layer* window1_layer = window1->layer();
   ui::Layer* window2_layer = window2->layer();
diff --git a/ash/custom_tab/arc_custom_tab_view.cc b/ash/custom_tab/arc_custom_tab_view.cc
index c22934b7..6b066c2 100644
--- a/ash/custom_tab/arc_custom_tab_view.cc
+++ b/ash/custom_tab/arc_custom_tab_view.cc
@@ -72,7 +72,7 @@
     return nullptr;
   }
   auto* parent = widget->widget_delegate()->GetContentsView();
-  auto* view = new ArcCustomTabView(surface_id, top_margin);
+  auto* view = new ArcCustomTabView(arc_app_window, surface_id, top_margin);
   parent->AddChildView(view);
   parent->SetLayoutManager(std::make_unique<views::FillLayout>());
   parent->Layout();
@@ -118,17 +118,32 @@
   window->parent()->StackChildAtTop(window);
 }
 
-ArcCustomTabView::ArcCustomTabView(int32_t surface_id, int32_t top_margin)
+void ArcCustomTabView::OnWindowHierarchyChanged(
+    const HierarchyChangeParams& params) {
+  auto* surface = exo::Surface::AsSurface(params.target);
+  if (surface && surface->GetClientSurfaceId() == surface_id_ &&
+      params.new_parent != nullptr) {
+    Layout();
+  }
+}
+
+ArcCustomTabView::ArcCustomTabView(aura::Window* arc_app_window,
+                                   int32_t surface_id,
+                                   int32_t top_margin)
     : binding_(this),
       remote_view_host_(new ws::ServerRemoteViewHost(
           ash::Shell::Get()->window_service_owner()->window_service())),
+      arc_app_window_(arc_app_window),
       surface_id_(surface_id),
       top_margin_(top_margin),
       weak_ptr_factory_(this) {
   AddChildView(remote_view_host_);
+  arc_app_window_->AddObserver(this);
 }
 
-ArcCustomTabView::~ArcCustomTabView() = default;
+ArcCustomTabView::~ArcCustomTabView() {
+  arc_app_window_->RemoveObserver(this);
+}
 
 void ArcCustomTabView::Bind(mojom::ArcCustomTabViewPtr* ptr) {
   binding_.Bind(mojo::MakeRequest(ptr));
diff --git a/ash/custom_tab/arc_custom_tab_view.h b/ash/custom_tab/arc_custom_tab_view.h
index 84d2b7f..f43fcff 100644
--- a/ash/custom_tab/arc_custom_tab_view.h
+++ b/ash/custom_tab/arc_custom_tab_view.h
@@ -11,12 +11,15 @@
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/ws/remote_view_host/server_remote_view_host.h"
+#include "ui/aura/window_observer.h"
 #include "ui/views/view.h"
 
 namespace ash {
 
 // Implementation of ArcCustomTabView interface.
-class ArcCustomTabView : public views::View, public mojom::ArcCustomTabView {
+class ArcCustomTabView : public views::View,
+                         public mojom::ArcCustomTabView,
+                         public aura::WindowObserver {
  public:
   // Creates a new ArcCustomTabView instance. The instance will be deleted when
   // the pointer is closed. Returns null when the arguments are invalid.
@@ -30,8 +33,13 @@
   // views::View:
   void Layout() override;
 
+  // aura::WindowObserver:
+  void OnWindowHierarchyChanged(const HierarchyChangeParams& params) override;
+
  private:
-  ArcCustomTabView(int32_t surface_id, int32_t top_margin);
+  ArcCustomTabView(aura::Window* arc_app_window,
+                   int32_t surface_id,
+                   int32_t top_margin);
   ~ArcCustomTabView() override;
 
   // Binds this instance to the pointer.
@@ -45,6 +53,7 @@
 
   mojo::Binding<mojom::ArcCustomTabView> binding_;
   ws::ServerRemoteViewHost* remote_view_host_;
+  aura::Window* arc_app_window_;
   int32_t surface_id_, top_margin_;
   base::WeakPtrFactory<ArcCustomTabView> weak_ptr_factory_;
 
diff --git a/ash/extended_desktop_unittest.cc b/ash/extended_desktop_unittest.cc
index db6a80a..be43f19 100644
--- a/ash/extended_desktop_unittest.cc
+++ b/ash/extended_desktop_unittest.cc
@@ -10,6 +10,7 @@
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/window_factory.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/root_window_finder.h"
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_util.h"
@@ -603,19 +604,21 @@
   wm::ActivateWindow(w1);
   // |w1_t1| is a transient child window of |w1|.
   std::unique_ptr<aura::Window> w1_t1 = CreateChildWindow(
-      w1, gfx::Rect(50, 50, 50, 50), kShellWindowId_DefaultContainer);
+      w1, gfx::Rect(50, 50, 50, 50), desks_util::GetActiveDeskContainerId());
   ::wm::AddTransientChild(w1, w1_t1.get());
   // |w1_t11| is a transient child window of transient child window |w1_t1|.
-  std::unique_ptr<aura::Window> w1_t11 = CreateChildWindow(
-      w1_t1.get(), gfx::Rect(2, 7, 35, 35), kShellWindowId_DefaultContainer);
+  std::unique_ptr<aura::Window> w1_t11 =
+      CreateChildWindow(w1_t1.get(), gfx::Rect(2, 7, 35, 35),
+                        desks_util::GetActiveDeskContainerId());
   ::wm::AddTransientChild(w1_t1.get(), w1_t11.get());
 
   // |w11| is a non-transient child window of |w1|.
   std::unique_ptr<aura::Window> w11 = CreateChildWindow(
-      w1, gfx::Rect(10, 10, 40, 40), kShellWindowId_DefaultContainer);
+      w1, gfx::Rect(10, 10, 40, 40), desks_util::GetActiveDeskContainerId());
   // |w11_t1| is a transient child window of |w11|.
-  std::unique_ptr<aura::Window> w11_t1 = CreateChildWindow(
-      w11.get(), gfx::Rect(30, 10, 80, 80), kShellWindowId_DefaultContainer);
+  std::unique_ptr<aura::Window> w11_t1 =
+      CreateChildWindow(w11.get(), gfx::Rect(30, 10, 80, 80),
+                        desks_util::GetActiveDeskContainerId());
   ::wm::AddTransientChild(w11.get(), w11_t1.get());
 
   EXPECT_EQ(root_windows[0], w1->GetRootWindow());
diff --git a/ash/first_run/desktop_cleaner.cc b/ash/first_run/desktop_cleaner.cc
index 0d54d33..cec78c9 100644
--- a/ash/first_run/desktop_cleaner.cc
+++ b/ash/first_run/desktop_cleaner.cc
@@ -8,6 +8,7 @@
 
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
+#include "ash/wm/desks/desks_util.h"
 #include "base/stl_util.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_observer.h"
@@ -19,12 +20,20 @@
 namespace ash {
 namespace {
 
-const int kContainerIdsToHide[] = {
-    kShellWindowId_DefaultContainer, kShellWindowId_AlwaysOnTopContainer,
-    // TODO(dzhioev): uncomment this when issue with BrowserView::CanActivate
-    // will be fixed.
-    // kShellWindowId_SystemModalContainer
-};
+std::vector<int> GetContainerIdsToHide() {
+  return std::vector<int>{
+      // Hide the active desk container. The inactive ones are already hidden.
+      // TODO(afakhry): Define the behavior of Virtual Desks during the first
+      // run tutorial whether it should be disabled or locked to the currently
+      // active desk.
+      desks_util::GetActiveDeskContainerId(),
+
+      kShellWindowId_AlwaysOnTopContainer,
+      // TODO(dzhioev): uncomment this when issue with BrowserView::CanActivate
+      // will be fixed.
+      // kShellWindowId_SystemModalContainer
+  };
+}
 
 }  // namespace
 
@@ -94,9 +103,8 @@
 DesktopCleaner::DesktopCleaner() {
   // TODO(dzhioev): Add support for secondary displays.
   aura::Window* root_window = Shell::Get()->GetPrimaryRootWindow();
-  for (size_t i = 0; i < base::size(kContainerIdsToHide); ++i) {
-    aura::Window* container =
-        Shell::GetContainer(root_window, kContainerIdsToHide[i]);
+  for (int id : GetContainerIdsToHide()) {
+    aura::Window* container = Shell::GetContainer(root_window, id);
     container_hiders_.push_back(std::make_unique<ContainerHider>(container));
   }
   notification_blocker_.reset(new NotificationBlocker());
@@ -106,9 +114,7 @@
 
 // static
 std::vector<int> DesktopCleaner::GetContainersToHideForTest() {
-  return std::vector<int>(
-      kContainerIdsToHide,
-      kContainerIdsToHide + base::size(kContainerIdsToHide));
+  return GetContainerIdsToHide();
 }
 
 }  // namespace ash
diff --git a/ash/frame/default_frame_header_unittest.cc b/ash/frame/default_frame_header_unittest.cc
index ef9b652..6a374906f 100644
--- a/ash/frame/default_frame_header_unittest.cc
+++ b/ash/frame/default_frame_header_unittest.cc
@@ -11,6 +11,7 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/desks/desks_util.h"
 #include "base/i18n/rtl.h"
 #include "base/test/icu_test_util.h"
 #include "ui/aura/window.h"
@@ -30,7 +31,7 @@
 // Ensure the title text is vertically aligned with the window icon.
 TEST_F(DefaultFrameHeaderTest, TitleIconAlignment) {
   std::unique_ptr<Widget> widget = CreateTestWidget(
-      nullptr, kShellWindowId_DefaultContainer, gfx::Rect(1, 2, 3, 4));
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect(1, 2, 3, 4));
   FrameCaptionButtonContainerView container(widget.get(), nullptr);
   views::StaticSizedView window_icon(gfx::Size(16, 16));
   window_icon.SetBounds(0, 0, 16, 16);
@@ -48,7 +49,7 @@
 
 TEST_F(DefaultFrameHeaderTest, BackButtonAlignment) {
   std::unique_ptr<Widget> widget = CreateTestWidget(
-      nullptr, kShellWindowId_DefaultContainer, gfx::Rect(1, 2, 3, 4));
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect(1, 2, 3, 4));
   FrameCaptionButtonContainerView container(widget.get(), nullptr);
   FrameBackButton back;
 
@@ -66,7 +67,7 @@
 TEST_F(DefaultFrameHeaderTest, MinimumHeaderWidthRTL) {
   base::test::ScopedRestoreICUDefaultLocale restore_locale;
   std::unique_ptr<Widget> widget = CreateTestWidget(
-      nullptr, kShellWindowId_DefaultContainer, gfx::Rect(1, 2, 3, 4));
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect(1, 2, 3, 4));
   FrameCaptionButtonContainerView container(widget.get(), nullptr);
 
   DefaultFrameHeader frame_header(
@@ -82,7 +83,7 @@
 // Ensure the right frame colors are used.
 TEST_F(DefaultFrameHeaderTest, FrameColors) {
   std::unique_ptr<Widget> widget = CreateTestWidget(
-      nullptr, kShellWindowId_DefaultContainer, gfx::Rect(1, 2, 3, 4));
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect(1, 2, 3, 4));
   FrameCaptionButtonContainerView container(widget.get(), nullptr);
   views::StaticSizedView window_icon(gfx::Size(16, 16));
   window_icon.SetBounds(0, 0, 16, 16);
diff --git a/ash/frame/non_client_frame_view_ash_unittest.cc b/ash/frame/non_client_frame_view_ash_unittest.cc
index 7315a6b..070d315 100644
--- a/ash/frame/non_client_frame_view_ash_unittest.cc
+++ b/ash/frame/non_client_frame_view_ash_unittest.cc
@@ -19,6 +19,7 @@
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_state.h"
@@ -404,8 +405,9 @@
 
 TEST_F(NonClientFrameViewAshTest, HeaderVisibilityInOverviewMode) {
   auto* delegate = new NonClientFrameViewAshTestWidgetDelegate();
-  std::unique_ptr<views::Widget> widget = CreateTestWidget(
-      delegate, kShellWindowId_DefaultContainer, gfx::Rect(0, 0, 400, 500));
+  std::unique_ptr<views::Widget> widget =
+      CreateTestWidget(delegate, desks_util::GetActiveDeskContainerId(),
+                       gfx::Rect(0, 0, 400, 500));
 
   // Verify the header is not painted in overview mode and painted when not in
   // overview mode.
@@ -513,8 +515,9 @@
   TestButtonModel* model_ptr = model.get();
 
   auto* delegate = new NonClientFrameViewAshTestWidgetDelegate();
-  std::unique_ptr<views::Widget> widget = CreateTestWidget(
-      delegate, kShellWindowId_DefaultContainer, gfx::Rect(0, 0, 400, 500));
+  std::unique_ptr<views::Widget> widget =
+      CreateTestWidget(delegate, desks_util::GetActiveDeskContainerId(),
+                       gfx::Rect(0, 0, 400, 500));
 
   ui::Accelerator accelerator_back_press(ui::VKEY_BROWSER_BACK, ui::EF_NONE);
   accelerator_back_press.set_key_state(ui::Accelerator::KeyState::PRESSED);
@@ -572,7 +575,7 @@
       new NonClientFrameViewAshTestWidgetDelegate;
   gfx::Rect window_bounds(10, 10, 200, 100);
   std::unique_ptr<views::Widget> widget = CreateTestWidget(
-      delegate, kShellWindowId_DefaultContainer, window_bounds);
+      delegate, desks_util::GetActiveDeskContainerId(), window_bounds);
 
   // The height is smaller by the top border height.
   gfx::Size client_bounds(200, 68);
@@ -677,8 +680,9 @@
 
 TEST_F(NonClientFrameViewAshTest, WideFrame) {
   auto* delegate = new NonClientFrameViewAshTestWidgetDelegate();
-  std::unique_ptr<views::Widget> widget = CreateTestWidget(
-      delegate, kShellWindowId_DefaultContainer, gfx::Rect(100, 0, 400, 500));
+  std::unique_ptr<views::Widget> widget =
+      CreateTestWidget(delegate, desks_util::GetActiveDeskContainerId(),
+                       gfx::Rect(100, 0, 400, 500));
 
   NonClientFrameViewAsh* non_client_frame_view =
       delegate->non_client_frame_view();
@@ -758,8 +762,9 @@
 
 TEST_F(NonClientFrameViewAshTest, WideFrameButton) {
   auto* delegate = new NonClientFrameViewAshTestWidgetDelegate();
-  std::unique_ptr<views::Widget> widget = CreateTestWidget(
-      delegate, kShellWindowId_DefaultContainer, gfx::Rect(100, 0, 400, 500));
+  std::unique_ptr<views::Widget> widget =
+      CreateTestWidget(delegate, desks_util::GetActiveDeskContainerId(),
+                       gfx::Rect(100, 0, 400, 500));
 
   std::unique_ptr<WideFrameView> wide_frame_view =
       std::make_unique<WideFrameView>(widget.get());
diff --git a/ash/media/media_notification_view.cc b/ash/media/media_notification_view.cc
index a4ca85b..ace6fe0 100644
--- a/ash/media/media_notification_view.cc
+++ b/ash/media/media_notification_view.cc
@@ -279,6 +279,7 @@
 
   title_label_->SetText(metadata.title);
   artist_label_->SetText(metadata.artist);
+  header_row_->SetSummaryText(metadata.album);
 
   if (!metadata.title.empty())
     RecordMetadataHistogram(Metadata::kTitle);
diff --git a/ash/media/media_notification_view_unittest.cc b/ash/media/media_notification_view_unittest.cc
index 8a2bd5b..f0785b0 100644
--- a/ash/media/media_notification_view_unittest.cc
+++ b/ash/media/media_notification_view_unittest.cc
@@ -535,6 +535,8 @@
   ExpectHistogramMetadataRecorded(MediaNotificationView::Metadata::kAlbum, 0);
   ExpectHistogramMetadataRecorded(MediaNotificationView::Metadata::kCount, 1);
 
+  EXPECT_FALSE(header_row()->summary_text_for_testing()->visible());
+
   media_session::MediaMetadata metadata;
   metadata.title = base::ASCIIToUTF16("title2");
   metadata.artist = base::ASCIIToUTF16("artist2");
@@ -545,9 +547,11 @@
   EXPECT_TRUE(title_artist_row()->visible());
   EXPECT_TRUE(title_label()->visible());
   EXPECT_TRUE(artist_label()->visible());
+  EXPECT_TRUE(header_row()->summary_text_for_testing()->visible());
 
   EXPECT_EQ(metadata.title, title_label()->text());
   EXPECT_EQ(metadata.artist, artist_label()->text());
+  EXPECT_EQ(metadata.album, header_row()->summary_text_for_testing()->text());
 
   EXPECT_EQ(kMediaTitleArtistRowExpectedHeight, title_artist_row()->height());
 
diff --git a/ash/metrics/user_metrics_recorder.cc b/ash/metrics/user_metrics_recorder.cc
index 2a9dd68..99f1c6a 100644
--- a/ash/metrics/user_metrics_recorder.cc
+++ b/ash/metrics/user_metrics_recorder.cc
@@ -5,6 +5,7 @@
 #include "ash/metrics/user_metrics_recorder.h"
 
 #include <memory>
+#include <vector>
 
 #include "ash/login/ui/lock_screen.h"
 #include "ash/metrics/demo_session_metrics_recorder.h"
@@ -19,6 +20,7 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shell.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_state.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
@@ -99,13 +101,17 @@
   return session->IsActiveUserSessionStarted() && !session->IsScreenLocked();
 }
 
-// Array of window container ids that contain visible windows to be counted for
-// UMA statistics. Note the containers are ordered from top most visible
-// container to the lowest to allow the |GetNumVisibleWindows| method to short
-// circuit when processing a maximized or fullscreen window.
-int kVisibleWindowContainerIds[] = {kShellWindowId_PipContainer,
-                                    kShellWindowId_AlwaysOnTopContainer,
-                                    kShellWindowId_DefaultContainer};
+// Returns a list of window container ids that contain visible windows to be
+// counted for UMA statistics. Note the containers are ordered from top most
+// visible container to the lowest to allow the |GetNumVisibleWindows| method to
+// short circuit when processing a maximized or fullscreen window.
+std::vector<int> GetVisibleWindowContainerIds() {
+  std::vector<int> ids{kShellWindowId_PipContainer,
+                       kShellWindowId_AlwaysOnTopContainer};
+  // TODO(afakhry): Add metrics for the inactive desks.
+  ids.emplace_back(desks_util::GetActiveDeskContainerId());
+  return ids;
+}
 
 // Returns an approximate count of how many windows are currently visible in the
 // primary root window.
@@ -113,7 +119,7 @@
   int visible_window_count = 0;
   bool maximized_or_fullscreen_window_present = false;
 
-  for (const int& current_container_id : kVisibleWindowContainerIds) {
+  for (const int& current_container_id : GetVisibleWindowContainerIds()) {
     if (maximized_or_fullscreen_window_present)
       break;
 
@@ -140,7 +146,7 @@
         ++visible_window_count;
 
       // Stop counting windows that will be hidden by maximized or fullscreen
-      // windows. Only windows in the kShellWindowId_DefaultContainer and
+      // windows. Only windows in the active desk container and
       // kShellWindowId_AlwaysOnTopContainer can be maximized or fullscreened
       // and completely obscure windows beneath them.
       if (child_window_state->IsMaximizedOrFullscreenOrPinned()) {
diff --git a/ash/public/cpp/frame_header.cc b/ash/public/cpp/frame_header.cc
index 2c9e7d1..0c45170e 100644
--- a/ash/public/cpp/frame_header.cc
+++ b/ash/public/cpp/frame_header.cc
@@ -62,11 +62,10 @@
 // widget's activation changes. Returns false if the header should switch to
 // new visuals instantaneously.
 bool CanAnimateActivation(views::Widget* widget) {
-  // Do not animate the header if the parent (e.g.
-  // kShellWindowId_DefaultContainer) is already animating. All of the
-  // implementers of FrameHeader animate activation by continuously painting
-  // during the animation. This gives the parent's animation a slower frame
-  // rate.
+  // Do not animate the header if the parent (e.g. the active desk container) is
+  // already animating. All of the implementers of FrameHeader animate
+  // activation by continuously painting during the animation. This gives the
+  // parent's animation a slower frame rate.
   // TODO(sky): Expose a better way to determine this rather than assuming the
   // parent is a toplevel container.
   aura::Window* window = widget->GetNativeWindow();
diff --git a/ash/public/cpp/shell_window_ids.cc b/ash/public/cpp/shell_window_ids.cc
index e969b41..ce4306e4 100644
--- a/ash/public/cpp/shell_window_ids.cc
+++ b/ash/public/cpp/shell_window_ids.cc
@@ -22,7 +22,7 @@
     kShellWindowId_SystemModalContainer,
     kShellWindowId_AlwaysOnTopContainer,
     kShellWindowId_AppListContainer,
-    kShellWindowId_DefaultContainer,
+    kShellWindowId_DefaultContainerDeprecated,
     kShellWindowId_HomeScreenContainer,
 
     // Launcher and status are intentionally checked after other containers
diff --git a/ash/public/cpp/shell_window_ids.h b/ash/public/cpp/shell_window_ids.h
index f7a1977..aae3e5b7 100644
--- a/ash/public/cpp/shell_window_ids.h
+++ b/ash/public/cpp/shell_window_ids.h
@@ -48,8 +48,12 @@
   // The wallpaper (desktop background) window.
   kShellWindowId_WallpaperContainer,
 
-  // The container for standard top-level windows.
-  kShellWindowId_DefaultContainer,
+  // The containers for standard top-level windows per active desks.
+  // Note: Do not use this container directly. Use
+  // `desks_util::GetActiveDeskContainerId()` instead.
+  // TODO(afakhry): Rename this container, unexpose it, and add the rest of the
+  // containers.
+  kShellWindowId_DefaultContainerDeprecated,
 
   // The container for top-level windows with the 'always-on-top' flag set.
   kShellWindowId_AlwaysOnTopContainer,
@@ -174,7 +178,7 @@
     kShellWindowId_UnparentedControlContainer,
     kShellWindowId_WallpaperContainer,
     kShellWindowId_VirtualKeyboardContainer,
-    kShellWindowId_DefaultContainer,
+    kShellWindowId_DefaultContainerDeprecated,
     kShellWindowId_AlwaysOnTopContainer,
     kShellWindowId_AppListContainer,
     kShellWindowId_HomeScreenContainer,
diff --git a/ash/public/interfaces/shell_test_api.test-mojom b/ash/public/interfaces/shell_test_api.test-mojom
index 718c0f4..f0239f01 100644
--- a/ash/public/interfaces/shell_test_api.test-mojom
+++ b/ash/public/interfaces/shell_test_api.test-mojom
@@ -9,6 +9,14 @@
   kExitAnimationComplete,
 };
 
+enum LauncherAnimationState {
+  kClosed,
+  kPeeking,
+  kHalf,
+  kFullscreenAllApps,
+  kFullscreenSearch
+};
+
 interface ShellTestApi {
   // Returns true if a system modal window is open (e.g. the Wi-Fi network
   // password dialog).
@@ -53,4 +61,8 @@
 
   // Runs the callback when the overview state becomes |state|.
   WaitForOverviewAnimationState(OverviewAnimationState state) => ();
+
+  // Runs the callback when the launcher state becomes |state| after
+  // state transition animation.
+  WaitForLauncherAnimationState(LauncherAnimationState state) => ();
 };
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 772063c..941da6f7 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -212,7 +212,8 @@
 void ReparentAllWindows(aura::Window* src, aura::Window* dst) {
   // Set of windows to move.
   const int kContainerIdsToMove[] = {
-      kShellWindowId_DefaultContainer,
+      // TODO(afakhry): Add rest of desks containers.
+      kShellWindowId_DefaultContainerDeprecated,
       kShellWindowId_AlwaysOnTopContainer,
       kShellWindowId_PipContainer,
       kShellWindowId_SystemModalContainer,
diff --git a/ash/screen_util.cc b/ash/screen_util.cc
index 5bc44ad..d8ad15c 100644
--- a/ash/screen_util.cc
+++ b/ash/screen_util.cc
@@ -10,6 +10,7 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/work_area_insets.h"
 #include "base/logging.h"
 #include "ui/aura/client/screen_position_client.h"
@@ -62,17 +63,17 @@
   return bounds;
 }
 
-gfx::Rect GetDisplayWorkAreaBoundsInParentForDefaultContainer(
+gfx::Rect GetDisplayWorkAreaBoundsInParentForActiveDeskContainer(
     aura::Window* window) {
   aura::Window* root_window = window->GetRootWindow();
   return GetDisplayWorkAreaBoundsInParent(
-      root_window->GetChildById(kShellWindowId_DefaultContainer));
+      desks_util::GetActiveDeskContainerForRoot(root_window));
 }
 
-gfx::Rect GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+gfx::Rect GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
     aura::Window* window) {
   gfx::Rect bounds =
-      GetDisplayWorkAreaBoundsInParentForDefaultContainer(window);
+      GetDisplayWorkAreaBoundsInParentForActiveDeskContainer(window);
   ::wm::ConvertRectToScreen(window->GetRootWindow(), &bounds);
   return bounds;
 }
diff --git a/ash/screen_util.h b/ash/screen_util.h
index 119f04e..dd39770 100644
--- a/ash/screen_util.h
+++ b/ash/screen_util.h
@@ -45,10 +45,10 @@
 ASH_EXPORT gfx::Rect GetDisplayWorkAreaBoundsInParentForLockScreen(
     aura::Window* window);
 
-// Returns the display's work area bounds on the default container.
-ASH_EXPORT gfx::Rect GetDisplayWorkAreaBoundsInParentForDefaultContainer(
+// Returns the display's work area bounds on the active desk container.
+ASH_EXPORT gfx::Rect GetDisplayWorkAreaBoundsInParentForActiveDeskContainer(
     aura::Window* window);
-ASH_EXPORT gfx::Rect GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+ASH_EXPORT gfx::Rect GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
     aura::Window* window);
 
 // Returns the bounds of the physical display containing the shelf for
diff --git a/ash/screen_util_unittest.cc b/ash/screen_util_unittest.cc
index bbc931bb..84a4514d 100644
--- a/ash/screen_util_unittest.cc
+++ b/ash/screen_util_unittest.cc
@@ -12,6 +12,7 @@
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
 #include "ui/aura/env.h"
@@ -231,7 +232,7 @@
   UpdateDisplay("1366x768");
 
   std::unique_ptr<aura::Window> window = CreateToplevelTestWindow(
-      gfx::Rect(300, 300, 200, 150), kShellWindowId_DefaultContainer);
+      gfx::Rect(300, 300, 200, 150), desks_util::GetActiveDeskContainerId());
 
   auto* docked_magnifier_controller =
       Shell::Get()->docked_magnifier_controller();
diff --git a/ash/shelf/shelf_window_watcher.cc b/ash/shelf/shelf_window_watcher.cc
index b4a15f1..a55a015f 100644
--- a/ash/shelf/shelf_window_watcher.cc
+++ b/ash/shelf/shelf_window_watcher.cc
@@ -13,6 +13,7 @@
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shelf/shelf_window_watcher_item_delegate.h"
 #include "ash/shell.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_util.h"
 #include "base/strings/string_util.h"
 #include "ui/aura/client/aura_constants.h"
@@ -93,8 +94,9 @@
 void ShelfWindowWatcher::ContainerWindowObserver::OnWindowHierarchyChanged(
     const HierarchyChangeParams& params) {
   if (!params.old_parent && params.new_parent &&
-      (params.new_parent->id() == kShellWindowId_DefaultContainer)) {
-    // A new window was created in the default container.
+      desks_util::IsDeskContainerId(params.new_parent->id())) {
+    // A new window was created in one of the desks' containers. Note that the
+    // shelf is globally showing all apps from all active and inactive desks.
     window_watcher_->OnUserWindowAdded(params.target);
   }
 }
@@ -263,11 +265,11 @@
 }
 
 void ShelfWindowWatcher::OnRootWindowAdded(aura::Window* root_window) {
-  aura::Window* container =
-      root_window->GetChildById(kShellWindowId_DefaultContainer);
-  for (aura::Window* window : container->children())
-    OnUserWindowAdded(window);
-  observed_container_windows_.Add(container);
+  for (aura::Window* container : desks_util::GetDesksContainers(root_window)) {
+    for (aura::Window* window : container->children())
+      OnUserWindowAdded(window);
+    observed_container_windows_.Add(container);
+  }
 }
 
 }  // namespace ash
diff --git a/ash/shelf/shelf_window_watcher_unittest.cc b/ash/shelf/shelf_window_watcher_unittest.cc
index c4c5768..f83bc90 100644
--- a/ash/shelf/shelf_window_watcher_unittest.cc
+++ b/ash/shelf/shelf_window_watcher_unittest.cc
@@ -15,6 +15,7 @@
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/window_factory.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_resizer.h"
 #include "ash/wm/window_state.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -74,12 +75,12 @@
 // Ensure shelf items are added and removed as windows are opened and closed.
 TEST_F(ShelfWindowWatcherTest, OpenAndClose) {
   // Windows with valid ShelfItemType and ShelfID properties get shelf items.
-  std::unique_ptr<views::Widget> widget1 =
-      CreateTestWidget(nullptr, kShellWindowId_DefaultContainer, gfx::Rect());
+  std::unique_ptr<views::Widget> widget1 = CreateTestWidget(
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
   CreateShelfItem(widget1->GetNativeWindow());
   EXPECT_EQ(3, model_->item_count());
-  std::unique_ptr<views::Widget> widget2 =
-      CreateTestWidget(nullptr, kShellWindowId_DefaultContainer, gfx::Rect());
+  std::unique_ptr<views::Widget> widget2 = CreateTestWidget(
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
   CreateShelfItem(widget2->GetNativeWindow());
   EXPECT_EQ(4, model_->item_count());
 
@@ -96,11 +97,11 @@
     return;
 
   // Windows with no valid ShelfItemType and ShelfID properties get shelf items.
-  std::unique_ptr<views::Widget> widget1 =
-      CreateTestWidget(nullptr, kShellWindowId_DefaultContainer, gfx::Rect());
+  std::unique_ptr<views::Widget> widget1 = CreateTestWidget(
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
   EXPECT_EQ(3, model_->item_count());
-  std::unique_ptr<views::Widget> widget2 =
-      CreateTestWidget(nullptr, kShellWindowId_DefaultContainer, gfx::Rect());
+  std::unique_ptr<views::Widget> widget2 = CreateTestWidget(
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
   EXPECT_EQ(4, model_->item_count());
 
   // Each ShelfItem is removed when the associated window is destroyed.
@@ -121,7 +122,7 @@
         window_factory::NewWindow(nullptr, type);
     window->Init(ui::LAYER_NOT_DRAWN);
     Shell::GetPrimaryRootWindow()
-        ->GetChildById(kShellWindowId_DefaultContainer)
+        ->GetChildById(desks_util::GetActiveDeskContainerId())
         ->AddChild(window.get());
     window->Show();
     EXPECT_EQ(type == aura::client::WINDOW_TYPE_NORMAL ? 3 : 2,
@@ -131,10 +132,10 @@
 
 TEST_F(ShelfWindowWatcherTest, CreateAndRemoveShelfItemProperties) {
   // Creating windows without a valid ShelfItemType only adds items in mash.
-  std::unique_ptr<views::Widget> widget1 =
-      CreateTestWidget(nullptr, kShellWindowId_DefaultContainer, gfx::Rect());
-  std::unique_ptr<views::Widget> widget2 =
-      CreateTestWidget(nullptr, kShellWindowId_DefaultContainer, gfx::Rect());
+  std::unique_ptr<views::Widget> widget1 = CreateTestWidget(
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
+  std::unique_ptr<views::Widget> widget2 = CreateTestWidget(
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
   const bool is_mash = ::features::IsMultiProcessMash();
   EXPECT_EQ(is_mash ? 4 : 2, model_->item_count());
 
@@ -173,8 +174,8 @@
 
 TEST_F(ShelfWindowWatcherTest, UpdateWindowProperty) {
   // Create a ShelfItem for a new window.
-  std::unique_ptr<views::Widget> widget =
-      CreateTestWidget(nullptr, kShellWindowId_DefaultContainer, gfx::Rect());
+  std::unique_ptr<views::Widget> widget = CreateTestWidget(
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
   ShelfID id = CreateShelfItem(widget->GetNativeWindow());
   EXPECT_EQ(3, model_->item_count());
 
@@ -190,8 +191,8 @@
 
 TEST_F(ShelfWindowWatcherTest, MaximizeAndRestoreWindow) {
   // Create a ShelfItem for a new window.
-  std::unique_ptr<views::Widget> widget =
-      CreateTestWidget(nullptr, kShellWindowId_DefaultContainer, gfx::Rect());
+  std::unique_ptr<views::Widget> widget = CreateTestWidget(
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
   ShelfID id = CreateShelfItem(widget->GetNativeWindow());
   EXPECT_EQ(3, model_->item_count());
 
@@ -223,8 +224,8 @@
 // TODO(simonhong): Add a test for removing a Window during the dragging.
 TEST_F(ShelfWindowWatcherTest, DragWindow) {
   // Create a ShelfItem for a new window.
-  std::unique_ptr<views::Widget> widget =
-      CreateTestWidget(nullptr, kShellWindowId_DefaultContainer, gfx::Rect());
+  std::unique_ptr<views::Widget> widget = CreateTestWidget(
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
   ShelfID id = CreateShelfItem(widget->GetNativeWindow());
   EXPECT_EQ(3, model_->item_count());
 
@@ -247,16 +248,16 @@
 // Ensure dialogs get shelf items.
 TEST_F(ShelfWindowWatcherTest, DialogWindows) {
   // An item is created for a dialog window.
-  std::unique_ptr<views::Widget> dialog_widget =
-      CreateTestWidget(nullptr, kShellWindowId_DefaultContainer, gfx::Rect());
+  std::unique_ptr<views::Widget> dialog_widget = CreateTestWidget(
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
   aura::Window* dialog = dialog_widget->GetNativeWindow();
   dialog->SetProperty(kShelfIDKey, new std::string(ShelfID("a").Serialize()));
   dialog->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
   EXPECT_EQ(3, model_->item_count());
 
   // An item is not created for an app window.
-  std::unique_ptr<views::Widget> app_widget =
-      CreateTestWidget(nullptr, kShellWindowId_DefaultContainer, gfx::Rect());
+  std::unique_ptr<views::Widget> app_widget = CreateTestWidget(
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
   aura::Window* app = app_widget->GetNativeWindow();
   app->SetProperty(kShelfIDKey, new std::string(ShelfID("c").Serialize()));
   app->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_APP));
@@ -271,8 +272,8 @@
 // Ensure items use the app icon and window icon aura::Window properties.
 TEST_F(ShelfWindowWatcherTest, ItemIcon) {
   // Create a ShelfItem for a window; it should have a default icon.
-  std::unique_ptr<views::Widget> widget =
-      CreateTestWidget(nullptr, kShellWindowId_DefaultContainer, gfx::Rect());
+  std::unique_ptr<views::Widget> widget = CreateTestWidget(
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
   aura::Window* window = widget->GetNativeWindow();
   ShelfID id = CreateShelfItem(window);
   EXPECT_EQ(3, model_->item_count());
@@ -303,7 +304,7 @@
   window->SetProperty(kShelfIDKey, new std::string(ShelfID("a").Serialize()));
   window->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
   Shell::GetPrimaryRootWindow()
-      ->GetChildById(kShellWindowId_DefaultContainer)
+      ->GetChildById(desks_util::GetActiveDeskContainerId())
       ->AddChild(window.get());
   window->Show();
   EXPECT_EQ(3, model_->item_count());
@@ -331,7 +332,7 @@
   window->SetProperty(kShelfIDKey, new std::string(ShelfID("a").Serialize()));
   window->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
   Shell::GetPrimaryRootWindow()
-      ->GetChildById(kShellWindowId_DefaultContainer)
+      ->GetChildById(desks_util::GetActiveDeskContainerId())
       ->AddChild(window.get());
   window->Show();
   EXPECT_EQ(3, model_->item_count());
@@ -343,7 +344,7 @@
                          new std::string(ShelfID("b").Serialize()));
   transient->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
   Shell::GetPrimaryRootWindow()
-      ->GetChildById(kShellWindowId_DefaultContainer)
+      ->GetChildById(desks_util::GetActiveDeskContainerId())
       ->AddChild(transient.get());
   ::wm::TransientWindowController::Get()->AddTransientChild(window.get(),
                                                             transient.get());
@@ -368,8 +369,8 @@
   EXPECT_EQ(2, model->item_count());
 
   // Construct a window that should get a shelf item once the session starts.
-  std::unique_ptr<views::Widget> widget =
-      CreateTestWidget(nullptr, kShellWindowId_DefaultContainer, gfx::Rect());
+  std::unique_ptr<views::Widget> widget = CreateTestWidget(
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
   ShelfWindowWatcherTest::CreateShelfItem(widget->GetNativeWindow());
   EXPECT_EQ(2, model->item_count());
 
diff --git a/ash/shell/window_watcher.cc b/ash/shell/window_watcher.cc
index 01a4c78..6ac7220 100644
--- a/ash/shell/window_watcher.cc
+++ b/ash/shell/window_watcher.cc
@@ -14,6 +14,7 @@
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/shell/window_watcher_shelf_item_delegate.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -42,19 +43,20 @@
   }
 
   void RootWindowAdded(aura::Window* root) {
-    aura::Window* container =
-        root->GetChildById(kShellWindowId_DefaultContainer);
-    container->AddObserver(watcher_);
-    for (aura::Window* window : container->children())
-      watcher_->OnWindowAdded(window);
+    // The shelf is globally observing all active and inactive desks containers.
+    for (aura::Window* container : desks_util::GetDesksContainers(root)) {
+      container->AddObserver(watcher_);
+      for (aura::Window* window : container->children())
+        watcher_->OnWindowAdded(window);
+    }
   }
 
   void RootWindowRemoved(aura::Window* root) {
-    aura::Window* container =
-        root->GetChildById(kShellWindowId_DefaultContainer);
-    container->RemoveObserver(watcher_);
-    for (aura::Window* window : container->children())
-      watcher_->OnWillRemoveWindow(window);
+    for (aura::Window* container : desks_util::GetDesksContainers(root)) {
+      container->RemoveObserver(watcher_);
+      for (aura::Window* window : container->children())
+        watcher_->OnWillRemoveWindow(window);
+    }
   }
 
  private:
diff --git a/ash/shell_test_api.cc b/ash/shell_test_api.cc
index 177f4eb..6d83764 100644
--- a/ash/shell_test_api.cc
+++ b/ash/shell_test_api.cc
@@ -105,6 +105,54 @@
   DISALLOW_COPY_AND_ASSIGN(OverviewAnimationStateWaiter);
 };
 
+// A waiter that waits until the animation ended with the target state, and
+// execute the callback.  This self destruction upon completion.
+class LauncherStateWaiter {
+ public:
+  LauncherStateWaiter(
+      mojom::LauncherAnimationState state,
+      ShellTestApi::WaitForLauncherAnimationStateCallback callback)
+      : callback_(std::move(callback)) {
+    switch (state) {
+      case mojom::LauncherAnimationState::kClosed:
+        target_state_ = app_list::AppListViewState::CLOSED;
+        break;
+      case mojom::LauncherAnimationState::kPeeking:
+        target_state_ = app_list::AppListViewState::PEEKING;
+        break;
+      case mojom::LauncherAnimationState::kHalf:
+        target_state_ = app_list::AppListViewState::HALF;
+        break;
+      case mojom::LauncherAnimationState::kFullscreenAllApps:
+        target_state_ = app_list::AppListViewState::FULLSCREEN_ALL_APPS;
+        break;
+      case mojom::LauncherAnimationState::kFullscreenSearch:
+        target_state_ = app_list::AppListViewState::FULLSCREEN_SEARCH;
+        break;
+    };
+    Shell::Get()->app_list_controller()->SetStateTransitionAnimationCallback(
+        base::BindRepeating(&LauncherStateWaiter::OnStateChanged,
+                            base::Unretained(this)));
+  }
+  ~LauncherStateWaiter() {
+    Shell::Get()->app_list_controller()->SetStateTransitionAnimationCallback(
+        base::NullCallback());
+  }
+
+  void OnStateChanged(app_list::AppListViewState state) {
+    if (target_state_ == state) {
+      std::move(callback_).Run();
+      delete this;
+    }
+  }
+
+ private:
+  app_list::AppListViewState target_state_;
+  ShellTestApi::WaitForLauncherAnimationStateCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(LauncherStateWaiter);
+};
+
 }  // namespace
 
 ShellTestApi::ShellTestApi() : ShellTestApi(Shell::Get()) {}
@@ -261,4 +309,10 @@
   new OverviewAnimationStateWaiter(state, std::move(callback));
 }
 
+void ShellTestApi::WaitForLauncherAnimationState(
+    mojom::LauncherAnimationState target_state,
+    WaitForLauncherAnimationStateCallback callback) {
+  new LauncherStateWaiter(target_state, std::move(callback));
+}
+
 }  // namespace ash
diff --git a/ash/shell_test_api.h b/ash/shell_test_api.h
index 040747a..ee9e6fef 100644
--- a/ash/shell_test_api.h
+++ b/ash/shell_test_api.h
@@ -70,6 +70,9 @@
   void WaitForOverviewAnimationState(
       mojom::OverviewAnimationState state,
       WaitForOverviewAnimationStateCallback callback) override;
+  void WaitForLauncherAnimationState(
+      mojom::LauncherAnimationState state,
+      WaitForLauncherAnimationStateCallback callback) override;
 
  private:
   Shell* shell_;  // not owned
diff --git a/ash/shell_unittest.cc b/ash/shell_unittest.cc
index a68fd576..4b79e80 100644
--- a/ash/shell_unittest.cc
+++ b/ash/shell_unittest.cc
@@ -25,6 +25,7 @@
 #include "ash/test_shell_delegate.h"
 #include "ash/wallpaper/wallpaper_widget_controller.h"
 #include "ash/window_factory.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_util.h"
 #include "base/command_line.h"
 #include "base/stl_util.h"
@@ -59,9 +60,9 @@
 
 namespace {
 
-aura::Window* GetDefaultContainer() {
+aura::Window* GetActiveDeskContainer() {
   return Shell::GetContainer(Shell::GetPrimaryRootWindow(),
-                             kShellWindowId_DefaultContainer);
+                             desks_util::GetActiveDeskContainerId());
 }
 
 aura::Window* GetAlwaysOnTopContainer() {
@@ -80,8 +81,9 @@
   aura::Window* root_window = Shell::GetPrimaryRootWindow();
   EXPECT_TRUE(
       Shell::GetContainer(root_window, kShellWindowId_WallpaperContainer));
-  EXPECT_TRUE(
-      Shell::GetContainer(root_window, kShellWindowId_DefaultContainer));
+  // TODO(afakhry): Test rest of desks containers.
+  EXPECT_TRUE(Shell::GetContainer(root_window,
+                                  kShellWindowId_DefaultContainerDeprecated));
   EXPECT_TRUE(
       Shell::GetContainer(root_window, kShellWindowId_AlwaysOnTopContainer));
   EXPECT_TRUE(Shell::GetContainer(root_window, kShellWindowId_ShelfContainer));
@@ -238,10 +240,10 @@
   // Normal window should be created in default container.
   TestCreateWindow(views::Widget::InitParams::TYPE_WINDOW,
                    false,  // always_on_top
-                   GetDefaultContainer());
+                   GetActiveDeskContainer());
   TestCreateWindow(views::Widget::InitParams::TYPE_POPUP,
                    false,  // always_on_top
-                   GetDefaultContainer());
+                   GetActiveDeskContainer());
 
   // Always-on-top window and popup are created in always-on-top container.
   TestCreateWindow(views::Widget::InitParams::TYPE_WINDOW,
@@ -283,9 +285,9 @@
   views::Widget* widget = CreateTestWindow(widget_params);
   widget->Show();
 
-  // It should be in default container.
+  // It should be in the active desk container.
   EXPECT_TRUE(
-      GetDefaultContainer()->Contains(widget->GetNativeWindow()->parent()));
+      GetActiveDeskContainer()->Contains(widget->GetNativeWindow()->parent()));
 
   // Flip always-on-top flag.
   widget->SetAlwaysOnTop(true);
@@ -294,15 +296,15 @@
 
   // Flip always-on-top flag.
   widget->SetAlwaysOnTop(false);
-  // It should go back to default container.
+  // It should go back to the active desk container.
   EXPECT_TRUE(
-      GetDefaultContainer()->Contains(widget->GetNativeWindow()->parent()));
+      GetActiveDeskContainer()->Contains(widget->GetNativeWindow()->parent()));
 
   // Set the same always-on-top flag again.
   widget->SetAlwaysOnTop(false);
-  // Should have no effect and we are still in the default container.
+  // Should have no effect and we are still in the the active desk container.
   EXPECT_TRUE(
-      GetDefaultContainer()->Contains(widget->GetNativeWindow()->parent()));
+      GetActiveDeskContainer()->Contains(widget->GetNativeWindow()->parent()));
 
   widget->Close();
 }
@@ -315,9 +317,9 @@
   views::Widget* widget = CreateTestWindow(widget_params);
   widget->Show();
 
-  // It should be in default container.
+  // It should be in the active desk container.
   EXPECT_TRUE(
-      GetDefaultContainer()->Contains(widget->GetNativeWindow()->parent()));
+      GetActiveDeskContainer()->Contains(widget->GetNativeWindow()->parent()));
 
   // Create a modal window.
   views::Widget* modal_widget = views::Widget::CreateWindowWithParent(
@@ -350,9 +352,9 @@
   widget->Show();
   EXPECT_TRUE(widget->GetNativeView()->HasFocus());
 
-  // It should be in default container.
+  // It should be in the active desk container.
   EXPECT_TRUE(
-      GetDefaultContainer()->Contains(widget->GetNativeWindow()->parent()));
+      GetActiveDeskContainer()->Contains(widget->GetNativeWindow()->parent()));
 
   Shell::Get()->session_controller()->LockScreenAndFlushForTest();
   // Create a LockScreen window.
diff --git a/ash/system/message_center/arc/arc_notification_view_unittest.cc b/ash/system/message_center/arc/arc_notification_view_unittest.cc
index f51978ea..780ee977 100644
--- a/ash/system/message_center/arc/arc_notification_view_unittest.cc
+++ b/ash/system/message_center/arc/arc_notification_view_unittest.cc
@@ -13,6 +13,7 @@
 #include "ash/system/message_center/arc/mock_arc_notification_item.h"
 #include "ash/system/message_center/arc/mock_arc_notification_surface.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/desks/desks_util.h"
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -90,7 +91,7 @@
         views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
     init_params.context = CurrentContext();
     init_params.parent = Shell::GetPrimaryRootWindow()->GetChildById(
-        kShellWindowId_DefaultContainer);
+        desks_util::GetActiveDeskContainerId());
     init_params.ownership =
         views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     views::Widget* widget = new views::Widget();
diff --git a/ash/system/message_center/ash_popup_alignment_delegate_unittest.cc b/ash/system/message_center/ash_popup_alignment_delegate_unittest.cc
index 23be4b98..5c3a42b 100644
--- a/ash/system/message_center/ash_popup_alignment_delegate_unittest.cc
+++ b/ash/system/message_center/ash_popup_alignment_delegate_unittest.cc
@@ -14,6 +14,7 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/desks/desks_util.h"
 #include "base/command_line.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/screen.h"
@@ -143,7 +144,7 @@
 
   // Create a window, otherwise autohide doesn't work.
   std::unique_ptr<views::Widget> widget = CreateTestWidget(
-      nullptr, kShellWindowId_DefaultContainer, gfx::Rect(0, 0, 50, 50));
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect(0, 0, 50, 50));
   Shelf* shelf = GetPrimaryShelf();
   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
   EXPECT_EQ(origin_x, alignment_delegate()->GetToastOriginX(toast_size));
diff --git a/ash/system/power/power_button_screenshot_controller.cc b/ash/system/power/power_button_screenshot_controller.cc
index 8359015..509eebc1 100644
--- a/ash/system/power/power_button_screenshot_controller.cc
+++ b/ash/system/power/power_button_screenshot_controller.cc
@@ -108,8 +108,10 @@
 
       if (!volume_down_timer_.IsRunning()) {
         volume_down_timer_.Start(
-            FROM_HERE, kScreenshotChordDelay, this,
-            &PowerButtonScreenshotController::OnVolumeDownTimeout);
+            FROM_HERE, kScreenshotChordDelay,
+            base::BindOnce(
+                &PowerButtonScreenshotController::OnVolumeDownTimeout,
+                base::Unretained(this), ui::Accelerator(*event)));
       }
     }
   }
@@ -140,8 +142,10 @@
   return false;
 }
 
-void PowerButtonScreenshotController::OnVolumeDownTimeout() {
-  Shell::Get()->accelerator_controller()->PerformActionIfEnabled(VOLUME_DOWN);
+void PowerButtonScreenshotController::OnVolumeDownTimeout(
+    const ui::Accelerator& accelerator) {
+  Shell::Get()->accelerator_controller()->PerformActionIfEnabled(VOLUME_DOWN,
+                                                                 accelerator);
 }
 
 }  // namespace ash
diff --git a/ash/system/power/power_button_screenshot_controller.h b/ash/system/power/power_button_screenshot_controller.h
index 156ed27a..36ed8e3 100644
--- a/ash/system/power/power_button_screenshot_controller.h
+++ b/ash/system/power/power_button_screenshot_controller.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "ui/base/accelerators/accelerator.h"
 #include "ui/events/event_handler.h"
 
 namespace base {
@@ -47,7 +48,7 @@
   bool InterceptScreenshotChord();
 
   // Called by |volume_down_timer_| to perform volume down accelerator.
-  void OnVolumeDownTimeout();
+  void OnVolumeDownTimeout(const ui::Accelerator& accelerator);
 
   // True if volume down key is pressed.
   bool volume_down_key_pressed_ = false;
diff --git a/ash/system/session/logout_confirmation_controller.cc b/ash/system/session/logout_confirmation_controller.cc
index 1644bb4..cd63a96 100644
--- a/ash/system/session/logout_confirmation_controller.cc
+++ b/ash/system/session/logout_confirmation_controller.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <utility>
+#include <vector>
 
 #include "ash/login_status.h"
 #include "ash/public/cpp/shell_window_ids.h"
@@ -13,6 +14,7 @@
 #include "ash/shell.h"
 #include "ash/shell_observer.h"
 #include "ash/system/session/logout_confirmation_dialog.h"
+#include "ash/wm/desks/desks_util.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/location.h"
@@ -27,10 +29,13 @@
 namespace {
 const int kLogoutConfirmationDelayInSeconds = 20;
 
-// Shell window containers monitored for when the last window closes.
-const int kLastWindowClosedContainerIds[] = {
-    kShellWindowId_DefaultContainer, kShellWindowId_AlwaysOnTopContainer,
-    kShellWindowId_PipContainer};
+std::vector<int> GetLastWindowClosedContainerIds() {
+  const auto& desks_ids = desks_util::GetDesksContainersIds();
+  std::vector<int> ids{desks_ids.begin(), desks_ids.end()};
+  ids.emplace_back(kShellWindowId_AlwaysOnTopContainer);
+  ids.emplace_back(kShellWindowId_PipContainer);
+  return ids;
+}
 
 void SignOut(LogoutConfirmationController::Source source) {
   if (Shell::Get()->session_controller()->IsDemoSession() &&
@@ -62,7 +67,7 @@
   ~LastWindowClosedObserver() override {
     // Stop observing all displays.
     for (aura::Window* root : Shell::GetAllRootWindows()) {
-      for (int id : kLastWindowClosedContainerIds)
+      for (int id : GetLastWindowClosedContainerIds())
         root->GetChildById(id)->RemoveObserver(this);
     }
     Shell::Get()->RemoveShellObserver(this);
@@ -72,7 +77,7 @@
   // Observes containers in the |root| window for the last browser and/or app
   // window being closed. The observers are removed automatically.
   void ObserveForLastWindowClosed(aura::Window* root) {
-    for (int id : kLastWindowClosedContainerIds)
+    for (int id : GetLastWindowClosedContainerIds())
       root->GetChildById(id)->AddObserver(this);
   }
 
@@ -87,7 +92,7 @@
     // Enumerate all root windows.
     for (aura::Window* root : Shell::GetAllRootWindows()) {
       // For each root window enumerate tracked containers.
-      for (int id : kLastWindowClosedContainerIds) {
+      for (int id : GetLastWindowClosedContainerIds()) {
         // In each container try to find child window that is not equal to
         // |closing_window| which would indicate that we have other top-level
         // window and logout time does not apply.
diff --git a/ash/system/session/logout_confirmation_controller_unittest.cc b/ash/system/session/logout_confirmation_controller_unittest.cc
index fe2bd540..c74750c 100644
--- a/ash/system/session/logout_confirmation_controller_unittest.cc
+++ b/ash/system/session/logout_confirmation_controller_unittest.cc
@@ -7,6 +7,7 @@
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/desks/desks_util.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/memory/ref_counted.h"
@@ -319,10 +320,10 @@
   UpdateDisplay("1024x768,800x600");
   std::unique_ptr<aura::Window> window1 =
       CreateChildWindow(Shell::GetAllRootWindows()[0]->GetChildById(
-          kShellWindowId_DefaultContainer));
+          desks_util::GetActiveDeskContainerId()));
   std::unique_ptr<aura::Window> window2 =
       CreateChildWindow(Shell::GetAllRootWindows()[1]->GetChildById(
-          kShellWindowId_DefaultContainer));
+          desks_util::GetActiveDeskContainerId()));
 
   // Closing the last window shows the dialog.
   window1.reset();
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index cbe1cca..4c1f7a4 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -183,10 +183,11 @@
 constexpr int kUnifiedTrayContentPadding = 8;
 constexpr int kUnifiedTopShortcutSpacing = 16;
 constexpr int kUnifiedNotificationHiddenLineHeight = 20;
+constexpr int kUnifiedTopShortcutContainerTopPadding = 12;
 constexpr int kUnifiedNotificationMinimumHeight = 40;
 constexpr gfx::Insets kUnifiedTopShortcutPadding(0, 16);
 constexpr gfx::Insets kUnifiedNotificationHiddenPadding(6, 16);
-
+constexpr gfx::Insets kUnifiedCircularButtonFocusPadding(4);
 constexpr int kStackingNotificationCounterMax = 8;
 constexpr int kStackingNotificationCounterRadius = 2;
 constexpr int kStackingNotificationCounterStartX = 18;
@@ -220,6 +221,8 @@
 constexpr int kUnifiedFeaturePodSpacing = 6;
 constexpr int kUnifiedFeaturePodHoverRadius = 4;
 constexpr int kUnifiedFeaturePodVerticalPadding = 28;
+constexpr int kUnifiedFeaturePodTopPadding = 24;
+constexpr int kUnifiedFeaturePodBottomPadding = 24;
 constexpr int kUnifiedFeaturePodHorizontalSidePadding = 12;
 constexpr int kUnifiedFeaturePodHorizontalMiddlePadding = 0;
 constexpr int kUnifiedFeaturePodCollapsedVerticalPadding = 16;
diff --git a/ash/system/unified/feature_pods_container_view.cc b/ash/system/unified/feature_pods_container_view.cc
index dea9f00..5b06311 100644
--- a/ash/system/unified/feature_pods_container_view.cc
+++ b/ash/system/unified/feature_pods_container_view.cc
@@ -39,7 +39,7 @@
   // floor(visible_count / kUnifiedFeaturePodItemsInRow)
   int number_of_lines = (visible_count + kUnifiedFeaturePodItemsInRow - 1) /
                         kUnifiedFeaturePodItemsInRow;
-  return kUnifiedFeaturePodVerticalPadding +
+  return kUnifiedFeaturePodBottomPadding +
          (kUnifiedFeaturePodVerticalPadding + kUnifiedFeaturePodSize.height()) *
              number_of_lines;
 }
@@ -145,10 +145,9 @@
           (kUnifiedFeaturePodSize.width() +
            kUnifiedFeaturePodHorizontalMiddlePadding) *
               column;
-  int y =
-      kUnifiedFeaturePodVerticalPadding +
-      (kUnifiedFeaturePodSize.height() + kUnifiedFeaturePodVerticalPadding) *
-          row;
+  int y = kUnifiedFeaturePodTopPadding + (kUnifiedFeaturePodSize.height() +
+                                          kUnifiedFeaturePodVerticalPadding) *
+                                             row;
 
   // When fully expanded, or below the second row, always return the same
   // position.
diff --git a/ash/system/unified/top_shortcuts_view.cc b/ash/system/unified/top_shortcuts_view.cc
index 6d9b5ba..eba1261 100644
--- a/ash/system/unified/top_shortcuts_view.cc
+++ b/ash/system/unified/top_shortcuts_view.cc
@@ -21,7 +21,7 @@
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
-
+#include "ui/views/view_class_properties.h"
 namespace ash {
 
 namespace {
@@ -38,11 +38,19 @@
 UserAvatarButton::UserAvatarButton(views::ButtonListener* listener)
     : Button(listener) {
   SetLayoutManager(std::make_unique<views::FillLayout>());
+  SetBorder(views::CreateEmptyBorder(kUnifiedCircularButtonFocusPadding));
   AddChildView(CreateUserAvatarView(0 /* user_index */));
 
   SetTooltipText(GetUserItemAccessibleString(0 /* user_index */));
-  SetFocusPainter(TrayPopupUtils::CreateFocusPainter());
+  SetInstallFocusRingOnFocus(true);
   SetFocusForPlatform();
+
+  int focus_ring_radius =
+      kTrayItemSize + kUnifiedCircularButtonFocusPadding.width();
+  auto path = std::make_unique<SkPath>();
+  path->addOval(gfx::RectToSkRect(
+      gfx::Rect(gfx::Size(focus_ring_radius, focus_ring_radius))));
+  SetProperty(views::kHighlightPathKey, path.release());
 }
 
 }  // namespace
@@ -89,15 +97,36 @@
   }
 
   int horizontal_position = child_area.x();
-  for (int i = 0; i < child_count(); i++) {
+
+  if (user_avatar_button_ && user_avatar_button_->visible()) {
+    int vertical_position =
+        child_area.y() + kUnifiedTopShortcutContainerTopPadding;
+    horizontal_position -= kUnifiedCircularButtonFocusPadding.left();
+
+    gfx::Size size = user_avatar_button_->GetPreferredSize();
+    gfx::Rect user_avatar_bounds(horizontal_position, vertical_position,
+                                 size.width(), size.height());
+    user_avatar_button_->SetBoundsRect(user_avatar_bounds);
+
+    horizontal_position += user_avatar_bounds.size().width() + spacing -
+                           kUnifiedCircularButtonFocusPadding.right();
+  }
+
+  int vertical_position = child_area.y() +
+                          kUnifiedTopShortcutContainerTopPadding +
+                          kUnifiedCircularButtonFocusPadding.bottom();
+  for (int i = 1; i < child_count(); i++) {
     views::View* child = child_at(i);
     if (!child->visible())
       continue;
     gfx::Rect bounds(child_area);
     bounds.set_x(horizontal_position);
+    bounds.set_y(vertical_position);
+
     int width = (child == sign_out_button_) ? sign_out_button_width
                                             : child->GetPreferredSize().width();
     bounds.set_width(width);
+    bounds.set_height(child->GetHeightForWidth(width));
     child->SetBoundsRect(bounds);
     horizontal_position += width + spacing;
   }
@@ -105,7 +134,6 @@
 
 gfx::Size TopShortcutButtonContainer::CalculatePreferredSize() const {
   int total_horizontal_size = 0;
-  int max_height = 0;
   int num_visible = 0;
   for (int i = 0; i < child_count(); i++) {
     const views::View* child = child_at(i);
@@ -116,14 +144,21 @@
       continue;
     total_horizontal_size += child_horizontal_size;
     num_visible++;
-    max_height = std::max(child->GetPreferredSize().height(), max_height);
   }
   int width =
       (num_visible == 0)
           ? 0
           : total_horizontal_size +
                 (num_visible - 1) * kUnifiedTopShortcutButtonDefaultSpacing;
-  return gfx::Size(width, max_height);
+  int height = kTrayItemSize + kUnifiedCircularButtonFocusPadding.height() +
+               kUnifiedTopShortcutContainerTopPadding;
+  return gfx::Size(width, height);
+}
+
+void TopShortcutButtonContainer::AddUserAvatarButton(
+    views::View* user_avatar_button) {
+  AddChildView(user_avatar_button);
+  user_avatar_button_ = user_avatar_button;
 }
 
 void TopShortcutButtonContainer::AddSignOutButton(
@@ -139,7 +174,8 @@
   auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::kHorizontal, kUnifiedTopShortcutPadding,
       kUnifiedTopShortcutSpacing));
-  layout->set_cross_axis_alignment(views::BoxLayout::CROSS_AXIS_ALIGNMENT_END);
+  layout->set_cross_axis_alignment(
+      views::BoxLayout::CROSS_AXIS_ALIGNMENT_START);
   container_ = new TopShortcutButtonContainer();
   AddChildView(container_);
 
@@ -147,7 +183,7 @@
       LoginStatus::NOT_LOGGED_IN) {
     user_avatar_button_ = new UserAvatarButton(this);
     user_avatar_button_->SetEnabled(controller->IsUserChooserEnabled());
-    container_->AddChildView(user_avatar_button_);
+    container_->AddUserAvatarButton(user_avatar_button_);
   }
 
   // Show the buttons in this row as disabled if the user is at the login
diff --git a/ash/system/unified/top_shortcuts_view.h b/ash/system/unified/top_shortcuts_view.h
index 8856183..d06f867 100644
--- a/ash/system/unified/top_shortcuts_view.h
+++ b/ash/system/unified/top_shortcuts_view.h
@@ -30,10 +30,12 @@
   void Layout() override;
   gfx::Size CalculatePreferredSize() const override;
 
+  void AddUserAvatarButton(views::View* user_avatar_button);
   // Add the sign-out button, which can be resized upon layout.
   void AddSignOutButton(views::View* sign_out_button);
 
  private:
+  views::View* user_avatar_button_ = nullptr;
   views::View* sign_out_button_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(TopShortcutButtonContainer);
diff --git a/ash/system/unified/unified_system_tray_bubble.cc b/ash/system/unified/unified_system_tray_bubble.cc
index e535c91..e7096a8 100644
--- a/ash/system/unified/unified_system_tray_bubble.cc
+++ b/ash/system/unified/unified_system_tray_bubble.cc
@@ -5,6 +5,7 @@
 #include "ash/system/unified/unified_system_tray_bubble.h"
 
 #include "ash/public/cpp/app_list/app_list_features.h"
+#include "ash/public/cpp/ash_features.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/system/status_area_widget.h"
@@ -317,8 +318,18 @@
 
   bubble_widget_->client_view()->layer()->SetBackgroundBlur(0);
 
-  blur_layer_ = views::Painter::CreatePaintedLayer(
-      views::Painter::CreateSolidRoundRectPainter(SK_ColorTRANSPARENT, 0));
+  if (features::ShouldUseShaderRoundedCorner()) {
+    blur_layer_ = std::make_unique<ui::LayerOwner>(
+        std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR));
+    blur_layer_->layer()->SetColor(SK_ColorTRANSPARENT);
+    blur_layer_->layer()->SetRoundedCornerRadius(
+        {kUnifiedTrayCornerRadius, kUnifiedTrayCornerRadius,
+         kUnifiedTrayCornerRadius, kUnifiedTrayCornerRadius});
+  } else {
+    blur_layer_ = views::Painter::CreatePaintedLayer(
+        views::Painter::CreateSolidRoundRectPainter(SK_ColorTRANSPARENT, 0));
+  }
+
   blur_layer_->layer()->SetFillsBoundsOpaquely(false);
 
   bubble_widget_->GetLayer()->Add(blur_layer_->layer());
diff --git a/ash/system/unified/user_chooser_view.cc b/ash/system/unified/user_chooser_view.cc
index 2297264..a1110bc 100644
--- a/ash/system/unified/user_chooser_view.cc
+++ b/ash/system/unified/user_chooser_view.cc
@@ -149,6 +149,9 @@
     gfx::ImageSkia icon =
         gfx::CreateVectorIcon(kSystemMenuGuestIcon, kMenuIconColor);
     image_view->SetImage(icon, icon.size());
+    // make sure icon height stays same for guest icon
+    image_view->SetBorder(views::CreateEmptyBorder(
+        gfx::Insets((kTrayItemSize - icon.size().height()) / 2, 0)));
   } else {
     image_view->SetImage(user_session->user_info->avatar->image,
                          gfx::Size(kTrayItemSize, kTrayItemSize));
diff --git a/ash/test/ash_test_base.h b/ash/test/ash_test_base.h
index f067845b..79fa96e7 100644
--- a/ash/test/ash_test_base.h
+++ b/ash/test/ash_test_base.h
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/wm/desks/desks_util.h"
 #include "base/macros.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread.h"
@@ -115,7 +116,7 @@
   // values for |container_id|.
   static std::unique_ptr<views::Widget> CreateTestWidget(
       views::WidgetDelegate* delegate = nullptr,
-      int container_id = kShellWindowId_DefaultContainer,
+      int container_id = desks_util::GetActiveDeskContainerId(),
       const gfx::Rect& bounds = gfx::Rect(),
       bool show = true);
 
diff --git a/ash/tooltips/tooltip_controller_unittest.cc b/ash/tooltips/tooltip_controller_unittest.cc
index 3e1b9c60..9c6c0b5 100644
--- a/ash/tooltips/tooltip_controller_unittest.cc
+++ b/ash/tooltips/tooltip_controller_unittest.cc
@@ -7,6 +7,7 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/desks/desks_util.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/aura/env.h"
@@ -39,8 +40,9 @@
   params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
   params.accept_events = true;
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
-  params.parent = Shell::Get()->GetContainer(
-      Shell::GetAllRootWindows().at(display), kShellWindowId_DefaultContainer);
+  params.parent =
+      Shell::Get()->GetContainer(Shell::GetAllRootWindows().at(display),
+                                 desks_util::GetActiveDeskContainerId());
   params.bounds = bounds;
   widget->Init(params);
   widget->Show();
diff --git a/ash/wm/always_on_top_controller.cc b/ash/wm/always_on_top_controller.cc
index 1b3e823..3d7f1a1 100644
--- a/ash/wm/always_on_top_controller.cc
+++ b/ash/wm/always_on_top_controller.cc
@@ -6,6 +6,7 @@
 
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/workspace/workspace_layout_manager.h"
 #include "ui/aura/client/aura_constants.h"
@@ -20,8 +21,8 @@
     aura::Window* pip_container)
     : always_on_top_container_(always_on_top_container),
       pip_container_(pip_container) {
-  DCHECK_NE(kShellWindowId_DefaultContainer, always_on_top_container_->id());
-  DCHECK_NE(kShellWindowId_DefaultContainer, pip_container_->id());
+  DCHECK(!desks_util::IsDeskContainer(always_on_top_container_));
+  DCHECK(!desks_util::IsDeskContainer(pip_container_));
   always_on_top_container_->SetLayoutManager(
       new WorkspaceLayoutManager(always_on_top_container_));
   pip_container_->SetLayoutManager(new WorkspaceLayoutManager(pip_container_));
@@ -44,8 +45,12 @@
   DCHECK(pip_container_);
 
   if (!window->GetProperty(aura::client::kAlwaysOnTopKey)) {
-    return always_on_top_container_->GetRootWindow()->GetChildById(
-        kShellWindowId_DefaultContainer);
+    aura::Window* root = always_on_top_container_->GetRootWindow();
+
+    // TODO(afakhry): Do we need to worry about the context of |window| here? Or
+    // is it safe to assume that |window| should always be parented to the
+    // active desks' container.
+    return desks_util::GetActiveDeskContainerForRoot(root);
   }
   if (window->parent() && wm::GetWindowState(window)->IsPip())
     return pip_container_;
diff --git a/ash/wm/always_on_top_controller_unittest.cc b/ash/wm/always_on_top_controller_unittest.cc
index 9d562e4..1427110 100644
--- a/ash/wm/always_on_top_controller_unittest.cc
+++ b/ash/wm/always_on_top_controller_unittest.cc
@@ -9,6 +9,7 @@
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/wm_event.h"
 #include "ash/wm/workspace/workspace_layout_manager.h"
@@ -130,7 +131,7 @@
   aura::Window* container =
       always_on_top_controller->GetContainer(window.get());
   ASSERT_TRUE(container);
-  EXPECT_EQ(kShellWindowId_DefaultContainer, container->id());
+  EXPECT_EQ(desks_util::GetActiveDeskContainerId(), container->id());
 }
 
 TEST_F(AlwaysOnTopControllerTest,
diff --git a/ash/wm/ash_focus_rules.cc b/ash/wm/ash_focus_rules.cc
index db3773c7..5b372c8e 100644
--- a/ash/wm/ash_focus_rules.cc
+++ b/ash/wm/ash_focus_rules.cc
@@ -9,6 +9,7 @@
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
 #include "ash/wm/container_finder.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/window_state.h"
 #include "ui/aura/client/aura_constants.h"
@@ -81,9 +82,11 @@
   if (!window->TargetVisibility())
     return false;
 
-  const int parent_shell_window_id = window->parent()->id();
-  return parent_shell_window_id == kShellWindowId_DefaultContainer ||
-         parent_shell_window_id == kShellWindowId_LockScreenContainer;
+  const aura::Window* parent = window->parent();
+  if (desks_util::IsActiveDeskContainer(parent))
+    return true;
+
+  return parent->id() == kShellWindowId_LockScreenContainer;
 }
 
 bool AshFocusRules::CanActivateWindow(const aura::Window* window) const {
diff --git a/ash/wm/ash_focus_rules_unittest.cc b/ash/wm/ash_focus_rules_unittest.cc
index fea4a3ec..0291078 100644
--- a/ash/wm/ash_focus_rules_unittest.cc
+++ b/ash/wm/ash_focus_rules_unittest.cc
@@ -11,6 +11,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
 #include "ash/window_factory.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/lock_state_controller.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
@@ -97,8 +98,8 @@
             Shell::Get()->session_controller()));
   }
 
-  aura::Window* CreateWindowInDefaultContainer() {
-    return CreateWindowInContainer(kShellWindowId_DefaultContainer);
+  aura::Window* CreateWindowInActiveDesk() {
+    return CreateWindowInContainer(desks_util::GetActiveDeskContainerId());
   }
 
   aura::Window* CreateWindowInAlwaysOnTopContainer() {
@@ -157,7 +158,7 @@
 // Verifies focus is returned (after unlocking the screen) to the most recent
 // window that had it before locking the screen.
 TEST_F(LockScreenAshFocusRulesTest, RegainFocusAfterUnlock) {
-  std::unique_ptr<aura::Window> normal_window(CreateWindowInDefaultContainer());
+  std::unique_ptr<aura::Window> normal_window(CreateWindowInActiveDesk());
   std::unique_ptr<aura::Window> always_on_top_window(
       CreateWindowInAlwaysOnTopContainer());
 
diff --git a/ash/wm/client_controlled_state_unittest.cc b/ash/wm/client_controlled_state_unittest.cc
index 43c52297..4cbaa0d 100644
--- a/ash/wm/client_controlled_state_unittest.cc
+++ b/ash/wm/client_controlled_state_unittest.cc
@@ -7,6 +7,7 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/screen_pinning_controller.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
@@ -106,7 +107,7 @@
     views::Widget::InitParams params;
     params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     params.parent = Shell::GetPrimaryRootWindow()->GetChildById(
-        kShellWindowId_DefaultContainer);
+        desks_util::GetActiveDeskContainerId());
     params.bounds = kInitialBounds;
     params.delegate = widget_delegate_;
 
diff --git a/ash/wm/container_finder_unittest.cc b/ash/wm/container_finder_unittest.cc
index d00db8a..166d905 100644
--- a/ash/wm/container_finder_unittest.cc
+++ b/ash/wm/container_finder_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ui/aura/window.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/widget/widget.h"
@@ -19,7 +20,7 @@
 TEST_F(ContainerFinderTest, GetContainerForWindow) {
   // Create a normal widget in the default container.
   std::unique_ptr<views::Widget> widget = CreateTestWidget(
-      nullptr, kShellWindowId_DefaultContainer, gfx::Rect(1, 2, 3, 4));
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect(1, 2, 3, 4));
   aura::Window* window = widget->GetNativeWindow();
 
   // The window itself is not a container.
@@ -28,7 +29,7 @@
   // Container lookup finds the default container.
   aura::Window* container = wm::GetContainerForWindow(window);
   ASSERT_TRUE(container);
-  EXPECT_EQ(kShellWindowId_DefaultContainer, container->id());
+  EXPECT_EQ(desks_util::GetActiveDeskContainerId(), container->id());
 }
 
 }  // namespace ash
diff --git a/ash/wm/desks/desks_util.cc b/ash/wm/desks/desks_util.cc
index 6ce77254..844d77b0 100644
--- a/ash/wm/desks/desks_util.cc
+++ b/ash/wm/desks/desks_util.cc
@@ -15,7 +15,7 @@
 
 constexpr std::array<int, kMaxNumberOfDesks> kDesksContainersIds = {
     // TODO(afakhry): Fill this.
-    kShellWindowId_DefaultContainer,
+    kShellWindowId_DefaultContainerDeprecated,
 };
 
 }  // namespace
@@ -26,7 +26,7 @@
 
 const char* GetDeskContainerName(int container_id) {
   switch (container_id) {
-    case kShellWindowId_DefaultContainer:
+    case kShellWindowId_DefaultContainerDeprecated:
       return "Desk_Container_A";
 
       // TODO(afakhry): Fill this.
@@ -51,19 +51,24 @@
   return containers;
 }
 
-bool IsDeskContainer(aura::Window* container) {
+bool IsDeskContainer(const aura::Window* container) {
   DCHECK(container);
   // TODO(afakhry): Add the rest of the desks containers.
-  return container->id() == kShellWindowId_DefaultContainer;
+  return container->id() == kShellWindowId_DefaultContainerDeprecated;
+}
+
+bool IsDeskContainerId(int id) {
+  // TODO(afakhry): Add the rest of the desks containers.
+  return id == kShellWindowId_DefaultContainerDeprecated;
 }
 
 int GetActiveDeskContainerId() {
   // TODO(afakhry): Do proper checking when the other desks containers are
   // added.
-  return kShellWindowId_DefaultContainer;
+  return kShellWindowId_DefaultContainerDeprecated;
 }
 
-ASH_EXPORT bool IsActiveDeskContainer(aura::Window* container) {
+ASH_EXPORT bool IsActiveDeskContainer(const aura::Window* container) {
   DCHECK(container);
   return container->id() == GetActiveDeskContainerId();
 }
diff --git a/ash/wm/desks/desks_util.h b/ash/wm/desks/desks_util.h
index fff0deb..af8c04b 100644
--- a/ash/wm/desks/desks_util.h
+++ b/ash/wm/desks/desks_util.h
@@ -28,11 +28,13 @@
 
 ASH_EXPORT const char* GetDeskContainerName(int container_id);
 
-ASH_EXPORT bool IsDeskContainer(aura::Window* container);
+ASH_EXPORT bool IsDeskContainer(const aura::Window* container);
+
+ASH_EXPORT bool IsDeskContainerId(int id);
 
 ASH_EXPORT int GetActiveDeskContainerId();
 
-ASH_EXPORT bool IsActiveDeskContainer(aura::Window* container);
+ASH_EXPORT bool IsActiveDeskContainer(const aura::Window* container);
 
 ASH_EXPORT aura::Window* GetActiveDeskContainerForRoot(aura::Window* root);
 
diff --git a/ash/wm/drag_window_resizer_unittest.cc b/ash/wm/drag_window_resizer_unittest.cc
index 56d86de..c3ad7b4 100644
--- a/ash/wm/drag_window_resizer_unittest.cc
+++ b/ash/wm/drag_window_resizer_unittest.cc
@@ -13,6 +13,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/window_factory.h"
 #include "ash/wm/cursor_manager_test_api.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/drag_window_controller.h"
 #include "ash/wm/window_positioning_utils.h"
 #include "ash/wm/window_util.h"
@@ -608,11 +609,10 @@
   // Move window from the root window with 2.0 device scale factor to the root
   // window with 1.0 device scale factor.
   {
-    // Make sure the window is on the default container first.
-    aura::Window* default_container =
-        RootWindowController::ForWindow(root_windows[1])
-            ->GetContainer(kShellWindowId_DefaultContainer);
-    default_container->AddChild(window_.get());
+    // Make sure the window is on the active desk container first.
+    aura::Window* active_desk_container =
+        desks_util::GetActiveDeskContainerForRoot(root_windows[1]);
+    active_desk_container->AddChild(window_.get());
     window_->SetBoundsInScreen(
         gfx::Rect(600, 0, 50, 60),
         display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]));
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index f254cb8f..facf810 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -219,8 +219,9 @@
       return split_view_controller->GetSnappedWindowBoundsInScreen(
           dragged_window, SplitViewController::LEFT);
     default:
-      return screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
-          dragged_window);
+      return screen_util::
+          GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
+              dragged_window);
   }
 }
 
@@ -244,8 +245,9 @@
       return split_view_controller->GetSnappedWindowBoundsInScreen(
           dragged_window, SplitViewController::LEFT);
     default:
-      return screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
-          dragged_window);
+      return screen_util::
+          GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
+              dragged_window);
   }
 }
 
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 4630bfc..4fa195b 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -66,7 +66,7 @@
 gfx::Rect GetGridBoundsInScreen(aura::Window* root_window,
                                 bool divider_changed) {
   gfx::Rect work_area =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           root_window);
 
   // If the shelf is in auto hide, overview will force it to be in auto hide
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index efe7449..e85936fc 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -24,6 +24,7 @@
 #include "ash/shelf/shelf_view_test_api.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/overview/caption_container_view.h"
 #include "ash/wm/overview/overview_constants.h"
 #include "ash/wm/overview/overview_controller.h"
@@ -608,7 +609,7 @@
 TEST_F(OverviewSessionTest, ActiveWindowChangedUserActionWindowClose) {
   base::UserActionTester user_action_tester;
   std::unique_ptr<views::Widget> widget(CreateTestWidget(
-      nullptr, kShellWindowId_DefaultContainer, gfx::Rect(400, 400)));
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect(400, 400)));
 
   ToggleOverview();
   aura::Window* window = widget->GetNativeWindow();
@@ -2759,7 +2760,7 @@
 TEST_F(OverviewSessionTest, DraggingFromTopAnimation) {
   EnterTabletMode();
   std::unique_ptr<views::Widget> widget(CreateTestWidget(
-      nullptr, kShellWindowId_DefaultContainer, gfx::Rect(200, 200)));
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect(200, 200)));
   widget->GetNativeWindow()->SetProperty(aura::client::kTopViewInset, 20);
 
   // Drag from the the top of the app to enter overview.
@@ -2901,7 +2902,7 @@
   }
 
   gfx::Rect GetWorkAreaInScreen(aura::Window* window) {
-    return screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+    return screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
         window);
   }
 
@@ -4071,7 +4072,7 @@
   OverviewItem* overview_item2 =
       GetWindowItemForWindow(grid_index, window2.get());
   const gfx::Rect work_area_rect =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window2.get());
   const gfx::PointF end_location2(work_area_rect.width(), 0);
   DragWindowTo(overview_item2, end_location2);
diff --git a/ash/wm/overview/overview_window_drag_controller.cc b/ash/wm/overview/overview_window_drag_controller.cc
index 7cbbf68..3bd0c6f8 100644
--- a/ash/wm/overview/overview_window_drag_controller.cc
+++ b/ash/wm/overview/overview_window_drag_controller.cc
@@ -453,7 +453,7 @@
   switch (snap_position) {
     case SplitViewController::NONE:
       return gfx::Rect(
-          screen_util::GetDisplayWorkAreaBoundsInParentForDefaultContainer(
+          screen_util::GetDisplayWorkAreaBoundsInParentForActiveDeskContainer(
               pending_snapped_window));
     case SplitViewController::LEFT:
       return split_view_controller_->GetSnappedWindowBoundsInScreen(
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index 6e266a5..fd252d81 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -441,7 +441,8 @@
     aura::Window* window,
     SnapPosition snap_position) {
   const gfx::Rect work_area_bounds_in_screen =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(window);
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
+          window);
   if (snap_position == NONE)
     return work_area_bounds_in_screen;
 
@@ -463,7 +464,8 @@
     aura::Window* window,
     SnapPosition snap_position) {
   const gfx::Rect work_area_bounds_in_screen =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(window);
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
+          window);
   if (snap_position == NONE)
     return work_area_bounds_in_screen;
 
@@ -479,7 +481,8 @@
 
 int SplitViewController::GetDefaultDividerPosition(aura::Window* window) const {
   const gfx::Rect work_area_bounds_in_screen =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(window);
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
+          window);
   const gfx::Size divider_size = SplitViewDivider::GetDividerSize(
       work_area_bounds_in_screen, GetCurrentScreenOrientation(),
       false /* is_dragging */);
@@ -547,7 +550,7 @@
     return;
   presentation_time_recorder_->RequestNext();
   const gfx::Rect work_area_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           GetDefaultSnappedWindow());
   gfx::Point modified_location_in_screen =
       GetBoundedPosition(location_in_screen, work_area_bounds);
@@ -578,7 +581,7 @@
   is_resizing_ = false;
 
   const gfx::Rect work_area_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           GetDefaultSnappedWindow());
   gfx::Point modified_location_in_screen =
       GetBoundedPosition(location_in_screen, work_area_bounds);
@@ -1042,7 +1045,7 @@
                            ? location_in_screen.x()
                            : location_in_screen.y();
   gfx::Rect work_area_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           GetDefaultSnappedWindow());
   if (!IsCurrentScreenOrientationLandscape())
     work_area_bounds.Transpose();
@@ -1079,7 +1082,7 @@
 SplitViewController::SnapPosition SplitViewController::GetBlackScrimPosition(
     const gfx::Point& location_in_screen) {
   const gfx::Rect work_area_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           GetDefaultSnappedWindow());
   if (!work_area_bounds.Contains(location_in_screen))
     return NONE;
@@ -1143,7 +1146,8 @@
     gfx::Rect* left_or_top_rect,
     gfx::Rect* right_or_bottom_rect) {
   const gfx::Rect work_area_bounds_in_screen =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(window);
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
+          window);
 
   // |divide_position_| might not be properly initialized yet.
   divider_position_ = (divider_position_ < 0)
@@ -1187,7 +1191,7 @@
   DCHECK(IsSplitViewModeActive());
 
   const gfx::Rect work_area_bounds_in_screen =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           GetDefaultSnappedWindow());
   const gfx::Size divider_size = SplitViewDivider::GetDividerSize(
       work_area_bounds_in_screen, GetCurrentScreenOrientation(),
@@ -1271,7 +1275,7 @@
 
 int SplitViewController::GetDividerEndPosition() {
   const gfx::Rect work_area_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           GetDefaultSnappedWindow());
   return IsCurrentScreenOrientationLandscape() ? work_area_bounds.width()
                                                : work_area_bounds.height();
diff --git a/ash/wm/splitview/split_view_controller_unittest.cc b/ash/wm/splitview/split_view_controller_unittest.cc
index f9ba055..2d952140 100644
--- a/ash/wm/splitview/split_view_controller_unittest.cc
+++ b/ash/wm/splitview/split_view_controller_unittest.cc
@@ -23,6 +23,7 @@
 #include "ash/system/status_area_widget_test_helper.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wallpaper/wallpaper_controller.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/drag_window_resizer.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
@@ -1437,7 +1438,7 @@
   EXPECT_TRUE(CanSnapInSplitview(window1.get()));
 
   const gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window1.get());
   aura::test::TestWindowDelegate* delegate =
       static_cast<aura::test::TestWindowDelegate*>(window1->delegate());
@@ -1472,7 +1473,7 @@
             OrientationLockType::kLandscapePrimary);
 
   gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window1.get());
   EXPECT_TRUE(CanSnapInSplitview(window1.get()));
   split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT);
@@ -1516,7 +1517,7 @@
             OrientationLockType::kPortraitPrimary);
 
   display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window1.get());
   delegate1->set_minimum_size(
       gfx::Size(display_bounds.width(), display_bounds.height() * 0.4f));
@@ -1546,7 +1547,7 @@
             OrientationLockType::kLandscapeSecondary);
 
   display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window1.get());
   delegate1->set_minimum_size(
       gfx::Size(display_bounds.width() * 0.4f, display_bounds.height()));
@@ -1578,7 +1579,7 @@
             OrientationLockType::kPortraitSecondary);
 
   display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window1.get());
   delegate1->set_minimum_size(
       gfx::Size(display_bounds.width(), display_bounds.height() * 0.4f));
@@ -1616,7 +1617,7 @@
   EXPECT_EQ(OrientationLockType::kLandscapePrimary,
             GetCurrentScreenOrientation());
   gfx::Rect workarea_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window1.get());
 
   // Snap the divider to one third position when there is only left window with
@@ -1702,7 +1703,7 @@
   const gfx::Rect bounds(0, 0, 200, 300);
   std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
   const gfx::Rect workarea_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window1.get());
 
   // Divider should be moved to the middle at the beginning.
@@ -1845,7 +1846,7 @@
   gfx::Rect divider_bounds =
       split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */);
   const int screen_width =
-      screen_util::GetDisplayWorkAreaBoundsInParentForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInParentForActiveDeskContainer(
           window1.get())
           .width();
   GetEventGenerator()->set_current_screen_location(
@@ -2128,7 +2129,7 @@
   gfx::Rect divider_bounds =
       split_view_divider()->GetDividerBoundsInScreen(false);
   gfx::Rect workarea_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window.get());
   generator->set_current_screen_location(divider_bounds.CenterPoint());
   // Drag the divider to one third position of the work area's width.
@@ -2226,7 +2227,7 @@
                               display::Display::RotationSource::ACTIVE);
 
   gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window.get());
   split_view_controller()->SnapWindow(window.get(), SplitViewController::LEFT);
   delegate->set_minimum_size(
@@ -2269,7 +2270,7 @@
   std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
 
   gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window1.get());
   split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT);
   split_view_controller()->SnapWindow(window2.get(),
@@ -2339,7 +2340,7 @@
                               display::Display::RotationSource::ACTIVE);
 
   gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window.get());
   split_view_controller()->SnapWindow(window.get(), SplitViewController::LEFT);
   delegate->set_minimum_size(
@@ -3429,7 +3430,7 @@
           window1->GetRootWindow());
   EXPECT_TRUE(current_grid->GetDropTarget());
   const gfx::Rect work_area_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window1.get());
   EXPECT_EQ(current_grid->bounds(), work_area_bounds);
   // The drop target should be visible.
@@ -4328,7 +4329,7 @@
   InitializeWindow(false);
   EXPECT_TRUE(wm::GetWindowState(window())->IsMaximized());
   gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window());
   const float long_scroll_delta = display_bounds.height() / 4 + 5;
 
@@ -4354,7 +4355,7 @@
   InitializeWindow();
   EXPECT_TRUE(wm::GetWindowState(window())->IsMaximized());
   gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window());
 
   // Move the window by a small amount of distance will maximize the window
@@ -4407,7 +4408,7 @@
   ShelfLayoutManager* shelf_layout_manager =
       AshTestBase::GetPrimaryShelf()->shelf_layout_manager();
   gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window());
 
   // Shelf will be auto-hidden if the window requests to be fullscreened.
@@ -4482,7 +4483,7 @@
   InitializeWindow();
   EXPECT_TRUE(wm::GetWindowState(window())->IsMaximized());
   gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window());
 
   const float long_scroll_delta = display_bounds.height() / 4 + 5;
@@ -4544,7 +4545,7 @@
                                       SplitViewController::RIGHT);
 
   gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window());
   const float long_scroll_y = display_bounds.bottom() - 10;
   float large_velocity =
@@ -4662,24 +4663,24 @@
                                       SplitViewController::RIGHT);
   EXPECT_EQ(window2.get(), wm::GetActiveWindow());
 
-  const aura::Window* default_container =
+  const aura::Window* active_desk_container =
       Shell::GetPrimaryRootWindowController()->GetContainer(
-          kShellWindowId_DefaultContainer);
+          desks_util::GetActiveDeskContainerId());
 
   // Backdrop window should below two snapped windows and its bounds should be
   // the same as the container bounds.
-  EXPECT_EQ(3U, default_container->children().size());
-  EXPECT_EQ(window(), default_container->children()[1]);
-  EXPECT_EQ(window2.get(), default_container->children()[2]);
-  EXPECT_EQ(default_container->bounds(),
-            default_container->children()[0]->bounds());
+  EXPECT_EQ(3U, active_desk_container->children().size());
+  EXPECT_EQ(window(), active_desk_container->children()[1]);
+  EXPECT_EQ(window2.get(), active_desk_container->children()[2]);
+  EXPECT_EQ(active_desk_container->bounds(),
+            active_desk_container->children()[0]->bounds());
 
   // Start window drag and activate the dragged window during drag.
   gfx::Point location(0, 10);
   SendScrollStartAndUpdate(location);
   wm::ActivateWindow(window());
 
-  aura::Window::Windows windows = default_container->children();
+  aura::Window::Windows windows = active_desk_container->children();
   auto it = std::find(windows.begin(), windows.end(), window2.get());
   // Backdrop window should be the window that just below the snapped |window2|
   // and its bounds should be the same as the snapped window during drag.
@@ -4692,12 +4693,12 @@
   // Backdrop should restore back to container bounds after drag.
   EndScrollSequence();
   EXPECT_EQ(window(), wm::GetActiveWindow());
-  windows = default_container->children();
+  windows = active_desk_container->children();
   it = std::find(windows.begin(), windows.end(), window2.get());
   if (it != windows.begin())
     backdrop_window = *(--it);
   DCHECK(backdrop_window);
-  EXPECT_EQ(backdrop_window->bounds(), default_container->bounds());
+  EXPECT_EQ(backdrop_window->bounds(), active_desk_container->bounds());
 }
 
 }  // namespace ash
diff --git a/ash/wm/splitview/split_view_divider.cc b/ash/wm/splitview/split_view_divider.cc
index 8b3587e3..9a5e9bb4 100644
--- a/ash/wm/splitview/split_view_divider.cc
+++ b/ash/wm/splitview/split_view_divider.cc
@@ -376,7 +376,7 @@
   aura::Window* root_window =
       divider_widget_->GetNativeWindow()->GetRootWindow();
   const gfx::Rect work_area_bounds_in_screen =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           root_window);
   const int divider_position = controller_->divider_position();
   const OrientationLockType screen_orientation = GetCurrentScreenOrientation();
diff --git a/ash/wm/splitview/split_view_utils.cc b/ash/wm/splitview/split_view_utils.cc
index ac1ac926..7083610 100644
--- a/ash/wm/splitview/split_view_utils.cc
+++ b/ash/wm/splitview/split_view_utils.cc
@@ -253,7 +253,7 @@
     // area size, the window can't be snapped in this case.
     const gfx::Size min_size = window->delegate()->GetMinimumSize();
     const gfx::Rect display_area =
-        screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+        screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
             window);
     const bool is_landscape = (display_area.width() > display_area.height());
     if ((is_landscape && min_size.width() > display_area.width() / 2) ||
diff --git a/ash/wm/switchable_windows.cc b/ash/wm/switchable_windows.cc
index be269757..9476fa9 100644
--- a/ash/wm/switchable_windows.cc
+++ b/ash/wm/switchable_windows.cc
@@ -12,7 +12,8 @@
 namespace wm {
 
 const int kSwitchableWindowContainerIds[] = {
-    kShellWindowId_DefaultContainer, kShellWindowId_AlwaysOnTopContainer};
+    kShellWindowId_DefaultContainerDeprecated,
+    kShellWindowId_AlwaysOnTopContainer};
 
 const size_t kSwitchableWindowContainerIdsLength =
     base::size(kSwitchableWindowContainerIds);
diff --git a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
index 1ebc131..59d418a8 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
@@ -1318,7 +1318,7 @@
   std::unique_ptr<aura::Window> left_window(CreateTestWindowInShellWithDelegate(
       &left_window_delegate, /*id=*/-1, /*bounds=*/gfx::Rect(0, 0, 400, 400)));
   const gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           left_window.get());
   left_window_delegate.set_minimum_size(
       gfx::Size(display_bounds.width() * 0.67f, display_bounds.height()));
@@ -1350,7 +1350,7 @@
           &right_window_delegate, /*id=*/-1,
           /*bounds=*/gfx::Rect(0, 0, 400, 400)));
   const gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           right_window.get());
   right_window_delegate.set_minimum_size(
       gfx::Size(display_bounds.width() * 0.67f, display_bounds.height()));
@@ -1381,7 +1381,7 @@
           &right_window_delegate, /*id=*/-1,
           /*bounds=*/gfx::Rect(0, 0, 400, 400)));
   const gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           right_window.get());
   right_window_delegate.set_minimum_size(
       gfx::Size(display_bounds.width() * 0.67f, display_bounds.height()));
@@ -1410,7 +1410,7 @@
   std::unique_ptr<aura::Window> left_window(CreateTestWindowInShellWithDelegate(
       &left_window_delegate, /*id=*/-1, /*bounds=*/gfx::Rect(0, 0, 400, 400)));
   const gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+      screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           left_window.get());
   left_window_delegate.set_minimum_size(
       gfx::Size(display_bounds.width() * 0.67f, display_bounds.height()));
diff --git a/ash/wm/top_level_window_factory_unittest.cc b/ash/wm/top_level_window_factory_unittest.cc
index cdd9ebfe..432b77b 100644
--- a/ash/wm/top_level_window_factory_unittest.cc
+++ b/ash/wm/top_level_window_factory_unittest.cc
@@ -13,6 +13,7 @@
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_properties.h"
 #include "mojo/public/cpp/bindings/map.h"
 #include "services/ws/public/cpp/property_type_converters.h"
@@ -106,7 +107,7 @@
   aura::Window* window = GetWindowTreeTestHelper()->NewTopLevelWindow(
       mojo::MapToFlatMap(std::move(properties)));
   ASSERT_TRUE(window->parent());
-  EXPECT_EQ(kShellWindowId_DefaultContainer, window->parent()->id());
+  EXPECT_EQ(desks_util::GetActiveDeskContainerId(), window->parent()->id());
   EXPECT_EQ(bounds, window->bounds());
   EXPECT_FALSE(window->IsVisible());
 }
diff --git a/ash/wm/toplevel_window_event_handler_unittest.cc b/ash/wm/toplevel_window_event_handler_unittest.cc
index 6ba1b4f2..211ecc9 100644
--- a/ash/wm/toplevel_window_event_handler_unittest.cc
+++ b/ash/wm/toplevel_window_event_handler_unittest.cc
@@ -9,6 +9,7 @@
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/window_factory.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/resize_shadow.h"
 #include "ash/wm/resize_shadow_controller.h"
 #include "ash/wm/window_state.h"
@@ -102,8 +103,8 @@
             .release();
     w1->set_id(1);
     w1->Init(ui::LAYER_TEXTURED);
-    aura::Window* parent = Shell::GetContainer(Shell::GetPrimaryRootWindow(),
-                                               kShellWindowId_DefaultContainer);
+    aura::Window* parent = Shell::GetContainer(
+        Shell::GetPrimaryRootWindow(), desks_util::GetActiveDeskContainerId());
     parent->AddChild(w1);
     w1->SetBounds(gfx::Rect(0, 0, 100, 100));
     w1->Show();
diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc
index 6931263c..b71f0689 100644
--- a/ash/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc
@@ -30,6 +30,7 @@
 #include "ash/wallpaper/wallpaper_controller_test_api.h"
 #include "ash/window_factory.h"
 #include "ash/wm/always_on_top_controller.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/fullscreen_window_finder.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/splitview/split_view_controller.h"
@@ -450,10 +451,10 @@
       window_factory::NewWindow(nullptr, aura::client::WINDOW_TYPE_NORMAL);
   window->Init(ui::LAYER_TEXTURED);
   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
-  aura::Window* default_container =
+  aura::Window* active_desk_container =
       Shell::GetPrimaryRootWindowController()->GetContainer(
-          kShellWindowId_DefaultContainer);
-  default_container->AddChild(window.get());
+          desks_util::GetActiveDeskContainerId());
+  active_desk_container->AddChild(window.get());
   window->Show();
   gfx::Rect work_area(GetPrimaryDisplay().work_area());
   EXPECT_EQ(work_area.ToString(), window->GetBoundsInScreen().ToString());
@@ -1130,7 +1131,7 @@
     AshTestBase::SetUp();
     UpdateDisplay("800x600");
     default_container_ = Shell::GetPrimaryRootWindowController()->GetContainer(
-        kShellWindowId_DefaultContainer);
+        desks_util::GetActiveDeskContainerId());
   }
 
   // Turn the top window back drop on / off.
@@ -1649,10 +1650,10 @@
   void SetUp() override {
     AshTestBase::SetUp();
     UpdateDisplay("800x600");
-    aura::Window* default_container =
+    aura::Window* active_desk_container =
         Shell::GetPrimaryRootWindowController()->GetContainer(
-            kShellWindowId_DefaultContainer);
-    layout_manager_ = GetWorkspaceLayoutManager(default_container);
+            desks_util::GetActiveDeskContainerId());
+    layout_manager_ = GetWorkspaceLayoutManager(active_desk_container);
   }
 
   void ShowKeyboard() {
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc
index 84a9346..d2e5dc7b 100644
--- a/ash/wm/workspace/workspace_window_resizer.cc
+++ b/ash/wm/workspace/workspace_window_resizer.cc
@@ -16,6 +16,7 @@
 #include "ash/screen_util.h"
 #include "ash/shell.h"
 #include "ash/wm/default_window_resizer.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/drag_window_resizer.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/pip/pip_window_resizer.h"
@@ -182,7 +183,8 @@
   const int parent_shell_window_id =
       window->parent() ? window->parent()->id() : -1;
   if (window->parent() &&
-      (parent_shell_window_id == kShellWindowId_DefaultContainer ||
+      // TODO(afakhry): Maybe use switchable containers?
+      (desks_util::IsDeskContainerId(parent_shell_window_id) ||
        parent_shell_window_id == kShellWindowId_AlwaysOnTopContainer)) {
     window_resizer.reset(WorkspaceWindowResizer::Create(
         window_state, std::vector<aura::Window*>()));
@@ -899,8 +901,10 @@
   aura::Window* root_window =
       Shell::Get()->window_tree_host_manager()->GetRootWindowForDisplayId(
           display.id());
-  const std::vector<aura::Window*>& children =
-      root_window->GetChildById(kShellWindowId_DefaultContainer)->children();
+  aura::Window* container =
+      desks_util::GetActiveDeskContainerForRoot(root_window);
+  DCHECK(container);
+  const std::vector<aura::Window*>& children = container->children();
   for (auto i = children.rbegin();
        i != children.rend() && !matcher.AreEdgesObscured(); ++i) {
     wm::WindowState* other_state = wm::GetWindowState(*i);
diff --git a/ash/wm/workspace_controller_unittest.cc b/ash/wm/workspace_controller_unittest.cc
index 9db34819..67d62f5 100644
--- a/ash/wm/workspace_controller_unittest.cc
+++ b/ash/wm/workspace_controller_unittest.cc
@@ -17,6 +17,7 @@
 #include "ash/system/status_area_widget.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/window_factory.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
@@ -114,7 +115,7 @@
 
   aura::Window* GetDesktop() {
     return Shell::GetContainer(Shell::GetPrimaryRootWindow(),
-                               kShellWindowId_DefaultContainer);
+                               desks_util::GetActiveDeskContainerId());
   }
 
   gfx::Rect GetFullscreenBounds(aura::Window* window) {
diff --git a/ash/ws/ax_ash_window_utils_unittest.cc b/ash/ws/ax_ash_window_utils_unittest.cc
index 57bcfc1d..7185607 100644
--- a/ash/ws/ax_ash_window_utils_unittest.cc
+++ b/ash/ws/ax_ash_window_utils_unittest.cc
@@ -7,6 +7,7 @@
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
+#include "ash/wm/desks/desks_util.h"
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_action_data.h"
@@ -137,7 +138,7 @@
 
   // Start with the widget's container.
   aura::Window* container = Shell::GetContainer(
-      Shell::GetPrimaryRootWindow(), kShellWindowId_DefaultContainer);
+      Shell::GetPrimaryRootWindow(), desks_util::GetActiveDeskContainerId());
   EXPECT_EQ(aura::Env::Mode::LOCAL, container->env()->mode());
 
   // Walking down "jumps the fence" into the client window.
diff --git a/ash/ws/window_service_delegate_impl_unittest.cc b/ash/ws/window_service_delegate_impl_unittest.cc
index bfd32f8..84cc348 100644
--- a/ash/ws/window_service_delegate_impl_unittest.cc
+++ b/ash/ws/window_service_delegate_impl_unittest.cc
@@ -6,6 +6,7 @@
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/resize_shadow.h"
 #include "ash/wm/resize_shadow_controller.h"
 #include "ash/wm/toplevel_window_event_handler.h"
@@ -400,9 +401,9 @@
 TEST_F(WindowServiceDelegateImplTest, ObserveTopmostWindow) {
   std::unique_ptr<aura::Window> window2 =
       CreateTestWindow(gfx::Rect(150, 100, 100, 100));
-  std::unique_ptr<aura::Window> window3(
-      CreateTestWindowInShell(SK_ColorRED, kShellWindowId_DefaultContainer,
-                              gfx::Rect(100, 150, 100, 100)));
+  std::unique_ptr<aura::Window> window3(CreateTestWindowInShell(
+      SK_ColorRED, desks_util::GetActiveDeskContainerId(),
+      gfx::Rect(100, 150, 100, 100)));
 
   // Left button is pressed on SetUp() -- release it first.
   GetEventGenerator()->ReleaseLeftButton();
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 83ed4b1..6512667a 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8916548565018644928
\ No newline at end of file
+8916518872782926384
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index fa67335..f39818f 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8916561048598159552
\ No newline at end of file
+8916520934446696624
\ No newline at end of file
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index cef5e05..18d503a 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -441,8 +441,12 @@
     "//components/module_installer/android:module_interface_processor",
   ]
 
+  proguard_configs = []
   if (async_ar) {
-    proguard_configs = [ "//chrome/android/features/ar/proguard_async.flags" ]
+    proguard_configs += [ "//chrome/android/features/ar/proguard_async.flags" ]
+  }
+  if (async_vr) {
+    proguard_configs += [ "//chrome/android/features/vr/proguard_async.flags" ]
   }
 
   processor_args_javac = [ "dagger.fastInit=enabled" ]
@@ -2158,6 +2162,7 @@
         {
           name = "vr"
           module_target = ":${target_name}__vr_bundle_module"
+          proguard_async = async_vr
         },
       ]
     }
@@ -2443,6 +2448,7 @@
     "java/src/org/chromium/chrome/browser/send_tab_to_self/NotificationManager.java",
     "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridge.java",
     "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfEntry.java",
+    "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBar.java",
     "java/src/org/chromium/chrome/browser/sessions/SessionTabHelper.java",
     "java/src/org/chromium/chrome/browser/signin/IdentityServicesProvider.java",
     "java/src/org/chromium/chrome/browser/signin/SigninInvestigator.java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 55f82c9..aab9de73 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1411,6 +1411,7 @@
   "java/src/org/chromium/chrome/browser/send_tab_to_self/NotificationManager.java",
   "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridge.java",
   "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfEntry.java",
+  "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBar.java",
   "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java",
   "java/src/org/chromium/chrome/browser/services/AccountsChangedReceiver.java",
   "java/src/org/chromium/chrome/browser/services/AndroidEduAndChildAccountHelper.java",
diff --git a/chrome/android/features/vr/BUILD.gn b/chrome/android/features/vr/BUILD.gn
index 3f76fb5..00038cff 100644
--- a/chrome/android/features/vr/BUILD.gn
+++ b/chrome/android/features/vr/BUILD.gn
@@ -133,6 +133,10 @@
     "//third_party/gvr-android-keyboard:kb_java",
     "//third_party/gvr-android-sdk:gvr_common_java",
   ]
+
+  if (async_vr) {
+    proguard_configs = [ "//base/android/proguard/chromium_code.flags" ]
+  }
 }
 
 generate_jni("jni_headers") {
diff --git a/chrome/android/features/vr/proguard_async.flags b/chrome/android/features/vr/proguard_async.flags
new file mode 100644
index 0000000..1b4893d
--- /dev/null
+++ b/chrome/android/features/vr/proguard_async.flags
@@ -0,0 +1,118 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Contains flags necessary to keep the VR module compatible with base when VR
+# is specified as an async DFM (when the VR module is proguarded separately).
+# This flags file was generated from the dependencies of Java classes in
+# src/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/
+# produced using the jdeps tool. It intentionally keeps all members from these
+# dependencies to ensure compatibility between the async VR module and base.
+
+# TODO(cuianthony): Reduce the scope of these keep rules by parsing information
+# from constant pools of relevant Java classes.
+
+-keep class org.chromium.base.ActivityState { *; }
+-keep class org.chromium.base.ApiCompatibilityUtils { *; }
+-keep class org.chromium.base.ApplicationStatus { *; }
+-keep class org.chromium.base.ApplicationStatus$ActivityStateListener { *; }
+-keep class org.chromium.base.Callback { *; }
+-keep class org.chromium.base.ContextUtils { *; }
+-keep class org.chromium.base.Log { *; }
+-keep class org.chromium.base.PackageUtils { *; }
+-keep class org.chromium.base.StrictModeContext { *; }
+-keep class org.chromium.base.ThreadUtils { *; }
+-keep class org.chromium.base.TraceEvent { *; }
+-keep class org.chromium.base.annotations.JNINamespace { *; }
+-keep class org.chromium.base.library_loader.LibraryLoader { *; }
+-keep class org.chromium.base.metrics.CachedMetrics { *; }
+-keep class org.chromium.base.metrics.CachedMetrics$BooleanHistogramSample { *; }
+-keep class org.chromium.base.metrics.RecordUserAction { *; }
+-keep class org.chromium.base.task.AsyncTask { *; }
+-keep class org.chromium.base.task.PostTask { *; }
+-keep class org.chromium.base.task.TaskTraits { *; }
+-keep class org.chromium.chrome.R { *; }
+-keep class org.chromium.chrome.R$anim { *; }
+-keep class org.chromium.chrome.R$id { *; }
+-keep class org.chromium.chrome.R$layout { *; }
+-keep class org.chromium.chrome.R$string { *; }
+-keep class org.chromium.chrome.R$style { *; }
+-keep class org.chromium.chrome.browser.ApplicationLifetime { *; }
+-keep class org.chromium.chrome.browser.ChromeActivity { *; }
+-keep class org.chromium.chrome.browser.ChromeFeatureList { *; }
+-keep class org.chromium.chrome.browser.ChromeTabbedActivity { *; }
+-keep class org.chromium.chrome.browser.compositor.CompositorSurfaceManager { *; }
+-keep class org.chromium.chrome.browser.compositor.CompositorSurfaceManager$SurfaceManagerCallbackTarget { *; }
+-keep class org.chromium.chrome.browser.compositor.CompositorView { *; }
+-keep class org.chromium.chrome.browser.compositor.CompositorViewHolder { *; }
+-keep class org.chromium.chrome.browser.customtabs.CustomTabActivity { *; }
+-keep class org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager { *; }
+-keep class org.chromium.chrome.browser.help.HelpAndFeedback { *; }
+-keep class org.chromium.chrome.browser.infobar.InfoBarIdentifier { *; }
+-keep class org.chromium.chrome.browser.infobar.SimpleConfirmInfoBarBuilder { *; }
+-keep class org.chromium.chrome.browser.infobar.SimpleConfirmInfoBarBuilder$Listener { *; }
+-keep class org.chromium.chrome.browser.modaldialog.ModalDialogView { *; }
+-keep class org.chromium.chrome.browser.modaldialog.ModalDialogViewBinder { *; }
+-keep class org.chromium.chrome.browser.page_info.PageInfoController { *; }
+-keep class org.chromium.chrome.browser.page_info.PageInfoController$OpenedFromSource { *; }
+-keep class org.chromium.chrome.browser.preferences.ChromePreferenceManager { *; }
+-keep class org.chromium.chrome.browser.profiles.Profile { *; }
+-keep class org.chromium.chrome.browser.tab.EmptyTabObserver { *; }
+-keep class org.chromium.chrome.browser.tab.Tab { *; }
+-keep class org.chromium.chrome.browser.tab.TabObserver { *; }
+-keep class org.chromium.chrome.browser.tab.TabRedirectHandler { *; }
+-keep class org.chromium.chrome.browser.tabmodel.ChromeTabCreator { *; }
+-keep class org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver { *; }
+-keep class org.chromium.chrome.browser.tabmodel.TabCreatorManager { *; }
+-keep class org.chromium.chrome.browser.tabmodel.TabCreatorManager$TabCreator { *; }
+-keep class org.chromium.chrome.browser.tabmodel.TabLaunchType { *; }
+-keep class org.chromium.chrome.browser.tabmodel.TabModel { *; }
+-keep class org.chromium.chrome.browser.tabmodel.TabModelSelector { *; }
+-keep class org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver { *; }
+-keep class org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver { *; }
+-keep class org.chromium.chrome.browser.toolbar.NewTabButton { *; }
+-keep class org.chromium.chrome.browser.toolbar.ToolbarManager { *; }
+-keep class org.chromium.chrome.browser.util.FeatureUtilities { *; }
+-keep class org.chromium.chrome.browser.util.IntentUtils { *; }
+-keep class org.chromium.chrome.browser.vr.OnExitVrRequestListener { *; }
+-keep class org.chromium.chrome.browser.vr.VrDelegate { *; }
+-keep class org.chromium.chrome.browser.vr.VrDelegateProvider { *; }
+-keep class org.chromium.chrome.browser.vr.VrIntentDelegate { *; }
+-keep class org.chromium.chrome.browser.vr.VrModuleProvider { *; }
+-keep class org.chromium.chrome.browser.webapps.WebappActivity { *; }
+-keep class org.chromium.chrome.browser.widget.findinpage.FindToolbarManager { *; }
+-keep class org.chromium.content_public.browser.ImeAdapter { *; }
+-keep class org.chromium.content_public.browser.InputMethodManagerWrapper { *; }
+-keep class org.chromium.content_public.browser.LoadUrlParams { *; }
+-keep class org.chromium.content_public.browser.MotionEventSynthesizer { *; }
+-keep class org.chromium.content_public.browser.ScreenOrientationDelegate { *; }
+-keep class org.chromium.content_public.browser.ScreenOrientationProvider { *; }
+-keep class org.chromium.content_public.browser.UiThreadTaskTraits { *; }
+-keep class org.chromium.content_public.browser.ViewEventSink { *; }
+-keep class org.chromium.content_public.browser.WebContents { *; }
+-keep class org.chromium.content_public.common.BrowserControlsState { *; }
+-keep class org.chromium.ui.UiUtils { *; }
+-keep class org.chromium.ui.base.ActivityWindowAndroid { *; }
+-keep class org.chromium.ui.base.AndroidPermissionDelegate { *; }
+-keep class org.chromium.ui.base.EventForwarder { *; }
+-keep class org.chromium.ui.base.PermissionCallback { *; }
+-keep class org.chromium.ui.base.WindowAndroid { *; }
+-keep class org.chromium.ui.base.WindowAndroid$IntentCallback { *; }
+-keep class org.chromium.ui.display.DisplayAndroid { *; }
+-keep class org.chromium.ui.display.VirtualDisplayAndroid { *; }
+-keep class org.chromium.ui.modaldialog.DialogDismissalCause { *; }
+-keep class org.chromium.ui.modaldialog.ModalDialogManager { *; }
+-keep class org.chromium.ui.modaldialog.ModalDialogManager$ModalDialogType { *; }
+-keep class org.chromium.ui.modaldialog.ModalDialogManager$Presenter { *; }
+-keep class org.chromium.ui.modaldialog.ModalDialogProperties { *; }
+-keep class org.chromium.ui.modaldialog.ModalDialogProperties$ButtonType { *; }
+-keep class org.chromium.ui.modaldialog.ModalDialogProperties$Controller { *; }
+-keep class org.chromium.ui.modelutil.PropertyKey { *; }
+-keep class org.chromium.ui.modelutil.PropertyModel { *; }
+-keep class org.chromium.ui.modelutil.PropertyModel$Builder { *; }
+-keep class org.chromium.ui.modelutil.PropertyModel$ReadableObjectPropertyKey { *; }
+-keep class org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey { *; }
+-keep class org.chromium.ui.modelutil.PropertyModelChangeProcessor { *; }
+-keep class org.chromium.ui.modelutil.PropertyModelChangeProcessor$ViewBinder { *; }
+-keep class org.chromium.ui.modelutil.PropertyObservable { *; }
+-keep class org.chromium.ui.widget.UiWidgetFactory { *; }
\ No newline at end of file
diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml
index 3e1c382f..38bf90e 100644
--- a/chrome/android/java/res/xml/privacy_preferences.xml
+++ b/chrome/android/java/res/xml/privacy_preferences.xml
@@ -34,7 +34,7 @@
         android:key="can_make_payment"
         android:title="@string/can_make_payment_title"
         android:summary="@string/settings_can_make_payment_toggle_label" />
-    <Preference
+    <org.chromium.chrome.browser.preferences.ChromeBasePreference
         android:key="usage_stats_reporting"
         android:title="@string/usage_stats_setting_title"
         android:persistent="false" />
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActionModeCallback.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActionModeCallback.java
index 501b675..bd1556d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActionModeCallback.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActionModeCallback.java
@@ -19,6 +19,7 @@
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.content.R;
 import org.chromium.content_public.browser.ActionModeCallbackHelper;
@@ -112,7 +113,8 @@
 
     private void search(String searchText) {
         RecordUserAction.record("MobileActionMode.WebSearch");
-        if (mTab.getTabModelSelector() == null) return;
+        TabModelSelector selector = TabModelSelector.from(mTab);
+        if (selector == null) return;
 
         String query = ActionModeCallbackHelper.sanitizeQuery(
                 searchText, ActionModeCallbackHelper.MAX_SEARCH_QUERY_LENGTH);
@@ -120,7 +122,7 @@
 
         TrackerFactory.getTrackerForProfile(mTab.getProfile())
                 .notifyEvent(EventConstants.WEB_SEARCH_PERFORMED);
-        mTab.getTabModelSelector().openNewTab(generateUrlParamsForSearch(query),
+        selector.openNewTab(generateUrlParamsForSearch(query),
                 TabLaunchType.FROM_LONGPRESS_FOREGROUND, mTab, mTab.isIncognito());
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsTabCreatorManager.java b/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsTabCreatorManager.java
index 6802bd9..27fb36e7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsTabCreatorManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsTabCreatorManager.java
@@ -73,8 +73,8 @@
         }
 
         @Override
-        public boolean createTabWithWebContents(Tab parent, WebContents webContents, int parentId,
-                @TabLaunchType int type, String url) {
+        public boolean createTabWithWebContents(
+                Tab parent, WebContents webContents, @TabLaunchType int type, String url) {
             throw new UnsupportedOperationException(
                     "Browser Actions does not support createTabWithWebContents");
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java
index 258d0995..fec0fcbd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java
@@ -93,9 +93,10 @@
                 IntentUtils.safeGetIntExtra(intent, IntentHandler.EXTRA_TAB_ID, Tab.INVALID_TAB_ID);
         int parentTabId = IntentUtils.safeGetIntExtra(
                 intent, IntentHandler.EXTRA_PARENT_TAB_ID, Tab.INVALID_TAB_ID);
+        Tab parent = mTabModelSelector != null ? mTabModelSelector.getTabById(parentTabId) : null;
         return new TabBuilder()
                 .setId(assignedTabId)
-                .setParentId(parentTabId)
+                .setParent(parent)
                 .setIncognito(mIntentDataProvider.isIncognito())
                 .setWindow(mActivityWindowAndroid.get())
                 .setLaunchType(TabLaunchType.FROM_EXTERNAL_APP)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java
index 91c7572..534d0e9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java
@@ -280,7 +280,7 @@
                 || contents.getNavigationController().isInitialNavigation();
         if (isInitialNavigation) {
             // Tab is created just for download, close it.
-            TabModelSelector selector = tab.getTabModelSelector();
+            TabModelSelector selector = TabModelSelector.from(tab);
             if (selector == null) return true;
             if (selector.getModel(tab.isIncognito()).getCount() == 1) return false;
             boolean closed = selector.closeTab(tab);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index 464519d7..4055e2a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -726,6 +726,8 @@
                     + intent.getData().getScheme(), ex);
         } catch (SecurityException ex) {
             Log.d(TAG, "cannot open intent: " + intent, ex);
+        } catch (Exception ex) {
+            Log.d(TAG, "cannot open intent: " + intent, ex);
         }
 
         return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameDialogCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameDialogCoordinator.java
index 00c97d95..496d7fe7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameDialogCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameDialogCoordinator.java
@@ -42,6 +42,10 @@
                         .build();
         mOnClickEventCallback = onClickCallback;
         mOnDismissEventCallback = dismissCallback;
+
+        mRenameDialogCustomView.setEmptyInputObserver((result) -> {
+            mRenameDialogModel.set(ModalDialogProperties.POSITIVE_BUTTON_DISABLED, result);
+        });
     }
 
     public void destroy() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameDialogCustomView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameDialogCustomView.java
index c7c7e339..7c1c946 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameDialogCustomView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameDialogCustomView.java
@@ -7,13 +7,16 @@
 import static android.content.Context.INPUT_METHOD_SERVICE;
 
 import android.content.Context;
+import android.text.Editable;
 import android.text.TextUtils;
+import android.text.TextWatcher;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.ScrollView;
 import android.widget.TextView;
 
+import org.chromium.base.Callback;
 import org.chromium.chrome.browser.widget.AlertDialogEditText;
 import org.chromium.chrome.download.R;
 import org.chromium.components.offline_items_collection.RenameResult;
@@ -24,6 +27,7 @@
 public class RenameDialogCustomView extends ScrollView {
     private TextView mSubtitleView;
     private AlertDialogEditText mFileName;
+    private Callback</*Empty*/ Boolean> mEmptyFileNameObserver;
 
     public RenameDialogCustomView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -35,6 +39,26 @@
         super.onFinishInflate();
         mSubtitleView = findViewById(R.id.subtitle);
         mFileName = findViewById(R.id.file_name);
+        mFileName.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {}
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                if (mEmptyFileNameObserver == null) return;
+                mEmptyFileNameObserver.onResult(getTargetName().isEmpty());
+            }
+        });
+    }
+
+    /**
+     * @param callback Callback to run when edit text is empty.
+     */
+    public void setEmptyInputObserver(Callback<Boolean> callback) {
+        mEmptyFileNameObserver = callback;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameDialogManager.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameDialogManager.java
index 7fccfbc..f6490574 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameDialogManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameDialogManager.java
@@ -158,9 +158,6 @@
         if (isPositiveButton) {
             mLastAttemptedName = mRenameDialogCoordinator.getCurSuggestedName();
 
-            // TODO(hesen): Have a TextWatcher on the input, and disable OK button if it's empty.
-            if (TextUtils.isEmpty(mLastAttemptedName)) return;
-
             if (TextUtils.equals(mLastAttemptedName, mOriginalName)) {
                 processDialogState(RenameDialogState.RENAME_DIALOG_CANCEL,
                         DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureController.java
index ebc7d749..1503499 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureController.java
@@ -28,6 +28,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 import org.chromium.chrome.browser.util.MathUtils;
 import org.chromium.content_public.browser.WebContents;
@@ -212,16 +213,17 @@
                 new DismissActivityOnTabModelSelectorEventObserver(activity);
         final WebContentsObserver webContentsObserver =
                 new DismissActivityOnWebContentsObserver(activity);
+        final TabModelSelector tabModelSelector = TabModelSelector.from(activityTab);
 
         activityTab.addObserver(tabObserver);
-        activityTab.getTabModelSelector().addObserver(tabModelSelectorObserver);
+        tabModelSelector.addObserver(tabModelSelectorObserver);
         webContents.addObserver(webContentsObserver);
 
         mOnLeavePipCallbacks.add(new Callback<ChromeActivity>() {
             @Override
             public void onResult(ChromeActivity activity2) {
                 activityTab.removeObserver(tabObserver);
-                activityTab.getTabModelSelector().removeObserver(tabModelSelectorObserver);
+                tabModelSelector.removeObserver(tabModelSelectorObserver);
                 webContents.removeObserver(webContentsObserver);
             }
         });
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
index 2394eff6..fc81539 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
@@ -133,20 +133,17 @@
      * @param url The URL to be handled.
      * @param candidatePage A NativePage to be reused if it matches the url, or null.
      * @param tab The Tab that will show the page.
-     * @param tabModelSelector The TabModelSelector containing the tab.
      * @param activity The activity used to create the views for the page.
      * @return A NativePage showing the specified url or null.
      */
-    public static NativePage createNativePageForURL(String url, NativePage candidatePage,
-            Tab tab, TabModelSelector tabModelSelector, ChromeActivity activity) {
-        return createNativePageForURL(url, candidatePage, tab, tabModelSelector, activity,
-                tab.isIncognito());
+    public static NativePage createNativePageForURL(
+            String url, NativePage candidatePage, Tab tab, ChromeActivity activity) {
+        return createNativePageForURL(url, candidatePage, tab, activity, tab.isIncognito());
     }
 
     @VisibleForTesting
-    static NativePage createNativePageForURL(String url, NativePage candidatePage,
-            Tab tab, TabModelSelector tabModelSelector, ChromeActivity activity,
-            boolean isIncognito) {
+    static NativePage createNativePageForURL(String url, NativePage candidatePage, Tab tab,
+            ChromeActivity activity, boolean isIncognito) {
         NativePage page;
 
         switch (nativePageType(url, candidatePage, isIncognito)) {
@@ -156,7 +153,8 @@
                 page = candidatePage;
                 break;
             case NativePageType.NTP:
-                page = sNativePageBuilder.buildNewTabPage(activity, tab, tabModelSelector);
+                page = sNativePageBuilder.buildNewTabPage(
+                        activity, tab, TabModelSelector.from(tab));
                 break;
             case NativePageType.BOOKMARKS:
                 page = sNativePageBuilder.buildBookmarksPage(activity, tab);
@@ -209,7 +207,7 @@
         @Override
         public int loadUrl(LoadUrlParams urlParams, boolean incognito) {
             if (incognito && !mTab.isIncognito()) {
-                mTab.getTabModelSelector().openNewTab(urlParams,
+                TabModelSelector.from(mTab).openNewTab(urlParams,
                         TabLaunchType.FROM_LONGPRESS_FOREGROUND, mTab,
                         /* incognito = */ true);
                 return TabLoadStatus.DEFAULT_PAGE_LOAD;
@@ -235,7 +233,7 @@
 
         @Override
         public boolean isVisible() {
-            return mTab == mTab.getTabModelSelector().getCurrentTab();
+            return mTab == TabModelSelector.from(mTab).getCurrentTab();
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBar.java
new file mode 100644
index 0000000..2d5dee6
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBar.java
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.send_tab_to_self;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.infobar.InfoBar;
+import org.chromium.chrome.browser.infobar.InfoBarCompactLayout;
+
+/**
+ * This infobar is shown to let users know they have a shared tab from another
+ * device that can be opened on this one.
+ */
+public class SendTabToSelfInfoBar extends InfoBar {
+    public SendTabToSelfInfoBar() {
+        // TODO(crbug.com/949233): Update this to the right icon
+        super(R.drawable.infobar_chrome, null, null);
+    }
+
+    @Override
+    protected boolean usesCompactLayout() {
+        return true;
+    }
+
+    @Override
+    protected void createCompactLayoutContent(InfoBarCompactLayout layout) {
+        new InfoBarCompactLayout.MessageBuilder(layout)
+                .withText("Tab shared")
+                // TODO(crbug.com/949233): Add the link in
+                // .withLink(textResId, onTapCallback)
+                .buildAndInsert();
+    }
+
+    @CalledByNative
+    private static SendTabToSelfInfoBar create() {
+        return new SendTabToSelfInfoBar();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java
index 518e0de..9e5421d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java
@@ -15,6 +15,7 @@
 import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler.OverrideUrlLoadingResult;
 import org.chromium.chrome.browser.externalnav.ExternalNavigationParams;
 import org.chromium.chrome.browser.tabmodel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
 import org.chromium.components.navigation_interception.NavigationParams;
 import org.chromium.content_public.browser.NavigationController;
@@ -312,7 +313,7 @@
             PostTask.postTask(UiThreadTaskTraits.DEFAULT, new Runnable() {
                 @Override
                 public void run() {
-                    mTab.getTabModelSelector().closeTab(mTab);
+                    TabModelSelector.from(mTab).closeTab(mTab);
                 }
             });
         } else if (TabRedirectHandler.from(mTab).isOnNavigation()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index d1f53d15..795cb93 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -153,7 +153,12 @@
      * caused it to be opened so that we can activate it when this tab gets
      * closed.
      */
-    private int mParentId = INVALID_TAB_ID;
+    private final int mParentId;
+
+    /**
+     * Tab id to be used as a source tab in SyncedTabDelegate.
+     */
+    private final int mSourceTabId;
 
     /**
      * By default, this id inherits from the tab that caused it to be opened, or it equals to tab
@@ -289,7 +294,7 @@
      * Package-private. Use {@link TabBuilder} to create an instance.
      *
      * @param id            The id this tab should be identified with.
-     * @param parentId      The id id of the tab that caused this tab to be opened.
+     * @param parent        The tab that caused this tab to be opened.
      * @param incognito     Whether or not this tab is incognito.
      * @param window        An instance of a {@link WindowAndroid}.
      * @param launchType    Type indicating how this tab was launched.
@@ -297,12 +302,20 @@
      * @param loadUrlParams Parameters used for a lazily loaded Tab.
      */
     @SuppressLint("HandlerLeak")
-    Tab(int id, int parentId, boolean incognito, WindowAndroid window,
+    Tab(int id, Tab parent, boolean incognito, WindowAndroid window,
             @Nullable @TabLaunchType Integer launchType,
             @Nullable @TabCreationState Integer creationState, LoadUrlParams loadUrlParams) {
         mId = TabIdManager.getInstance().generateValidId(id);
-        mParentId = parentId;
         mIncognito = incognito;
+        if (parent == null) {
+            mParentId = INVALID_TAB_ID;
+            mSourceTabId = INVALID_TAB_ID;
+            mRootId = mId;
+        } else {
+            mParentId = parent.getId();
+            mSourceTabId = parent.isIncognito() == incognito ? mParentId : INVALID_TAB_ID;
+            mRootId = parent.getRootId();
+        }
 
         // Override the configuration for night mode to always stay in light mode until all UIs in
         // Tab are inflated from activity context instead of application context. This is to avoid
@@ -328,13 +341,6 @@
 
         TabHelpers.initTabHelpers(this, creationState);
 
-        if (mParentId == INVALID_TAB_ID || getTabModelSelector() == null
-                || getTabModelSelector().getTabById(mParentId) == null) {
-            mRootId = mId;
-        } else {
-            mRootId = getTabModelSelector().getTabById(mParentId).getRootId();
-        }
-
         mAttachStateChangeListener = new OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View view) {
@@ -570,15 +576,6 @@
         return context == context.getApplicationContext() ? getThemedApplicationContext() : context;
     }
 
-    /**
-     * @return {@link TabModelSelector} that currently hosts the {@link TabModel} for this
-     *         {@link Tab}.
-     */
-    public TabModelSelector getTabModelSelector() {
-        if (getActivity() == null) return null;
-        return getActivity().getTabModelSelector();
-    }
-
     /** @return WebContentsState representing the state of the WebContents (navigations, etc.) */
     WebContentsState getFrozenContentsState() {
         return mFrozenContentsState;
@@ -995,11 +992,8 @@
         WebContents webContents = getWebContents();
         if (webContents != null) webContents.setTopLevelNativeWindow(null);
 
-        TabModelSelector tabModelSelector = getTabModelSelector();
-        if (tabModelSelector != null) {
-            tabModelSelector.getModel(mIncognito).removeTab(this);
-        }
-
+        // TabModelSelector of this Tab, if present, gets notified to remove the tab from
+        // the TabModel it belonged to.
         for (TabObserver observer : mObservers) {
             observer.onActivityAttachmentChanged(this, false);
         }
@@ -1215,17 +1209,9 @@
 
             mWebContentsDelegate = mDelegateFactory.createWebContentsDelegate(this);
 
-            int parentId = getParentId();
-            if (parentId != INVALID_TAB_ID) {
-                Tab parentTab = getTabModelSelector().getTabById(parentId);
-                if (parentTab != null && parentTab.isIncognito() != isIncognito()) {
-                    parentId = INVALID_TAB_ID;
-                }
-            }
-
             assert mNativeTabAndroid != 0;
             nativeInitWebContents(mNativeTabAndroid, mIncognito, isDetached(), webContents,
-                    parentId, mWebContentsDelegate,
+                    mSourceTabId, mWebContentsDelegate,
                     new TabContextMenuPopulator(
                             mDelegateFactory.createContextMenuPopulator(this), this));
 
@@ -1253,8 +1239,8 @@
         // completed.
         if (isDetached()) return false;
         NativePage candidateForReuse = forceReload ? null : getNativePage();
-        NativePage nativePage = NativePageFactory.createNativePageForURL(url, candidateForReuse,
-                this, getTabModelSelector(), getActivity());
+        NativePage nativePage = NativePageFactory.createNativePageForURL(
+                url, candidateForReuse, this, getActivity());
         if (nativePage != null) {
             showNativePage(nativePage);
             notifyPageTitleChanged();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBuilder.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBuilder.java
index c8f18054..ba1785c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBuilder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBuilder.java
@@ -15,7 +15,7 @@
  */
 public class TabBuilder {
     private int mId = Tab.INVALID_TAB_ID;
-    private int mParentId = Tab.INVALID_TAB_ID;
+    private Tab mParent;
     private boolean mIncognito;
     private WindowAndroid mWindow;
     private Integer mLaunchType;
@@ -34,12 +34,12 @@
     }
 
     /**
-     * Sets the id of the tab from which the new one is opened.
-     * @param parentId The id of the parent Tab.
+     * Sets the tab from which the new one is opened.
+     * @param parent The parent Tab.
      * @return {@link TabBuilder} creating the Tab.
      */
-    public TabBuilder setParentId(int parentId) {
-        mParentId = parentId;
+    public TabBuilder setParent(Tab parent) {
+        mParent = parent;
         return this;
     }
 
@@ -87,7 +87,7 @@
         }
 
         return new Tab(
-                mId, mParentId, mIncognito, mWindow, mLaunchType, mCreationType, mLoadUrlParams);
+                mId, mParent, mIncognito, mWindow, mLaunchType, mCreationType, mLoadUrlParams);
     }
 
     private TabBuilder setCreationType(@TabCreationState int type) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
index b960310..c60f254 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
@@ -25,6 +25,7 @@
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.tabmodel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.document.TabDelegate;
 import org.chromium.chrome.browser.util.IntentUtils;
 import org.chromium.chrome.browser.util.UrlUtilities;
@@ -173,7 +174,7 @@
         RecordUserAction.record("LinkOpenedInNewTab");
         LoadUrlParams loadUrlParams = new LoadUrlParams(url);
         loadUrlParams.setReferrer(referrer);
-        mTab.getTabModelSelector().openNewTab(
+        TabModelSelector.from(mTab).openNewTab(
                 loadUrlParams, TabLaunchType.FROM_LONGPRESS_BACKGROUND, mTab, isIncognito());
     }
 
@@ -191,8 +192,8 @@
     @Override
     public void onOpenInNewIncognitoTab(String url) {
         RecordUserAction.record("MobileNewTabOpened");
-        mTab.getTabModelSelector().openNewTab(new LoadUrlParams(url),
-                TabLaunchType.FROM_LONGPRESS_FOREGROUND, mTab, true);
+        TabModelSelector.from(mTab).openNewTab(
+                new LoadUrlParams(url), TabLaunchType.FROM_LONGPRESS_FOREGROUND, mTab, true);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabParentIntent.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabParentIntent.java
index 0d11c3d..c31f491d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabParentIntent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabParentIntent.java
@@ -8,6 +8,7 @@
 
 import org.chromium.base.UserData;
 import org.chromium.base.UserDataHost;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 
 /**
  * A holder of {@link Intent} object to be used to bring back the parent {@link Activity}
@@ -40,7 +41,7 @@
 
     @Override
     public void onCloseContents(Tab tab) {
-        boolean isSelected = mTab.getTabModelSelector().getCurrentTab() == tab;
+        boolean isSelected = TabModelSelector.from(mTab).getCurrentTab() == tab;
 
         // If the parent Tab belongs to another Activity, fire the Intent to bring it back.
         if (isSelected && mParentIntent != null && tab.getActivity().getIntent() != mParentIntent) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabUma.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabUma.java
index 48302ee..d2f546c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabUma.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabUma.java
@@ -14,6 +14,7 @@
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
 import org.chromium.chrome.browser.tabmodel.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 import org.chromium.chrome.browser.tabmodel.TabSelectionType;
 import org.chromium.net.NetError;
@@ -242,7 +243,7 @@
 
     @Override
     public void onShown(Tab tab, @TabSelectionType int selectionType) {
-        int rank = computeMRURank(tab, tab.getTabModelSelector().getModel(tab.isIncognito()));
+        int rank = computeMRURank(tab, TabModelSelector.from(tab).getModel(tab.isIncognito()));
         long previousTimestampMillis = tab.getTimestampMillis();
         long now = SystemClock.elapsedRealtime();
 
@@ -323,7 +324,7 @@
                     }
                 }
             };
-            tab.getTabModelSelector().addObserver(mNewTabObserver);
+            TabModelSelector.from(tab).addObserver(mNewTabObserver);
         }
 
         // Record "tab age upon first display" metrics. previousTimestampMillis is persisted through
@@ -371,7 +372,7 @@
     }
 
     private void removeObservers(Tab tab) {
-        if (mNewTabObserver != null) tab.getTabModelSelector().removeObserver(mNewTabObserver);
+        if (mNewTabObserver != null) TabModelSelector.from(tab).removeObserver(mNewTabObserver);
         tab.removeObserver(this);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java
index 98448f4..dcc7a15 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java
@@ -41,6 +41,7 @@
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager.TabCreator;
 import org.chromium.chrome.browser.tabmodel.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.tabmodel.TabWindowManager;
 import org.chromium.chrome.browser.util.IntentUtils;
@@ -91,7 +92,7 @@
         mHandler = new Handler();
         mCloseContentsRunnable = () -> {
             // TODO(jinsukkim): Move |closeTab| to TabModelSelector by making it observe its tabs.
-            mTab.getTabModelSelector().closeTab(mTab);
+            TabModelSelector.from(mTab).closeTab(mTab);
             RewindableIterator<TabObserver> observers = mTab.getTabObservers();
             while (observers.hasNext()) observers.next().onCloseContents(mTab);
         };
@@ -291,7 +292,7 @@
     protected TabModel getTabModel() {
         // TODO(dfalcantara): Remove this when DocumentActivity.getTabModelSelector()
         //                    can return a TabModelSelector that activateContents() can use.
-        return mTab.getTabModelSelector().getModel(mTab.isIncognito());
+        return TabModelSelector.from(mTab).getModel(mTab.isIncognito());
     }
 
     @CalledByNative
@@ -319,13 +320,13 @@
         // Creating new Tabs asynchronously requires starting a new Activity to create the Tab,
         // so the Tab returned will always be null.  There's no way to know synchronously
         // whether the Tab is created, so assume it's always successful.
-        boolean createdSuccessfully = tabCreator.createTabWithWebContents(mTab,
-                webContents, mTab.getId(), TabLaunchType.FROM_LONGPRESS_FOREGROUND, url);
+        boolean createdSuccessfully = tabCreator.createTabWithWebContents(
+                mTab, webContents, TabLaunchType.FROM_LONGPRESS_FOREGROUND, url);
         boolean success = tabCreator.createsTabsAsynchronously() || createdSuccessfully;
 
         if (success) {
             if (disposition == WindowOpenDisposition.NEW_FOREGROUND_TAB) {
-                if (mTab.getTabModelSelector()
+                if (TabModelSelector.from(mTab)
                                 .getTabModelFilterProvider()
                                 .getCurrentTabModelFilter()
                                 .getRelatedTabList(mTab.getId())
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
index 1af4ded2..99ec101 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
@@ -126,12 +126,13 @@
                         intent, IntentHandler.EXTRA_PARENT_INTENT);
                 parentId = IntentUtils.safeGetIntExtra(
                         intent, IntentHandler.EXTRA_PARENT_TAB_ID, parentId);
-
+                TabModelSelector selector = mActivity.getTabModelSelector();
+                parent = selector != null ? selector.getTabById(parentId) : null;
                 assert TabModelUtils.getTabIndexById(mTabModel, assignedTabId)
                         == TabModel.INVALID_TAB_INDEX;
                 tab = TabBuilder.createLiveTab(!openInForeground)
                               .setId(assignedTabId)
-                              .setParentId(parentId)
+                              .setParent(parent)
                               .setIncognito(mIncognito)
                               .setWindow(mNativeWindow)
                               .setLaunchType(type)
@@ -144,7 +145,7 @@
                 // to preserve resources (cpu, memory, strong renderer binding) for the foreground
                 // tab.
                 tab = TabBuilder.createForLazyLoad(loadUrlParams)
-                              .setParentId(parentId)
+                              .setParent(parent)
                               .setIncognito(mIncognito)
                               .setWindow(mNativeWindow)
                               .setLaunchType(type)
@@ -152,7 +153,7 @@
                 tab.initialize(null, delegateFactory, !openInForeground, null, false);
             } else {
                 tab = TabBuilder.createLiveTab(!openInForeground)
-                              .setParentId(parentId)
+                              .setParent(parent)
                               .setIncognito(mIncognito)
                               .setWindow(mNativeWindow)
                               .setLaunchType(type)
@@ -176,9 +177,10 @@
     }
 
     @Override
-    public boolean createTabWithWebContents(Tab parent, WebContents webContents, int parentId,
-            @TabLaunchType int type, String url) {
+    public boolean createTabWithWebContents(
+            Tab parent, WebContents webContents, @TabLaunchType int type, String url) {
         // The parent tab was already closed.  Do not open child tabs.
+        int parentId = parent != null ? parent.getId() : Tab.INVALID_TAB_ID;
         if (mTabModel.isClosurePending(parentId)) return false;
 
         // If parent is in the same tab model, place the new tab next to it.
@@ -190,7 +192,7 @@
         TabDelegateFactory delegateFactory = parent == null ? createDefaultTabDelegateFactory()
                 : parent.getDelegateFactory();
         Tab tab = TabBuilder.createLiveTab(!openInForeground)
-                          .setParentId(parentId)
+                          .setParent(parent)
                           .setIncognito(mIncognito)
                           .setWindow(mNativeWindow)
                           .setLaunchType(type)
@@ -291,9 +293,11 @@
 
     @Override
     public Tab createFrozenTab(TabState state, int id, int index) {
+        TabModelSelector selector = mActivity.getTabModelSelector();
+        Tab parent = selector != null ? selector.getTabById(state.parentId) : null;
         Tab tab = TabBuilder.createFromFrozenState()
                           .setId(id)
-                          .setParentId(state.parentId)
+                          .setParent(parent)
                           .setIncognito(state.isIncognito())
                           .setWindow(mNativeWindow)
                           .build();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCreatorManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCreatorManager.java
index 9825ab5..3ae6f111 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCreatorManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCreatorManager.java
@@ -65,26 +65,23 @@
          * Creates a Tab to host the given WebContents.
          * @param parent      The parent tab, if present.
          * @param webContents The web contents to create a tab around.
-         * @param parentId    The id of the parent tab.
          * @param type        The TabLaunchType describing how this tab was created.
          * @param url         URL to show in the Tab. (Needed only for asynchronous tab creation.)
          * @return            Whether a Tab was created successfully.
          */
-        public abstract boolean createTabWithWebContents(Tab parent, WebContents webContents,
-                int parentId, @TabLaunchType int type, String url);
+        public abstract boolean createTabWithWebContents(
+                Tab parent, WebContents webContents, @TabLaunchType int type, String url);
 
         /**
          * Creates a tab around the native web contents pointer.
          * @param parent      The parent tab, if present.
          * @param webContents The web contents to create a tab around.
-         * @param parentId    The id of the parent tab.
          * @param type        The TabLaunchType describing how this tab was created.
          * @return            Whether a Tab was created successfully.
          */
         public final boolean createTabWithWebContents(
-                Tab parent, WebContents webContents, int parentId, @TabLaunchType int type) {
-            return createTabWithWebContents(
-                    parent, webContents, parentId, type, webContents.getVisibleUrl());
+                Tab parent, WebContents webContents, @TabLaunchType int type) {
+            return createTabWithWebContents(parent, webContents, type, webContents.getVisibleUrl());
         }
 
         /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
index b52f48e..f68e8cb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
@@ -760,10 +760,10 @@
     }
 
     @Override
-    protected boolean createTabWithWebContents(Tab parent, boolean incognito,
-            WebContents webContents, int parentId) {
-        return getTabCreator(incognito).createTabWithWebContents(parent, webContents, parentId,
-                TabLaunchType.FROM_LONGPRESS_BACKGROUND);
+    protected boolean createTabWithWebContents(
+            Tab parent, boolean incognito, WebContents webContents) {
+        return getTabCreator(incognito).createTabWithWebContents(
+                parent, webContents, TabLaunchType.FROM_LONGPRESS_BACKGROUND);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
index 9e7153c..c9493fa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
@@ -117,12 +117,11 @@
      * @param parent      The parent tab that creates the new tab.
      * @param incognito   Whether or not the tab is incognito.
      * @param webContents A {@link WebContents} object.
-     * @param parentId    ID of the parent.
      * @return Whether or not the Tab was successfully created.
      */
     @CalledByNative
-    protected abstract boolean createTabWithWebContents(Tab parent, boolean incognito,
-            WebContents webContents, int parentId);
+    protected abstract boolean createTabWithWebContents(
+            Tab parent, boolean incognito, WebContents webContents);
 
     @CalledByNative
     protected abstract void openNewTab(Tab parent, String url, String initiatorOrigin,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelector.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelector.java
index 0deb20e..2460777 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelector.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelector.java
@@ -20,6 +20,16 @@
  */
 public interface TabModelSelector {
     /**
+     * @param tab The Tab to get its {@link TabModelSelector} from.
+     * @return {@link TabModelSelector} that currently hosts the {@link TabModel} for this
+     *         {@link Tab}.
+     */
+    public static TabModelSelector from(Tab tab) {
+        if (tab == null || tab.getActivity() == null) return null;
+        return tab.getActivity().getTabModelSelector();
+    }
+
+    /**
      * A delegate interface to push close all tabs requests.
      */
     public interface CloseAllTabsDelegate {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
index 16f3346..3435a2a0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
@@ -193,6 +193,11 @@
             public void onNavigationEntriesDeleted(Tab tab) {
                 mTabSaver.addTabToSaveQueue(tab);
             }
+
+            @Override
+            public void onActivityAttachmentChanged(Tab tab, boolean attached) {
+                if (!attached) getModel(tab.isIncognito()).removeTab(tab);
+            }
         };
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModelImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModelImpl.java
index ce04bf4..c0d59c2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModelImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModelImpl.java
@@ -271,8 +271,8 @@
     }
 
     @Override
-    protected boolean createTabWithWebContents(Tab parent, boolean isIncognito,
-            WebContents webContents, int parentTabId) {
+    protected boolean createTabWithWebContents(
+            Tab parent, boolean isIncognito, WebContents webContents) {
         return false;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java
index 1f228145..edeaeab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java
@@ -61,14 +61,14 @@
     }
 
     @Override
-    public boolean createTabWithWebContents(Tab parent, WebContents webContents, int parentId,
-            @TabLaunchType int type, String url) {
+    public boolean createTabWithWebContents(
+            Tab parent, WebContents webContents, @TabLaunchType int type, String url) {
         if (url == null) url = "";
 
         AsyncTabCreationParams asyncParams =
                 new AsyncTabCreationParams(
                         new LoadUrlParams(url, PageTransition.AUTO_TOPLEVEL), webContents);
-        createNewTab(asyncParams, type, parentId);
+        createNewTab(asyncParams, type, parent != null ? parent.getId() : Tab.INVALID_TAB_ID);
         return true;
     }
 
@@ -80,8 +80,8 @@
      * @param activity      The current {@link Activity}
      * @param parentId      The ID of the parent tab, or {@link Tab#INVALID_TAB_ID}.
      */
-    public void createTabInOtherWindow(LoadUrlParams loadUrlParams, Activity activity,
-            int parentId) {
+    public void createTabInOtherWindow(
+            LoadUrlParams loadUrlParams, Activity activity, int parentId) {
         Intent intent = createNewTabIntent(
                 new AsyncTabCreationParams(loadUrlParams), parentId, false);
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RecentTabsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RecentTabsTest.java
index 47ed82c..43ceed1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RecentTabsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RecentTabsTest.java
@@ -104,7 +104,7 @@
                 new ClientId(OfflinePageBridge.LAST_N_NAMESPACE, Integer.toString(tab.getId()));
 
         // The tab should be foreground and so no snapshot should exist.
-        TabModelSelector tabModelSelector = tab.getTabModelSelector();
+        TabModelSelector tabModelSelector = TabModelSelector.from(tab);
         Assert.assertEquals(tabModelSelector.getCurrentTab(), tab);
         Assert.assertFalse(tab.isHidden());
         Assert.assertNull(OfflineTestUtil.getPageByClientId(firstTabClientId));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/MockTab.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/MockTab.java
index 62f4c76..62666bd1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/MockTab.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/MockTab.java
@@ -13,6 +13,6 @@
      * these two fields only.
      */
     public MockTab(int id, boolean incognito) {
-        super(id, Tab.INVALID_TAB_ID, incognito, null, null, null, null);
+        super(id, null, incognito, null, null, null, null);
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
index 6b7b103..7b678692 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
@@ -114,7 +114,6 @@
         public Tab createFrozenTab(TabState state, int id, int index) {
             Tab tab = TabBuilder.createFromFrozenState()
                               .setId(id)
-                              .setParentId(state.parentId)
                               .setIncognito(state.isIncognito())
                               .build();
             TabTestUtils.restoreFieldsFromState(tab, state);
@@ -124,8 +123,8 @@
         }
 
         @Override
-        public boolean createTabWithWebContents(Tab parent, WebContents webContents, int parentId,
-                @TabLaunchType int type, String url) {
+        public boolean createTabWithWebContents(
+                Tab parent, WebContents webContents, @TabLaunchType int type, String url) {
             return false;
         }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNavigationTest.java
index bb34894..638d132 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNavigationTest.java
@@ -32,6 +32,7 @@
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule;
 import org.chromium.chrome.browser.vr.util.NativeUiUtils;
 import org.chromium.chrome.browser.vr.util.RenderTestUtils;
@@ -716,7 +717,7 @@
         VrBrowserTransitionUtils.forceExitVr();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             // Close the tab that's automatically open at test start.
-            mTestRule.getActivity().getActivityTab().getTabModelSelector().closeAllTabs();
+            TabModelSelector.from(mTestRule.getActivity().getActivityTab()).closeAllTabs();
             // Create an Incognito tab. Closing all tabs automatically goes to overview mode, but
             // appears to take some amount of time to do so. Instead of waiting until then and
             // creating through the menu item, just create an Incognito tab directly.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/native_page/NativePageFactoryTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/native_page/NativePageFactoryTest.java
index e6359ad..39bc2bd 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/native_page/NativePageFactoryTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/native_page/NativePageFactoryTest.java
@@ -199,7 +199,7 @@
                     MockNativePage candidate = candidateType == NativePageType.NONE ? null
                             : new MockNativePage(candidateType);
                     MockNativePage page = (MockNativePage) NativePageFactory.createNativePageForURL(
-                            urlCombo.url, candidate, null, null, null, isIncognito);
+                            urlCombo.url, candidate, null, null, isIncognito);
                     String debugMessage = String.format(
                             "Failed test case: isIncognito=%s, urlCombo={%s,%s}, candidateType=%s",
                             isIncognito, urlCombo.url, urlCombo.expectedType, candidateType);
@@ -224,14 +224,16 @@
     public void testCreateNativePageWithInvalidUrl() {
         for (UrlCombo urlCombo : VALID_URLS) {
             if (!isValidInIncognito(urlCombo)) {
-                Assert.assertNull(urlCombo.url, NativePageFactory.createNativePageForURL(
-                        urlCombo.url, null, null, null, null, true));
+                Assert.assertNull(urlCombo.url,
+                        NativePageFactory.createNativePageForURL(
+                                urlCombo.url, null, null, null, true));
             }
         }
         for (boolean isIncognito : new boolean[] {true, false}) {
             for (String invalidUrl : INVALID_URLS) {
-                Assert.assertNull(invalidUrl, NativePageFactory.createNativePageForURL(invalidUrl,
-                        null, null, null, null, isIncognito));
+                Assert.assertNull(invalidUrl,
+                        NativePageFactory.createNativePageForURL(
+                                invalidUrl, null, null, null, isIncognito));
             }
         }
     }
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 01d056a..be40d0f 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -552,6 +552,21 @@
        Only Android devices are currently supported.
     </message>
 
+    <!-- Plugin VM Page -->
+    <!-- TODO(timloh): Remove transleatable flag after strings are finalized. -->
+    <message name="IDS_SETTINGS_PLUGIN_VM_PAGE_TITLE" desc="The title of Plugin VM section." translateable="false">
+      Plugin VM
+    </message>
+    <message name="IDS_SETTINGS_PLUGIN_VM_PAGE_LABEL" desc="The text associated with the primary section setting for Plugin VM." translateable="false">
+      Plugin VM
+    </message>
+    <message name="IDS_SETTINGS_PLUGIN_VM_PAGE_SUBTEXT" desc="Description for the section for enabling and managing Crostini." translateable="false">
+      Run Plugin VM apps on your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>
+    </message>
+    <message name="IDS_SETTINGS_PLUGIN_VM_PRINTER_ACCESS" desc="The text associated with the primary section setting for Plugin VM." translateable="false">
+      Give access to printers
+    </message>
+
     <!-- Android Apps Page -->
     <message name="IDS_SETTINGS_ANDROID_APPS_TITLE" desc="The title of Google Play Store (Arc++ / Android Apps) section.">
       Google Play Store
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index a12eaa94..7df20f2 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2488,6 +2488,8 @@
       "android/send_tab_to_self/android_notification_handler.cc",
       "android/send_tab_to_self/android_notification_handler.h",
       "android/send_tab_to_self/send_tab_to_self_android_bridge.cc",
+      "android/send_tab_to_self/send_tab_to_self_infobar.cc",
+      "android/send_tab_to_self/send_tab_to_self_infobar.h",
       "android/service_tab_launcher.cc",
       "android/service_tab_launcher.h",
       "android/sessions/session_tab_helper_android.cc",
@@ -2755,10 +2757,10 @@
       "apps/app_service/app_icon_factory.h",
       "apps/app_service/app_icon_source.cc",
       "apps/app_service/app_icon_source.h",
-      "apps/app_service/app_service_proxy.cc",
-      "apps/app_service/app_service_proxy.h",
       "apps/app_service/app_service_proxy_factory.cc",
       "apps/app_service/app_service_proxy_factory.h",
+      "apps/app_service/app_service_proxy_impl.cc",
+      "apps/app_service/app_service_proxy_impl.h",
       "apps/app_service/dip_px_util.cc",
       "apps/app_service/dip_px_util.h",
       "apps/intent_helper/apps_navigation_throttle.cc",
@@ -3317,6 +3319,7 @@
       "//chrome/browser/search:generated",
       "//chrome/common/importer:interfaces",
       "//chrome/services/app_service:lib",
+      "//chrome/services/app_service/public/cpp:app_service_proxy",
       "//chrome/services/app_service/public/cpp:app_update",
       "//chrome/services/app_service/public/cpp:icon_loader",
       "//components/feedback",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 29dcef3a..0eb15c8 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1365,6 +1365,9 @@
     {"single-process-mash", flag_descriptions::kSingleProcessMashName,
      flag_descriptions::kSingleProcessMashDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(features::kSingleProcessMash)},
+    {"mojo-imf", flag_descriptions::kMojoImfName,
+     flag_descriptions::kMojoImfDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kMojoIMF)},
 #endif  // OS_CHROMEOS
     {
         "disable-accelerated-video-decode",
@@ -1494,11 +1497,6 @@
     {"enable-virtual-keyboard", flag_descriptions::kVirtualKeyboardName,
      flag_descriptions::kVirtualKeyboardDescription, kOsCrOS,
      SINGLE_VALUE_TYPE(keyboard::switches::kEnableVirtualKeyboard)},
-    {"virtual-keyboard-overscroll",
-     flag_descriptions::kVirtualKeyboardOverscrollName,
-     flag_descriptions::kVirtualKeyboardOverscrollDescription, kOsCrOS,
-     SINGLE_DISABLE_VALUE_TYPE(
-         keyboard::switches::kDisableVirtualKeyboardOverscroll)},
     {"enable-physical-keyboard-autocorrect",
      flag_descriptions::kPhysicalKeyboardAutocorrectName,
      flag_descriptions::kPhysicalKeyboardAutocorrectDescription, kOsCrOS,
diff --git a/chrome/browser/android/send_tab_to_self/send_tab_to_self_infobar.cc b/chrome/browser/android/send_tab_to_self/send_tab_to_self_infobar.cc
new file mode 100644
index 0000000..e833fd6f
--- /dev/null
+++ b/chrome/browser/android/send_tab_to_self/send_tab_to_self_infobar.cc
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/send_tab_to_self/send_tab_to_self_infobar.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/android/jni_string.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/android/tab_android.h"
+#include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/ui/android/infobars/infobar_android.h"
+#include "components/infobars/core/infobar_delegate.h"
+#include "content/public/browser/web_contents.h"
+#include "jni/SendTabToSelfInfoBar_jni.h"
+
+namespace send_tab_to_self {
+
+SendTabToSelfInfoBar::SendTabToSelfInfoBar(
+    std::unique_ptr<SendTabToSelfInfoBarDelegate> delegate)
+    : InfoBarAndroid(std::move(delegate)) {}
+
+SendTabToSelfInfoBar::~SendTabToSelfInfoBar() = default;
+
+void SendTabToSelfInfoBar::ProcessButton(int action) {
+  NOTREACHED();  // No button on this infobar.
+}
+
+base::android::ScopedJavaLocalRef<jobject>
+SendTabToSelfInfoBar::CreateRenderInfoBar(JNIEnv* env) {
+  return Java_SendTabToSelfInfoBar_create(env);
+}
+
+void SendTabToSelfInfoBar::OnLinkClicked(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj) {
+  // TODO(crbug.com/949233): Open the tab here via the delegate
+  NOTIMPLEMENTED();
+}
+
+// static
+void SendTabToSelfInfoBar::ShowInfoBar(content::WebContents* web_contents,
+                                       SendTabToSelfInfoBarDelegate* delegate) {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace send_tab_to_self
diff --git a/chrome/browser/android/send_tab_to_self/send_tab_to_self_infobar.h b/chrome/browser/android/send_tab_to_self/send_tab_to_self_infobar.h
new file mode 100644
index 0000000..ad815568
--- /dev/null
+++ b/chrome/browser/android/send_tab_to_self/send_tab_to_self_infobar.h
@@ -0,0 +1,44 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_INFOBAR_H_
+#define CHROME_BROWSER_ANDROID_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_INFOBAR_H_
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/android/infobars/infobar_android.h"
+#include "components/send_tab_to_self/send_tab_to_self_infobar_delegate.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace send_tab_to_self {
+// Communicates to the user that a tab was shared from another device. See
+// SendTabToSelfInfoBar.java for UI specifics, and SendTabToSelfInfobarDelegate
+// for behavior specifics.
+class SendTabToSelfInfoBar : public InfoBarAndroid {
+ public:
+  ~SendTabToSelfInfoBar() override;
+  // |delegate| must remain alive while showing this info bar.
+  static void ShowInfoBar(content::WebContents* web_contents,
+                          SendTabToSelfInfoBarDelegate* delegate);
+
+ private:
+  explicit SendTabToSelfInfoBar(
+      std::unique_ptr<SendTabToSelfInfoBarDelegate> delegate);
+  // InfoBarAndroid:
+  base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar(
+      JNIEnv* env) override;
+  void OnLinkClicked(JNIEnv* env,
+                     const base::android::JavaParamRef<jobject>& obj) override;
+  void ProcessButton(int action) override;
+
+  DISALLOW_COPY_AND_ASSIGN(SendTabToSelfInfoBar);
+};
+
+}  // namespace send_tab_to_self
+
+#endif  // CHROME_BROWSER_ANDROID_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_INFOBAR_H_
diff --git a/chrome/browser/apps/app_service/app_icon_source.cc b/chrome/browser/apps/app_service/app_icon_source.cc
index 0a16d96..92b97cd 100644
--- a/chrome/browser/apps/app_service/app_icon_source.cc
+++ b/chrome/browser/apps/app_service/app_icon_source.cc
@@ -11,10 +11,11 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/dip_px_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/url_constants.h"
+#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "extensions/grit/extensions_browser_resources.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "url/gurl.h"
@@ -80,7 +81,7 @@
   int size_in_dip = apps_util::ConvertPxToDip(size);
 
   apps::AppServiceProxy* app_service_proxy =
-      apps::AppServiceProxy::Get(profile_);
+      apps::AppServiceProxyFactory::GetForProfile(profile_);
   if (!app_service_proxy) {
     LoadDefaultImage(callback);
     return;
diff --git a/chrome/browser/apps/app_service/app_service_proxy_factory.cc b/chrome/browser/apps/app_service/app_service_proxy_factory.cc
index b407a8b..b657fa7 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_factory.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_factory.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 
 #include "base/feature_list.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_impl.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -26,7 +26,7 @@
   //     is branched from (i.e. "inherit" the parent service),
   //   - return a temporary service just for the incognito session (probably
   //     the least sensible option).
-  return static_cast<AppServiceProxy*>(
+  return static_cast<AppServiceProxyImpl*>(
       AppServiceProxyFactory::GetInstance()->GetServiceForBrowserContext(
           profile, true /* create */));
 }
@@ -57,7 +57,7 @@
 
 KeyedService* AppServiceProxyFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  return new AppServiceProxy(Profile::FromBrowserContext(context));
+  return new AppServiceProxyImpl(Profile::FromBrowserContext(context));
 }
 
 bool AppServiceProxyFactory::ServiceIsCreatedWithBrowserContext() const {
diff --git a/chrome/browser/apps/app_service/app_service_proxy.cc b/chrome/browser/apps/app_service/app_service_proxy_impl.cc
similarity index 75%
rename from chrome/browser/apps/app_service/app_service_proxy.cc
rename to chrome/browser/apps/app_service/app_service_proxy_impl.cc
index 6f61a5e4..12dae356 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_impl.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 "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_impl.h"
 
 #include <utility>
 
@@ -16,10 +16,10 @@
 
 namespace apps {
 
-AppServiceProxy::InnerIconLoader::InnerIconLoader(AppServiceProxy* host)
+AppServiceProxyImpl::InnerIconLoader::InnerIconLoader(AppServiceProxyImpl* host)
     : host_(host), overriding_icon_loader_for_testing_(nullptr) {}
 
-apps::mojom::IconKeyPtr AppServiceProxy::InnerIconLoader::GetIconKey(
+apps::mojom::IconKeyPtr AppServiceProxyImpl::InnerIconLoader::GetIconKey(
     const std::string& app_id) {
   if (overriding_icon_loader_for_testing_) {
     return overriding_icon_loader_for_testing_->GetIconKey(app_id);
@@ -35,7 +35,7 @@
 }
 
 std::unique_ptr<IconLoader::Releaser>
-AppServiceProxy::InnerIconLoader::LoadIconFromIconKey(
+AppServiceProxyImpl::InnerIconLoader::LoadIconFromIconKey(
     apps::mojom::AppType app_type,
     const std::string& app_id,
     apps::mojom::IconKeyPtr icon_key,
@@ -69,11 +69,12 @@
 }
 
 // static
-AppServiceProxy* AppServiceProxy::Get(Profile* profile) {
-  return AppServiceProxyFactory::GetForProfile(profile);
+AppServiceProxyImpl* AppServiceProxyImpl::GetImplForTesting(Profile* profile) {
+  return static_cast<AppServiceProxyImpl*>(
+      AppServiceProxyFactory::GetForProfile(profile));
 }
 
-AppServiceProxy::AppServiceProxy(Profile* profile)
+AppServiceProxyImpl::AppServiceProxyImpl(Profile* profile)
     : inner_icon_loader_(this),
       outer_icon_loader_(&inner_icon_loader_,
                          apps::IconCache::GarbageCollectionPolicy::kEager) {
@@ -88,16 +89,16 @@
   connector->BindInterface(apps::mojom::kServiceName,
                            mojo::MakeRequest(&app_service_));
 
-  // The AppServiceProxy is a subscriber: something that wants to be able to
-  // list all known apps.
+  // The AppServiceProxyImpl is a subscriber: something that wants to be able
+  // to list all known apps.
   apps::mojom::SubscriberPtr subscriber;
   bindings_.AddBinding(this, mojo::MakeRequest(&subscriber));
   app_service_->RegisterSubscriber(std::move(subscriber), nullptr);
 
 #if defined(OS_CHROMEOS)
-  // The AppServiceProxy is also a publisher, of a variety of app types. That
-  // responsibility isn't intrinsically part of the AppServiceProxy, but doing
-  // that here, for each such app type, is as good a place as any.
+  // The AppServiceProxyImpl is also a publisher, of a variety of app types.
+  // That responsibility isn't intrinsically part of the AppServiceProxyImpl,
+  // but doing that here, for each such app type, is as good a place as any.
   built_in_chrome_os_apps_.Initialize(app_service_, profile);
   crostini_apps_.Initialize(app_service_, profile);
   extension_apps_.Initialize(app_service_, profile,
@@ -107,22 +108,15 @@
 #endif  // OS_CHROMEOS
 }
 
-AppServiceProxy::~AppServiceProxy() = default;
+AppServiceProxyImpl::~AppServiceProxyImpl() = default;
 
-apps::mojom::AppServicePtr& AppServiceProxy::AppService() {
-  return app_service_;
-}
-
-apps::AppRegistryCache& AppServiceProxy::AppRegistryCache() {
-  return cache_;
-}
-
-apps::mojom::IconKeyPtr AppServiceProxy::GetIconKey(const std::string& app_id) {
+apps::mojom::IconKeyPtr AppServiceProxyImpl::GetIconKey(
+    const std::string& app_id) {
   return outer_icon_loader_.GetIconKey(app_id);
 }
 
 std::unique_ptr<apps::IconLoader::Releaser>
-AppServiceProxy::LoadIconFromIconKey(
+AppServiceProxyImpl::LoadIconFromIconKey(
     apps::mojom::AppType app_type,
     const std::string& app_id,
     apps::mojom::IconKeyPtr icon_key,
@@ -135,10 +129,10 @@
       allow_placeholder_icon, std::move(callback));
 }
 
-void AppServiceProxy::Launch(const std::string& app_id,
-                             int32_t event_flags,
-                             apps::mojom::LaunchSource launch_source,
-                             int64_t display_id) {
+void AppServiceProxyImpl::Launch(const std::string& app_id,
+                                 int32_t event_flags,
+                                 apps::mojom::LaunchSource launch_source,
+                                 int64_t display_id) {
   if (app_service_.is_bound()) {
     cache_.ForOneApp(app_id, [this, event_flags, launch_source,
                               display_id](const apps::AppUpdate& update) {
@@ -148,8 +142,8 @@
   }
 }
 
-void AppServiceProxy::SetPermission(const std::string& app_id,
-                                    apps::mojom::PermissionPtr permission) {
+void AppServiceProxyImpl::SetPermission(const std::string& app_id,
+                                        apps::mojom::PermissionPtr permission) {
   if (app_service_.is_bound()) {
     cache_.ForOneApp(
         app_id, [this, &permission](const apps::AppUpdate& update) {
@@ -159,7 +153,7 @@
   }
 }
 
-void AppServiceProxy::Uninstall(const std::string& app_id) {
+void AppServiceProxyImpl::Uninstall(const std::string& app_id) {
   if (app_service_.is_bound()) {
     cache_.ForOneApp(app_id, [this](const apps::AppUpdate& update) {
       app_service_->Uninstall(update.AppType(), update.AppId());
@@ -167,7 +161,7 @@
   }
 }
 
-void AppServiceProxy::OpenNativeSettings(const std::string& app_id) {
+void AppServiceProxyImpl::OpenNativeSettings(const std::string& app_id) {
   if (app_service_.is_bound()) {
     cache_.ForOneApp(app_id, [this](const apps::AppUpdate& update) {
       app_service_->OpenNativeSettings(update.AppType(), update.AppId());
@@ -175,7 +169,7 @@
   }
 }
 
-apps::IconLoader* AppServiceProxy::OverrideInnerIconLoaderForTesting(
+apps::IconLoader* AppServiceProxyImpl::OverrideInnerIconLoaderForTesting(
     apps::IconLoader* icon_loader) {
   apps::IconLoader* old =
       inner_icon_loader_.overriding_icon_loader_for_testing_;
@@ -183,7 +177,7 @@
   return old;
 }
 
-void AppServiceProxy::Shutdown() {
+void AppServiceProxyImpl::Shutdown() {
 #if defined(OS_CHROMEOS)
   if (app_service_.is_bound()) {
     extension_apps_.Shutdown();
@@ -192,11 +186,11 @@
 #endif  // OS_CHROMEOS
 }
 
-void AppServiceProxy::OnApps(std::vector<apps::mojom::AppPtr> deltas) {
+void AppServiceProxyImpl::OnApps(std::vector<apps::mojom::AppPtr> deltas) {
   cache_.OnApps(std::move(deltas));
 }
 
-void AppServiceProxy::Clone(apps::mojom::SubscriberRequest request) {
+void AppServiceProxyImpl::Clone(apps::mojom::SubscriberRequest request) {
   bindings_.AddBinding(this, std::move(request));
 }
 
diff --git a/chrome/browser/apps/app_service/app_service_proxy.h b/chrome/browser/apps/app_service/app_service_proxy_impl.h
similarity index 76%
rename from chrome/browser/apps/app_service/app_service_proxy.h
rename to chrome/browser/apps/app_service/app_service_proxy_impl.h
index 4a75605..e142ef5b 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.h
+++ b/chrome/browser/apps/app_service/app_service_proxy_impl.h
@@ -2,16 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_H_
-#define CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_H_
+#ifndef CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_IMPL_H_
+#define CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_IMPL_H_
 
 #include <memory>
 
 #include "base/macros.h"
-#include "chrome/services/app_service/public/cpp/app_registry_cache.h"
+#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "chrome/services/app_service/public/cpp/icon_cache.h"
-#include "chrome/services/app_service/public/mojom/app_service.mojom.h"
-#include "chrome/services/app_service/public/mojom/types.mojom.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 
@@ -31,20 +29,23 @@
 // proxy for a given Profile, and therefore share its caches.
 //
 // See chrome/services/app_service/README.md.
-class AppServiceProxy : public KeyedService,
-                        public apps::IconLoader,
-                        public apps::mojom::Subscriber {
+class AppServiceProxyImpl : public KeyedService,
+                            public apps::AppServiceProxy,
+                            public apps::mojom::Subscriber {
  public:
-  static AppServiceProxy* Get(Profile* profile);
+  // This method returns an AppServiceProxyImpl, not just an AppServiceProxy, so
+  // that callers (which are presumably in test code) can then call other
+  // XxxForTesting methods.
+  //
+  // For regular (non-test) code, use AppServiceProxyFactory::GetForProfile
+  // instead.
+  static AppServiceProxyImpl* GetImplForTesting(Profile* profile);
 
-  explicit AppServiceProxy(Profile* profile);
+  explicit AppServiceProxyImpl(Profile* profile);
 
-  ~AppServiceProxy() override;
+  ~AppServiceProxyImpl() override;
 
-  apps::mojom::AppServicePtr& AppService();
-  apps::AppRegistryCache& AppRegistryCache();
-
-  // apps::IconLoader overrides.
+  // apps::AppServiceProxy (including apps::IconLoader) overrides.
   apps::mojom::IconKeyPtr GetIconKey(const std::string& app_id) override;
   std::unique_ptr<IconLoader::Releaser> LoadIconFromIconKey(
       apps::mojom::AppType app_type,
@@ -54,18 +55,14 @@
       int32_t size_hint_in_dip,
       bool allow_placeholder_icon,
       apps::mojom::Publisher::LoadIconCallback callback) override;
-
   void Launch(const std::string& app_id,
               int32_t event_flags,
               apps::mojom::LaunchSource launch_source,
-              int64_t display_id);
-
+              int64_t display_id) override;
   void SetPermission(const std::string& app_id,
-                     apps::mojom::PermissionPtr permission);
-
-  void Uninstall(const std::string& app_id);
-
-  void OpenNativeSettings(const std::string& app_id);
+                     apps::mojom::PermissionPtr permission) override;
+  void Uninstall(const std::string& app_id) override;
+  void OpenNativeSettings(const std::string& app_id) override;
 
   apps::IconLoader* OverrideInnerIconLoaderForTesting(
       apps::IconLoader* icon_loader);
@@ -74,8 +71,8 @@
   // An adapter, presenting an IconLoader interface based on the underlying
   // Mojo service (or on a fake implementation for testing).
   //
-  // Conceptually, the ASP (the AppServiceProxy) is itself such an adapter: UI
-  // clients call the IconLoader::LoadIconFromIconKey method (which the ASP
+  // Conceptually, the ASP (the AppServiceProxyImpl) is itself such an adapter:
+  // UI clients call the IconLoader::LoadIconFromIconKey method (which the ASP
   // implements) and the ASP translates (i.e. adapts) these to Mojo calls (or
   // C++ calls to the Fake). This diagram shows control flow going left to
   // right (with "=c=>" and "=m=>" denoting C++ and Mojo calls), and the
@@ -104,7 +101,7 @@
   // component: the one that ultimately talks to the Mojo service.
   //
   // The outer_icon_loader_ field (of type IconCache) is the "Outer" component:
-  // the entry point for calls into the AppServiceProxy.
+  // the entry point for calls into the AppServiceProxyImpl.
   //
   // Note that even if the ASP provides some icon caching, upstream UI clients
   // may want to introduce further icon caching. See the commentary where
@@ -113,7 +110,7 @@
   // IPC coalescing would be one of the "MoreDecorators".
   class InnerIconLoader : public apps::IconLoader {
    public:
-    explicit InnerIconLoader(AppServiceProxy* host);
+    explicit InnerIconLoader(AppServiceProxyImpl* host);
 
     // apps::IconLoader overrides.
     apps::mojom::IconKeyPtr GetIconKey(const std::string& app_id) override;
@@ -126,8 +123,9 @@
         bool allow_placeholder_icon,
         apps::mojom::Publisher::LoadIconCallback callback) override;
 
-    // |host_| owns |this|, as the InnerIconLoader is an AppServiceProxy field.
-    AppServiceProxy* host_;
+    // |host_| owns |this|, as the InnerIconLoader is an AppServiceProxyImpl
+    // field.
+    AppServiceProxyImpl* host_;
 
     apps::IconLoader* overriding_icon_loader_for_testing_;
   };
@@ -139,9 +137,7 @@
   void OnApps(std::vector<apps::mojom::AppPtr> deltas) override;
   void Clone(apps::mojom::SubscriberRequest request) override;
 
-  apps::mojom::AppServicePtr app_service_;
   mojo::BindingSet<apps::mojom::Subscriber> bindings_;
-  apps::AppRegistryCache cache_;
 
   // The LoadIconFromIconKey implementation sends a chained series of requests
   // through each icon loader, starting from the outer and working back to the
@@ -158,9 +154,9 @@
   ExtensionApps extension_web_apps_;
 #endif  // OS_CHROMEOS
 
-  DISALLOW_COPY_AND_ASSIGN(AppServiceProxy);
+  DISALLOW_COPY_AND_ASSIGN(AppServiceProxyImpl);
 };
 
 }  // namespace apps
 
-#endif  // CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_H_
+#endif  // CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_IMPL_H_
diff --git a/chrome/browser/apps/app_service/app_service_proxy_unittest.cc b/chrome/browser/apps/app_service/app_service_proxy_impl_unittest.cc
similarity index 93%
rename from chrome/browser/apps/app_service/app_service_proxy_unittest.cc
rename to chrome/browser/apps/app_service/app_service_proxy_impl_unittest.cc
index cfad9c2..a147ecd 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_unittest.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_impl_unittest.cc
@@ -6,12 +6,12 @@
 #include <vector>
 
 #include "base/callback.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image_skia_rep.h"
 
-class AppServiceProxyTest : public testing::Test {
+class AppServiceProxyImplTest : public testing::Test {
  protected:
   using UniqueReleaser = std::unique_ptr<apps::IconLoader::Releaser>;
 
@@ -66,11 +66,11 @@
 
     return loader->LoadIcon(app_type, app_id, icon_compression,
                             size_hint_in_dip, allow_placeholder_icon,
-                            base::BindOnce(&AppServiceProxyTest::OnLoadIcon,
+                            base::BindOnce(&AppServiceProxyImplTest::OnLoadIcon,
                                            base::Unretained(this)));
   }
 
-  void OverrideAppServiceProxyInnerIconLoader(apps::AppServiceProxy* proxy,
+  void OverrideAppServiceProxyInnerIconLoader(apps::AppServiceProxyImpl* proxy,
                                               apps::IconLoader* icon_loader) {
     proxy->OverrideInnerIconLoaderForTesting(icon_loader);
   }
@@ -84,8 +84,8 @@
   int num_outer_finished_callbacks_ = 0;
 };
 
-TEST_F(AppServiceProxyTest, IconCache) {
-  apps::AppServiceProxy proxy(nullptr);
+TEST_F(AppServiceProxyImplTest, IconCache) {
+  apps::AppServiceProxyImpl proxy(nullptr);
   FakeIconLoader fake;
   OverrideAppServiceProxyInnerIconLoader(&proxy, &fake);
 
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index a168a56..0899b24 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -10,7 +10,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/arc_apps_factory.h"
 #include "chrome/browser/apps/app_service/dip_px_util.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
@@ -19,6 +19,7 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_icon_descriptor.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/grit/component_extension_resources.h"
+#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "components/arc/app_permissions/arc_app_permissions_bridge.h"
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/common/app.mojom.h"
@@ -172,8 +173,9 @@
 
   apps::mojom::PublisherPtr publisher;
   binding_.Bind(mojo::MakeRequest(&publisher));
-  apps::AppServiceProxy::Get(profile)->AppService()->RegisterPublisher(
-      std::move(publisher), apps::mojom::AppType::kArc);
+  apps::AppServiceProxyFactory::GetForProfile(profile)
+      ->AppService()
+      ->RegisterPublisher(std::move(publisher), apps::mojom::AppType::kArc);
 }
 
 ArcApps::~ArcApps() {
diff --git a/chrome/browser/apps/app_service/crostini_apps.cc b/chrome/browser/apps/app_service/crostini_apps.cc
index 9e189ff0..d555b47 100644
--- a/chrome/browser/apps/app_service/crostini_apps.cc
+++ b/chrome/browser/apps/app_service/crostini_apps.cc
@@ -6,7 +6,6 @@
 
 #include <utility>
 
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/dip_px_util.h"
 #include "chrome/browser/apps/app_service/launch_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h"
diff --git a/chrome/browser/apps/intent_helper/OWNERS b/chrome/browser/apps/intent_helper/OWNERS
index 8db6e277..1e68db6 100644
--- a/chrome/browser/apps/intent_helper/OWNERS
+++ b/chrome/browser/apps/intent_helper/OWNERS
@@ -1,3 +1,5 @@
+dominickn@chromium.org
+mxcai@chromium.org
 # For ARC related code
 djacobo@chromium.org
 # For ARC related code, backup reviewers
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 8985978..675d8fa4 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -71,6 +71,7 @@
     "//chrome/common",
     "//chrome/common/extensions/api",
     "//chrome/services/app_service:lib",
+    "//chrome/services/app_service/public/cpp:app_service_proxy",
     "//chrome/services/app_service/public/cpp:app_update",
     "//chrome/services/diagnosticsd/public/mojom",
     "//chrome/services/file_util/public/cpp",
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc
index bdd5a0b..81399f0 100644
--- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc
+++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc
@@ -12,6 +12,7 @@
 #include "ash/shell.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h"
+#include "components/arc/arc_util.h"
 #include "components/arc/metrics/arc_metrics_constants.h"
 #include "ui/aura/env.h"
 #include "ui/base/ui_base_features.h"
@@ -82,7 +83,7 @@
 
 bool ArcKioskAppLauncher::CheckAndPinWindow(aura::Window* const window) {
   DCHECK_GE(task_id_, 0);
-  if (ArcAppWindowLauncherController::GetWindowTaskId(window) != task_id_)
+  if (arc::GetWindowTaskId(window) != task_id_)
     return false;
   // Stop observing as target window is already found.
   StopObserving();
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
index 1f03a38a2..f844390 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
@@ -18,6 +18,7 @@
 #include "chromeos/constants/chromeos_features.h"
 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
 #include "components/arc/arc_service_manager.h"
+#include "components/arc/arc_util.h"
 #include "components/arc/session/arc_bridge_service.h"
 #include "components/exo/input_method_surface.h"
 #include "components/exo/shell_surface.h"
@@ -36,8 +37,6 @@
 
 namespace {
 
-constexpr int32_t kNoTaskId = -1;
-
 exo::Surface* GetArcSurface(const aura::Window* window) {
   if (!window)
     return nullptr;
@@ -48,18 +47,6 @@
   return arc_surface;
 }
 
-int32_t GetTaskId(aura::Window* window) {
-  const std::string* arc_app_id = exo::GetShellApplicationId(window);
-  if (!arc_app_id)
-    return kNoTaskId;
-
-  int32_t task_id = kNoTaskId;
-  if (sscanf(arc_app_id->c_str(), "org.chromium.arc.%d", &task_id) != 1)
-    return kNoTaskId;
-
-  return task_id;
-}
-
 void DispatchFocusChange(arc::mojom::AccessibilityNodeInfoData* node_data,
                          Profile* profile) {
   chromeos::AccessibilityManager* accessibility_manager =
@@ -168,7 +155,7 @@
   aura::Window* window = GetActiveWindow();
   if (!window)
     return;
-  int32_t task_id = GetTaskId(window);
+  int32_t task_id = arc::GetWindowTaskId(window);
   if (task_id == kNoTaskId)
     return;
 
@@ -194,7 +181,7 @@
     return;
 
   aura::Window* window = window_tracker->Pop();
-  int32_t task_id = GetTaskId(window);
+  int32_t task_id = arc::GetWindowTaskId(window);
   DCHECK_NE(task_id, kNoTaskId);
 
   if (!enabled) {
@@ -315,7 +302,7 @@
       if (!active_window)
         return;
 
-      int32_t task_id = GetTaskId(active_window);
+      int32_t task_id = arc::GetWindowTaskId(active_window);
       if (task_id != event_data->task_id)
         return;
 
@@ -678,7 +665,7 @@
 
   // First, do a lookup for the task id associated with this app. There should
   // always be a valid entry.
-  int32_t task_id = GetTaskId(window);
+  int32_t task_id = arc::GetWindowTaskId(window);
 
   // Do a lookup for the tree source. A tree source may not exist because the
   // app isn't whitelisted Android side or no data has been received for the
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_graphics_model.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_graphics_model.cc
index 58ea13f..4375f338 100644
--- a/chrome/browser/chromeos/arc/tracing/arc_tracing_graphics_model.cc
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_graphics_model.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/chromeos/arc/tracing/arc_tracing_event.h"
 #include "chrome/browser/chromeos/arc/tracing/arc_tracing_event_matcher.h"
 #include "chrome/browser/chromeos/arc/tracing/arc_tracing_model.h"
+#include "components/arc/arc_util.h"
 
 namespace arc {
 
@@ -351,9 +352,8 @@
         LOG(ERROR) << "Failed to get app id from event: " << event->ToString();
         continue;
       }
-      int task_id = -1;
-      if (sscanf(app_id.c_str(), "org.chromium.arc.%d", &task_id) != 1 ||
-          task_id < 0) {
+      int task_id = GetTaskIdFromWindowAppId(app_id);
+      if (task_id == kNoTaskId) {
         LOG(ERROR) << "Failed to parse app id from event: "
                    << event->ToString();
         continue;
diff --git a/chrome/browser/chromeos/assistant/assistant_util.cc b/chrome/browser/chromeos/assistant/assistant_util.cc
index dda3c07..2b5934d6 100644
--- a/chrome/browser/chromeos/assistant/assistant_util.cc
+++ b/chrome/browser/chromeos/assistant/assistant_util.cc
@@ -51,7 +51,7 @@
                                          ULOC_US,
                                          "da_DK",
                                          "nl_NL",
-                                         "no_NO"
+                                         "no_NO",
                                          "sv_SE"};
 
   const PrefService* prefs = profile->GetPrefs();
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service.cc b/chrome/browser/chromeos/crostini/crostini_registry_service.cc
index 47dd733..9c6f9ec 100644
--- a/chrome/browser/chromeos/crostini/crostini_registry_service.cc
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
-#include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/dbus/vm_applications/apps.pb.h"
@@ -35,8 +34,7 @@
 
 namespace {
 
-// Prefixes of the ApplicationId set on exo windows.
-constexpr char kArcWindowAppIdPrefix[] = "org.chromium.arc";
+// Prefix of the ApplicationId set on exo windows for X apps.
 constexpr char kCrostiniWindowAppIdPrefix[] = "org.chromium.termina.";
 // This comes after kCrostiniWindowAppIdPrefix
 constexpr char kWMClassPrefix[] = "wmclass.";
@@ -487,8 +485,8 @@
 }
 
 // The code follows these steps to identify apps and returns the first match:
-// 1) Ignore windows if the App Id is prefixed by org.chromium.arc.
-// 2) If the Startup Id is set, look for a matching desktop file id.
+// 1) If the Startup Id is set, look for a matching desktop file id.
+// 2) Ignore windows if the App Id is not set.
 // 3) If the App Id is not prefixed by org.chromium.termina., it's an app with
 // native Wayland support. Look for a matching desktop file id.
 // 4) If the App Id is prefixed by org.chromium.termina.wmclass.:
@@ -516,13 +514,8 @@
     // Try a lookup with the window app id.
   }
 
-  // TODO(timloh): Crostini shouldn't need to know about Arc and PluginVm.
-  if (!window_app_id ||
-      base::StartsWith(*window_app_id, kArcWindowAppIdPrefix,
-                       base::CompareCase::SENSITIVE) ||
-      plugin_vm::IsPluginVmExoApplicationId(*window_app_id)) {
+  if (!window_app_id)
     return std::string();
-  }
 
   // Wayland apps won't be prefixed with org.chromium.termina.
   if (!base::StartsWith(*window_app_id, kCrostiniWindowAppIdPrefix,
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service.h b/chrome/browser/chromeos/crostini/crostini_registry_service.h
index 0b7ec83d..ba24934 100644
--- a/chrome/browser/chromeos/crostini/crostini_registry_service.h
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service.h
@@ -139,10 +139,9 @@
   //
   // First try to return a desktop file id matching the |window_startup_id|.
   //
-  // If the given window app id is not for Crostini (i.e. Arc++), returns an
-  // empty string. If we can uniquely identify a registry entry, returns the
-  // crostini app id for that. Otherwise, returns the string pointed to by
-  // |window_app_id|, prefixed by "crostini:".
+  // If the app id is empty, returns empty string. If we can uniquely identify
+  // a registry entry, returns the crostini app id for that. Otherwise, returns
+  // the string pointed to by |window_app_id|, prefixed by "crostini:".
   //
   // As the window app id is derived from fields set by the app itself, it is
   // possible for an app to masquerade as a different app.
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc b/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc
index a6b1cbb..dab07e7 100644
--- a/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc
@@ -357,10 +357,6 @@
   window_app_id = "fancy.app";
   EXPECT_EQ(service()->GetCrostiniShelfAppId(&window_app_id, nullptr),
             "crostini:fancy.app");
-
-  window_app_id = "org.chromium.arc.h";
-  EXPECT_EQ(service()->GetCrostiniShelfAppId(&window_app_id, nullptr),
-            std::string());
 }
 
 TEST_F(CrostiniRegistryServiceTest, GetCrostiniAppIdStartupWMClass) {
diff --git a/chrome/browser/chromeos/input_method/input_method_engine.cc b/chrome/browser/chromeos/input_method/input_method_engine.cc
index f084489..41f5434 100644
--- a/chrome/browser/chromeos/input_method/input_method_engine.cc
+++ b/chrome/browser/chromeos/input_method/input_method_engine.cc
@@ -72,8 +72,8 @@
     ime::mojom::ImeEngineFactoryPtr factory_ptr;
     factory_binding_.Close();
     factory_binding_.Bind(mojo::MakeRequest(&factory_ptr));
-    factory_binding_.set_connection_error_handler(
-        base::BindOnce(&MojoHelper::OnConnectionLost, base::Unretained(this)));
+    factory_binding_.set_connection_error_handler(base::BindOnce(
+        &MojoHelper::OnFactoryConnectionLost, base::Unretained(this)));
 
     if (registry) {
       registry_ = std::move(registry);
@@ -86,12 +86,16 @@
     registry_->ActivateFactory(std::move(factory_ptr));
   }
 
+  void set_allow_finish_input(bool allow) { allow_finish_input_ = allow; }
+
   // ime::mojom::ImeEngineFactory overrides:
   void CreateEngine(ime::mojom::ImeEngineRequest engine_request,
                     ime::mojom::ImeEngineClientPtr client) override {
     engine_binding_.Close();
     engine_binding_.Bind(std::move(engine_request));
     engine_client_ = std::move(client);
+    engine_client_.set_connection_error_handler(base::BindOnce(
+        &MojoHelper::OnClientConnectionLost, base::Unretained(this)));
   }
 
   // ime::mojom::ImeEngine overrides:
@@ -100,8 +104,17 @@
         info->type, info->mode, info->flags, info->focus_reason,
         info->should_do_learning);
     engine_->FocusIn(context);
+    allow_finish_input_ = true;
   }
-  void FinishInput() override { engine_->FocusOut(); }
+  void FinishInput() override {
+    // Only allows the call of FocusOut() when the FocusIn() was caused from a
+    // mojo-based client. Please see the comments for |allow_finish_input_| for
+    // the details.
+    if (allow_finish_input_) {
+      engine_->FocusOut();
+      allow_finish_input_ = false;
+    }
+  }
   void CancelInput() override { engine_->Reset(); }
   void ProcessKeyEvent(
       std::unique_ptr<ui::Event> key_event,
@@ -132,13 +145,15 @@
   }
 
  private:
-  void OnConnectionLost() {
+  void OnFactoryConnectionLost() {
     // After the connection to |ImeEngineFactoryRegistry| is broken, notifies
     // the client to reconnect through Window Service.
     if (engine_client_)
       engine_client_->Reconnect();
   }
 
+  void OnClientConnectionLost() { engine_client_.reset(); }
+
   InputMethodEngine* engine_;
   mojo::Binding<ime::mojom::ImeEngineFactory> factory_binding_;
   mojo::Binding<ime::mojom::ImeEngine> engine_binding_;
@@ -146,6 +161,14 @@
   ime::mojom::ImeEngineClientPtr engine_client_;
   ime::mojom::ImeEngineFactoryRegistryPtr registry_;
 
+  // Whether mutes the call of FinishInput().
+  // This is to guard the mis-ordered calls of FocusIn() & FocusOut() calls when
+  // switching between mojo-based and non-mojo-based clients.
+  // e.g. app_list window is non-mojo-based client, so need to guard the
+  // FocusOut() call from the mojo-based client because the app_list window's
+  // FocusIn() comes in first.
+  bool allow_finish_input_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(MojoHelper);
 };
 
@@ -185,6 +208,12 @@
   return !active_component_id_.empty();
 }
 
+void InputMethodEngine::FocusIn(
+    const ui::IMEEngineHandlerInterface::InputContext& input_context) {
+  InputMethodEngineBase::FocusIn(input_context);
+  mojo_helper_->set_allow_finish_input(false);
+}
+
 void InputMethodEngine::PropertyActivate(const std::string& property_name) {
   observer_->OnMenuItemActivated(active_component_id_, property_name);
 }
diff --git a/chrome/browser/chromeos/input_method/input_method_engine.h b/chrome/browser/chromeos/input_method/input_method_engine.h
index 710f8adc..5a8d0e2 100644
--- a/chrome/browser/chromeos/input_method/input_method_engine.h
+++ b/chrome/browser/chromeos/input_method/input_method_engine.h
@@ -92,6 +92,8 @@
   // InputMethodEngineBase overrides.
   void Enable(const std::string& component_id) override;
   bool IsActive() const override;
+  void FocusIn(const ui::IMEEngineHandlerInterface::InputContext& input_context)
+      override;
 
   // ui::IMEEngineHandlerInterface overrides.
   void PropertyActivate(const std::string& property_name) override;
diff --git a/chrome/browser/chromeos/input_method/input_method_engine_unittest.cc b/chrome/browser/chromeos/input_method/input_method_engine_unittest.cc
index 93088e8..66abd87 100644
--- a/chrome/browser/chromeos/input_method/input_method_engine_unittest.cc
+++ b/chrome/browser/chromeos/input_method/input_method_engine_unittest.cc
@@ -424,6 +424,22 @@
   engine_->CommitText(context, "input", &error);
   engine_->FlushForTesting();
   EXPECT_TRUE(client.commit_text_called());
+
+  engine_ptr->FinishInput();
+  engine_ptr.FlushForTesting();
+  EXPECT_EQ(ONBLUR, observer_->GetCallsBitmapAndReset());
+
+  // Switches from a mojo-based client to a non-mojo-based client.
+  engine_ptr->StartInput(ime::mojom::EditorInfo::New(
+      ui::TEXT_INPUT_TYPE_TEXT, ui::TEXT_INPUT_MODE_DEFAULT,
+      ui::TEXT_INPUT_FLAG_NONE, ui::TextInputClient::FOCUS_REASON_MOUSE,
+      false));
+  engine_ptr.FlushForTesting();
+  engine_ptr->FinishInput();
+  FocusIn(ui::TEXT_INPUT_TYPE_TEXT);
+  engine_ptr.FlushForTesting();
+  // Verifies no ONBLUR is called.
+  EXPECT_EQ(ONFOCUS, observer_->GetCallsBitmapAndReset());
 }
 
 }  // namespace input_method
diff --git a/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc b/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc
index 75c972a..f109680b 100644
--- a/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc
+++ b/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc
@@ -16,12 +16,13 @@
 #include "base/optional.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/apps/app_service/app_icon_source.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/chromeos/kiosk_next_home/app_controller_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/common/extensions/extension_constants.h"
+#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "chrome/services/app_service/public/mojom/types.mojom.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "components/arc/arc_service_manager.h"
@@ -43,7 +44,7 @@
 
 AppControllerService::AppControllerService(Profile* profile)
     : profile_(profile),
-      app_service_proxy_(apps::AppServiceProxy::Get(profile)) {
+      app_service_proxy_(apps::AppServiceProxyFactory::GetForProfile(profile)) {
   DCHECK(profile);
   app_service_proxy_->AppRegistryCache().AddObserver(this);
 
diff --git a/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc b/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc
index 9b8ee94..4c7e40e1 100644
--- a/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc
+++ b/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc
@@ -13,12 +13,12 @@
 #include "base/command_line.h"
 #include "base/optional.h"
 #include "base/test/bind_test_util.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/services/app_service/public/cpp/app_registry_cache.h"
+#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "chrome/services/app_service/public/cpp/app_update.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/constants/chromeos_switches.h"
@@ -67,7 +67,7 @@
     profile_ = std::make_unique<TestingProfile>();
 
     arc_test_.SetUp(profile());
-    proxy_ = apps::AppServiceProxy::Get(profile());
+    proxy_ = apps::AppServiceProxyFactory::GetForProfile(profile());
 
     app_controller_service_ = AppControllerService::Get(profile());
 
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_util.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_util.cc
index eda3d192..c89b2de 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_util.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_util.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/exo/shell_surface_util.h"
 #include "components/prefs/pref_service.h"
 
 namespace plugin_vm {
@@ -66,8 +67,9 @@
   return IsPluginVmAllowedForProfile(profile) && IsPluginVmConfigured(profile);
 }
 
-// TODO(timloh): Implement this (crbug.com/940319).
-bool IsPluginVmExoApplicationId(const std::string& app_id) {
+// TODO(timloh): Implement detection for Plugin VM windows, e.g. by setting an
+// exo application id (crbug.com/940319).
+bool IsPluginVmWindow(const aura::Window* window) {
   return false;
 }
 
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_util.h b/chrome/browser/chromeos/plugin_vm/plugin_vm_util.h
index dbecf5d..b041e33 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_util.h
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_util.h
@@ -7,6 +7,10 @@
 
 #include <string>
 
+namespace aura {
+class Window;
+}  // namespace aura
+
 class Profile;
 
 namespace plugin_vm {
@@ -28,8 +32,8 @@
 
 void ShowPluginVmLauncherView(Profile* profile);
 
-// Checks if an exo window's app id is for plugin vm.
-bool IsPluginVmExoApplicationId(const std::string& app_id);
+// Checks if an window is for plugin vm.
+bool IsPluginVmWindow(const aura::Window* window);
 
 // Retrieves the license key to be used for PluginVm. If
 // none is set this will return an empty string.
diff --git a/chrome/browser/devtools/inspector_protocol_config.json b/chrome/browser/devtools/inspector_protocol_config.json
index 6e98d251..2137e85 100644
--- a/chrome/browser/devtools/inspector_protocol_config.json
+++ b/chrome/browser/devtools/inspector_protocol_config.json
@@ -8,8 +8,9 @@
         "options": [
             {
                 "domain": "Page",
-                "include": [ "enable", "disable", "setAdBlockingEnabled" ],
-                "include_events": []
+                "include": [ "enable", "disable", "setAdBlockingEnabled", "getInstallabilityErrors" ],
+                "include_events": [],
+                "async": ["getInstallabilityErrors"]
             },
             {
                 "domain": "Browser",
diff --git a/chrome/browser/devtools/protocol/page_handler.cc b/chrome/browser/devtools/protocol/page_handler.cc
index 1020609c..eea7f9c 100644
--- a/chrome/browser/devtools/protocol/page_handler.cc
+++ b/chrome/browser/devtools/protocol/page_handler.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/devtools/protocol/page_handler.h"
 
+#include "chrome/browser/installable/installable_manager.h"
 #include "chrome/browser/subresource_filter/chrome_subresource_filter_client.h"
 
 PageHandler::PageHandler(content::WebContents* web_contents,
@@ -47,3 +48,28 @@
   ToggleAdBlocking(enabled);
   return protocol::Response::OK();
 }
+
+void PageHandler::GetInstallabilityErrors(
+    std::unique_ptr<GetInstallabilityErrorsCallback> callback) {
+  auto errors = protocol::Array<std::string>::create();
+  InstallableManager* manager =
+      web_contents() ? InstallableManager::FromWebContents(web_contents())
+                     : nullptr;
+  if (!manager) {
+    callback->sendFailure(
+        protocol::Response::Error("Unable to fetch errors for target"));
+    return;
+  }
+  manager->GetAllErrors(base::BindOnce(&PageHandler::GotInstallabilityErrors,
+                                       std::move(callback)));
+}
+
+// static
+void PageHandler::GotInstallabilityErrors(
+    std::unique_ptr<GetInstallabilityErrorsCallback> callback,
+    std::vector<std::string> errors) {
+  auto result = protocol::Array<std::string>::create();
+  for (const auto& error : errors)
+    result->addItem(error);
+  callback->sendSuccess(std::move(result));
+}
diff --git a/chrome/browser/devtools/protocol/page_handler.h b/chrome/browser/devtools/protocol/page_handler.h
index bc0d62cc..4225193 100644
--- a/chrome/browser/devtools/protocol/page_handler.h
+++ b/chrome/browser/devtools/protocol/page_handler.h
@@ -26,8 +26,14 @@
   protocol::Response Enable() override;
   protocol::Response Disable() override;
   protocol::Response SetAdBlockingEnabled(bool enabled) override;
+  void GetInstallabilityErrors(
+      std::unique_ptr<GetInstallabilityErrorsCallback> callback) override;
 
  private:
+  static void GotInstallabilityErrors(
+      std::unique_ptr<GetInstallabilityErrorsCallback> callback,
+      std::vector<std::string> errors);
+
   bool enabled_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(PageHandler);
diff --git a/chrome/browser/extensions/api/cast_streaming/performance_test.cc b/chrome/browser/extensions/api/cast_streaming/performance_test.cc
index 03a5a0e5..8d3d46c 100644
--- a/chrome/browser/extensions/api/cast_streaming/performance_test.cc
+++ b/chrome/browser/extensions/api/cast_streaming/performance_test.cc
@@ -67,13 +67,13 @@
 // long enough to collect sufficient tracing data; and, unfortunately, there's
 // nothing we can do about that.
 #define EXPECT_FOR_PERFORMANCE_RUN(expr)             \
-  if (!(expr)) {                                     \
+  do {                                               \
     if (is_full_performance_run()) {                 \
-      LOG(ERROR) << "Failure: " << #expr;            \
-    } else {                                         \
+      EXPECT_TRUE(expr);                             \
+    } else if (!(expr)) {                            \
       LOG(WARNING) << "Allowing failure: " << #expr; \
     }                                                \
-  }
+  } while (false)
 
 enum TestFlags {
   kSmallWindow = 1 << 2,      // Window size: 1 = 800x600, 0 = 2000x1000
@@ -168,7 +168,7 @@
              const std::string& modifier,
              const std::string& trace,
              const std::string& unit) {
-    if (num_values_ > 0) {
+    if (num_values_ >= 20) {
       perf_test::PrintResultMeanAndError(measurement,
                                          modifier,
                                          trace,
@@ -176,7 +176,8 @@
                                          unit,
                                          true);
     } else {
-      LOG(ERROR) << "No events for " << measurement << modifier << " " << trace;
+      LOG(ERROR) << "Not enough events (" << num_values_ << ") for "
+                 << measurement << modifier << " " << trace;
     }
   }
 
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc
index fe7bf28a2..7b455d2 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc
@@ -22,7 +22,6 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/tracing.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
@@ -33,7 +32,6 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
-#include "services/service_manager/sandbox/features.h"
 #include "third_party/zlib/google/compression_utils.h"
 #include "ui/gl/gl_switches.h"
 
@@ -49,15 +47,6 @@
   // Because screen capture is involved, require pixel output.
   EnablePixelOutput();
 
-  feature_list_.InitWithFeatures(
-      {
-          service_manager::features::kAudioServiceSandbox,
-          features::kAudioServiceAudioStreams,
-          features::kAudioServiceLaunchOnStartup,
-          features::kAudioServiceOutOfProcess,
-      },
-      {});
-
   InProcessBrowserTest::SetUp();
 }
 
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.h b/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.h
index 8a1c7146..51d0fd4 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.h
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.h
@@ -11,7 +11,6 @@
 
 #include "base/macros.h"
 #include "base/strings/string_piece.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/trace_event_analyzer.h"
 #include "chrome/test/base/in_process_browser_test.h"
 
@@ -136,9 +135,6 @@
 
   const extensions::Extension* extension_ = nullptr;
 
-  // Manages the Audio Service feature set, enabled for these performance tests.
-  base::test::ScopedFeatureList feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(TabCapturePerformanceTestBase);
 };
 
diff --git a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
index ab4b26e..895fdee 100644
--- a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
@@ -2073,10 +2073,12 @@
     deltas.push_back(std::move(d0));
   }
   bool request_headers_modified0;
+  std::set<std::string> ignore1, ignore2;
   net::HttpRequestHeaders headers0;
   headers0.MergeFrom(base_headers);
   MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers0, &ignored_actions,
-                                    &logger, &request_headers_modified0);
+                                    &logger, &ignore1, &ignore2,
+                                    &request_headers_modified0);
   ASSERT_TRUE(headers0.GetHeader("key1", &header_value));
   EXPECT_EQ("value 1", header_value);
   ASSERT_TRUE(headers0.GetHeader("key2", &header_value));
@@ -2096,11 +2098,14 @@
   deltas.sort(&InDecreasingExtensionInstallationTimeOrder);
   ignored_actions.clear();
   logger.clear();
+  ignore1.clear();
+  ignore2.clear();
   bool request_headers_modified1;
   net::HttpRequestHeaders headers1;
   headers1.MergeFrom(base_headers);
   MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers1, &ignored_actions,
-                                    &logger, &request_headers_modified1);
+                                    &logger, &ignore1, &ignore2,
+                                    &request_headers_modified1);
   EXPECT_FALSE(headers1.HasHeader("key1"));
   ASSERT_TRUE(headers1.GetHeader("key2", &header_value));
   EXPECT_EQ("value 3", header_value);
@@ -2122,11 +2127,14 @@
   deltas.sort(&InDecreasingExtensionInstallationTimeOrder);
   ignored_actions.clear();
   logger.clear();
+  ignore1.clear();
+  ignore2.clear();
   bool request_headers_modified2;
   net::HttpRequestHeaders headers2;
   headers2.MergeFrom(base_headers);
   MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers2, &ignored_actions,
-                                    &logger, &request_headers_modified2);
+                                    &logger, &ignore1, &ignore2,
+                                    &request_headers_modified2);
   EXPECT_FALSE(headers2.HasHeader("key1"));
   ASSERT_TRUE(headers2.GetHeader("key2", &header_value));
   EXPECT_EQ("value 3", header_value);
@@ -2152,11 +2160,14 @@
   deltas.sort(&InDecreasingExtensionInstallationTimeOrder);
   ignored_actions.clear();
   logger.clear();
+  ignore1.clear();
+  ignore2.clear();
   bool request_headers_modified3;
   net::HttpRequestHeaders headers3;
   headers3.MergeFrom(base_headers);
   MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers3, &ignored_actions,
-                                    &logger, &request_headers_modified3);
+                                    &logger, &ignore1, &ignore2,
+                                    &request_headers_modified3);
   EXPECT_FALSE(headers3.HasHeader("key1"));
   ASSERT_TRUE(headers3.GetHeader("key2", &header_value));
   EXPECT_EQ("value 3", header_value);
@@ -2218,11 +2229,13 @@
   }
   deltas.sort(&InDecreasingExtensionInstallationTimeOrder);
   bool request_headers_modified1;
+  std::set<std::string> ignore1, ignore2;
   net::HttpRequestHeaders headers1;
   headers1.MergeFrom(base_headers);
   ignored_actions.clear();
   MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers1, &ignored_actions,
-                                    &logger, &request_headers_modified1);
+                                    &logger, &ignore1, &ignore2,
+                                    &request_headers_modified1);
   EXPECT_TRUE(headers1.HasHeader("Cookie"));
   ASSERT_TRUE(headers1.GetHeader("Cookie", &header_value));
   EXPECT_EQ("name=new value; name2=new value; name4=\"value 4\"", header_value);
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 68846d3..da7b75b 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2306,6 +2306,11 @@
     "expiry_milestone": 80
   },
   {
+    "name": "mojo-imf",
+    "owners": [ "shuchen" ],
+    "expiry_milestone": 78
+  },
+  {
     "name": "native-filesystem-api",
     "owners": [ "mek", "pwnall" ],
     "expiry_milestone":  80
@@ -3069,11 +3074,6 @@
     "expiry_milestone": 76
   },
   {
-    "name": "virtual-keyboard-overscroll",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "wake-on-wifi-packet",
     "owners": [ "abhishekbh", "chirantan" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 4e00821..4669071b 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3320,6 +3320,12 @@
     "Runs the system UI (ash) as a mojo service, but inside the browser "
     "process. The browser uses the mojo window service (ws) APIs.";
 
+const char kMojoImfName[] = "Mojo-based IMF to bridge the client and IME";
+const char kMojoImfDescription[] =
+    "Makes the system UI (ash) as the bridge between the client and the IME via"
+    " mojo APIs. This can only take effect if the flag \"single-process-masn\" "
+    "is enabled.";
+
 const char kSmartTextSelectionName[] = "Smart Text Selection";
 const char kSmartTextSelectionDescription[] =
     "Shows quick actions for text "
@@ -3395,10 +3401,6 @@
 const char kVirtualKeyboardName[] = "Virtual Keyboard";
 const char kVirtualKeyboardDescription[] = "Enable virtual keyboard support.";
 
-const char kVirtualKeyboardOverscrollName[] = "Virtual Keyboard Overscroll";
-const char kVirtualKeyboardOverscrollDescription[] =
-    "Enables virtual keyboard overscroll support.";
-
 const char kWakeOnPacketsName[] = "Wake On Packets";
 const char kWakeOnPacketsDescription[] =
     "Enables waking the device based on the receipt of some network packets.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 777d698..38d717f 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1974,6 +1974,9 @@
 extern const char kAggressiveTabDiscardThresholds[];
 extern const char kAggressiveThresholds[];
 
+extern const char kMojoImfName[];
+extern const char kMojoImfDescription[];
+
 extern const char kMtpWriteSupportName[];
 extern const char kMtpWriteSupportDescription[];
 
@@ -2055,9 +2058,6 @@
 extern const char kVirtualKeyboardName[];
 extern const char kVirtualKeyboardDescription[];
 
-extern const char kVirtualKeyboardOverscrollName[];
-extern const char kVirtualKeyboardOverscrollDescription[];
-
 extern const char kWakeOnPacketsName[];
 extern const char kWakeOnPacketsDescription[];
 
diff --git a/chrome/browser/lookalikes/lookalike_url_interstitial_page.h b/chrome/browser/lookalikes/lookalike_url_interstitial_page.h
index b6c928f..c98b603e 100644
--- a/chrome/browser/lookalikes/lookalike_url_interstitial_page.h
+++ b/chrome/browser/lookalikes/lookalike_url_interstitial_page.h
@@ -31,10 +31,11 @@
     kTopSite = 1,
     kSiteEngagement = 2,
     kEditDistance = 3,
+    kEditDistanceSiteEngagement = 4,
 
     // Append new items to the end of the list above; do not modify or replace
     // existing values. Comment out obsolete items.
-    kMaxValue = kEditDistance,
+    kMaxValue = kEditDistanceSiteEngagement,
   };
 
   // Used for UKM. There is only a single UserAction per navigation.
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
index 51274e1..72567b38 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
@@ -90,6 +90,67 @@
   return std::string();
 }
 
+// Returns the first matching top domain with an edit distance of at most one
+// to |domain_and_registry|.
+std::string GetSimilarDomainFromTop500(const DomainInfo& navigated_domain) {
+  for (const std::string& navigated_skeleton : navigated_domain.skeletons) {
+    for (const char* const top_domain_skeleton : top500_domains::kTop500) {
+      if (lookalikes::IsEditDistanceAtMostOne(
+              base::UTF8ToUTF16(navigated_skeleton),
+              base::UTF8ToUTF16(top_domain_skeleton))) {
+        const std::string top_domain =
+            url_formatter::LookupSkeletonInTopDomains(top_domain_skeleton);
+        DCHECK(!top_domain.empty());
+        // If the only difference between the navigated and top
+        // domains is the registry part, this is unlikely to be a spoofing
+        // attempt. Ignore this match and continue. E.g. If the navigated domain
+        // is google.com.tw and the top domain is google.com.tr, this won't
+        // produce a match.
+        const std::string top_domain_without_registry =
+            url_formatter::top_domains::HostnameWithoutRegistry(top_domain);
+        DCHECK(url_formatter::top_domains::IsEditDistanceCandidate(
+            top_domain_without_registry));
+        if (navigated_domain.domain_without_registry !=
+            top_domain_without_registry) {
+          return top_domain;
+        }
+      }
+    }
+  }
+  return std::string();
+}
+
+// Returns the first matching engaged domain with an edit distance of at most
+// one to |domain_and_registry|.
+std::string GetSimilarDomainFromEngagedSites(
+    const DomainInfo& navigated_domain,
+    const std::vector<DomainInfo>& engaged_sites) {
+  for (const std::string& navigated_skeleton : navigated_domain.skeletons) {
+    for (const DomainInfo& engaged_site : engaged_sites) {
+      if (!url_formatter::top_domains::IsEditDistanceCandidate(
+              engaged_site.domain_and_registry)) {
+        continue;
+      }
+      for (const std::string& engaged_skeleton : engaged_site.skeletons) {
+        if (lookalikes::IsEditDistanceAtMostOne(
+                base::UTF8ToUTF16(navigated_skeleton),
+                base::UTF8ToUTF16(engaged_skeleton))) {
+          // If the only difference between the navigated and engaged
+          // domain is the registry part, this is unlikely to be a spoofing
+          // attempt. Ignore this match and continue. E.g. If the navigated
+          // domain is google.com.tw and the top domain is google.com.tr, this
+          // won't produce a match.
+          if (navigated_domain.domain_without_registry !=
+              engaged_site.domain_without_registry) {
+            return engaged_site.domain_and_registry;
+          }
+        }
+      }
+    }
+  }
+  return std::string();
+}
+
 }  // namespace
 
 namespace lookalikes {
@@ -98,6 +159,47 @@
 const char LookalikeUrlNavigationThrottle::kHistogramName[] =
     "NavigationSuggestion.Event";
 
+bool IsEditDistanceAtMostOne(const base::string16& str1,
+                             const base::string16& str2) {
+  if (str1.size() > str2.size() + 1 || str2.size() > str1.size() + 1) {
+    return false;
+  }
+  base::string16::const_iterator i = str1.begin();
+  base::string16::const_iterator j = str2.begin();
+  size_t edit_count = 0;
+  while (i != str1.end() && j != str2.end()) {
+    if (*i == *j) {
+      i++;
+      j++;
+    } else {
+      edit_count++;
+      if (edit_count > 1) {
+        return false;
+      }
+
+      if (str1.size() > str2.size()) {
+        // First string is longer than the second. This can only happen if the
+        // first string has an extra character.
+        i++;
+      } else if (str2.size() > str1.size()) {
+        // Second string is longer than the first. This can only happen if the
+        // second string has an extra character.
+        j++;
+      } else {
+        // Both strings are the same length. This can only happen if the two
+        // strings differ by a single character.
+        i++;
+        j++;
+      }
+    }
+  }
+  if (i != str1.end() || j != str2.end()) {
+    // A character at the end did not match.
+    edit_count++;
+  }
+  return edit_count <= 1;
+}
+
 LookalikeUrlNavigationThrottle::LookalikeUrlNavigationThrottle(
     content::NavigationHandle* navigation_handle)
     : content::NavigationThrottle(navigation_handle),
@@ -204,8 +306,8 @@
     const GURL& url,
     const DomainInfo& navigated_domain,
     const std::vector<DomainInfo>& engaged_sites) {
-  ThrottleCheckResult result = LookalikeUrlNavigationThrottle::PerformChecks(
-      url, navigated_domain, engaged_sites);
+  ThrottleCheckResult result =
+      PerformChecks(url, navigated_domain, engaged_sites);
 
   if (!interstitials_enabled_) {
     return;
@@ -273,7 +375,7 @@
   ukm::SourceId source_id = ukm::ConvertToSourceId(
       navigation_handle()->GetNavigationId(), ukm::SourceIdType::NAVIGATION_ID);
 
-  if (interstitials_enabled_ && match_type != MatchType::kEditDistance) {
+  if (ShouldDisplayInterstitial(match_type)) {
     return ShowInterstitial(suggested_url, url, source_id, match_type);
   }
 
@@ -284,12 +386,20 @@
   return content::NavigationThrottle::PROCEED;
 }
 
+bool LookalikeUrlNavigationThrottle::ShouldDisplayInterstitial(
+    MatchType match_type) const {
+  return interstitials_enabled_ && match_type != MatchType::kEditDistance &&
+         match_type != MatchType::kEditDistanceSiteEngagement;
+}
+
 bool LookalikeUrlNavigationThrottle::GetMatchingDomain(
     const DomainInfo& navigated_domain,
     const std::vector<DomainInfo>& engaged_sites,
     std::string* matched_domain,
     MatchType* match_type) {
   DCHECK(!navigated_domain.domain_and_registry.empty());
+  DCHECK(matched_domain);
+  DCHECK(match_type);
 
   if (navigated_domain.idn_result.has_idn_component) {
     // If the navigated domain is IDN, check its skeleton against engaged sites
@@ -318,95 +428,34 @@
     }
   }
 
-  // If we can't find an exact top domain or an engaged site, try to find a top
-  // domain within an edit distance of one.
-  const std::string similar_domain =
+  if (!url_formatter::top_domains::IsEditDistanceCandidate(
+          navigated_domain.domain_and_registry)) {
+    return false;
+  }
+
+  // If we can't find an exact top domain or an engaged site, try to find an
+  // engaged domain within an edit distance of one.
+  const std::string similar_engaged_domain =
+      GetSimilarDomainFromEngagedSites(navigated_domain, engaged_sites);
+  if (!similar_engaged_domain.empty() &&
+      navigated_domain.domain_and_registry != similar_engaged_domain) {
+    RecordEvent(NavigationSuggestionEvent::kMatchEditDistanceSiteEngagement);
+    *matched_domain = similar_engaged_domain;
+    *match_type = MatchType::kEditDistanceSiteEngagement;
+    return true;
+  }
+
+  // Finally, try to find a top domain within an edit distance of one.
+  const std::string similar_top_domain =
       GetSimilarDomainFromTop500(navigated_domain);
-  if (!similar_domain.empty() &&
-      navigated_domain.domain_and_registry != similar_domain) {
+  if (!similar_top_domain.empty() &&
+      navigated_domain.domain_and_registry != similar_top_domain) {
     RecordEvent(NavigationSuggestionEvent::kMatchEditDistance);
-    *matched_domain = similar_domain;
+    *matched_domain = similar_top_domain;
     *match_type = MatchType::kEditDistance;
     return true;
   }
   return false;
 }
 
-// static
-bool LookalikeUrlNavigationThrottle::IsEditDistanceAtMostOne(
-    const base::string16& str1,
-    const base::string16& str2) {
-  if (str1.size() > str2.size() + 1 || str2.size() > str1.size() + 1) {
-    return false;
-  }
-  base::string16::const_iterator i = str1.begin();
-  base::string16::const_iterator j = str2.begin();
-  size_t edit_count = 0;
-  while (i != str1.end() && j != str2.end()) {
-    if (*i == *j) {
-      i++;
-      j++;
-    } else {
-      edit_count++;
-      if (edit_count > 1) {
-        return false;
-      }
-
-      if (str1.size() > str2.size()) {
-        // First string is longer than the second. This can only happen if the
-        // first string has an extra character.
-        i++;
-      } else if (str2.size() > str1.size()) {
-        // Second string is longer than the first. This can only happen if the
-        // second string has an extra character.
-        j++;
-      } else {
-        // Both strings are the same length. This can only happen if the two
-        // strings differ by a single character.
-        i++;
-        j++;
-      }
-    }
-  }
-  if (i != str1.end() || j != str2.end()) {
-    // A character at the end did not match.
-    edit_count++;
-  }
-  return edit_count <= 1;
-}
-
-// static
-std::string LookalikeUrlNavigationThrottle::GetSimilarDomainFromTop500(
-    const DomainInfo& navigated_domain) {
-  if (!url_formatter::top_domains::IsEditDistanceCandidate(
-          navigated_domain.domain_and_registry)) {
-    return std::string();
-  }
-  const std::string domain_without_registry =
-      url_formatter::top_domains::HostnameWithoutRegistry(
-          navigated_domain.domain_and_registry);
-
-  for (const std::string& navigated_skeleton : navigated_domain.skeletons) {
-    for (const char* const top_domain_skeleton : top500_domains::kTop500) {
-      if (IsEditDistanceAtMostOne(base::UTF8ToUTF16(navigated_skeleton),
-                                  base::UTF8ToUTF16(top_domain_skeleton))) {
-        const std::string top_domain =
-            url_formatter::LookupSkeletonInTopDomains(top_domain_skeleton);
-        DCHECK(!top_domain.empty());
-        // If the only difference between the navigated and top
-        // domains is the registry part, this is unlikely to be a spoofing
-        // attempt. Ignore this match and continue. E.g. If the navigated domain
-        // is google.com.tw and the top domain is google.com.tr, this won't
-        // produce a match.
-        const std::string top_domain_without_registry =
-            url_formatter::top_domains::HostnameWithoutRegistry(top_domain);
-        if (domain_without_registry != top_domain_without_registry) {
-          return top_domain;
-        }
-      }
-    }
-  }
-  return std::string();
-}
-
 }  // namespace lookalikes
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.h b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.h
index 6b283a5d..743ff61 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.h
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.h
@@ -26,6 +26,12 @@
 
 struct DomainInfo;
 
+// Returns true if the Levenshtein distance between |str1| and |str2| is at most
+// one. This has O(max(n,m)) complexity as opposed to O(n*m) of the usual edit
+// distance computation.
+bool IsEditDistanceAtMostOne(const base::string16& str1,
+                             const base::string16& str2);
+
 // Observes navigations and shows an interstitial if the navigated domain name
 // is visually similar to a top domain or a domain with a site engagement score.
 class LookalikeUrlNavigationThrottle : public content::NavigationThrottle {
@@ -39,13 +45,13 @@
     kMatchTopSite = 3,
     kMatchSiteEngagement = 4,
     kMatchEditDistance = 5,
+    kMatchEditDistanceSiteEngagement = 6,
 
     // Append new items to the end of the list above; do not modify or
     // replace existing values. Comment out obsolete items.
-    kMaxValue = kMatchEditDistance,
+    kMaxValue = kMatchEditDistanceSiteEngagement,
   };
 
-
   static const char kHistogramName[];
 
   explicit LookalikeUrlNavigationThrottle(content::NavigationHandle* handle);
@@ -77,6 +83,9 @@
                              const DomainInfo& navigated_domain,
                              const std::vector<DomainInfo>& engaged_sites);
 
+  bool ShouldDisplayInterstitial(
+      LookalikeUrlInterstitialPage::MatchType match_type) const;
+
   // Returns true if a domain is visually similar to the hostname of |url|. The
   // matching domain can be a top domain or an engaged site. Similarity check
   // is made using both visual skeleton and edit distance comparison. If this
@@ -87,16 +96,6 @@
                          std::string* matched_domain,
                          LookalikeUrlInterstitialPage::MatchType* match_type);
 
-  // Returns if the Levenshtein distance between |str1| and |str2| is at most 1.
-  // This has O(max(n,m)) complexity as opposed to O(n*m) of the usual edit
-  // distance computation.
-  static bool IsEditDistanceAtMostOne(const base::string16& str1,
-                                      const base::string16& str2);
-
-  // Returns the first matching top domain with an edit distance of at most one
-  // to |domain_and_registry|.
-  static std::string GetSimilarDomainFromTop500(const DomainInfo& domain_info);
-
   ThrottleCheckResult ShowInterstitial(
       const GURL& safe_domain,
       const GURL& url,
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
index 8dfac5ca..922d6704 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
@@ -472,6 +472,34 @@
   CheckNoUkm();
 }
 
+// Navigate to a domain within an edit distance of 1 to an engaged domain.
+// This should record metrics, but should not show a lookalike warning
+// interstitial yet.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       EditDistance_EngagedDomain_Match) {
+  base::HistogramTester histograms;
+  SetEngagementScore(browser(), GURL("https://test-site.com"), kHighEngagement);
+
+  // The skeleton of this domain is one 1 edit away from the skeleton of
+  // test-site.com.
+  const GURL kNavigatedUrl = GetURL("best-sité.com");
+  // Even if the navigated site has a low engagement score, it should be
+  // considered for lookalike suggestions.
+  SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
+  // Advance clock to force a fetch of new engaged sites list.
+  test_clock()->Advance(base::TimeDelta::FromHours(1));
+
+  TestInterstitialNotShown(browser(), kNavigatedUrl);
+  histograms.ExpectTotalCount(LookalikeUrlNavigationThrottle::kHistogramName,
+                              1);
+  histograms.ExpectBucketCount(
+      LookalikeUrlNavigationThrottle::kHistogramName,
+      NavigationSuggestionEvent::kMatchEditDistanceSiteEngagement, 1);
+
+  CheckUkm({kNavigatedUrl}, "MatchType",
+           MatchType::kEditDistanceSiteEngagement);
+}
+
 // Navigate to a domain within an edit distance of 1 to a top domain.
 // This should record metrics, but should not show a lookalike warning
 // interstitial yet.
@@ -512,6 +540,29 @@
   CheckNoUkm();
 }
 
+// Tests negative examples for the edit distance with engaged sites.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       EditDistance_SiteEngagement_NoMatch) {
+  SetEngagementScore(browser(), GURL("https://test-site.com.tr"),
+                     kHighEngagement);
+  SetEngagementScore(browser(), GURL("https://1234.com"), kHighEngagement);
+  SetEngagementScore(browser(), GURL("https://gooogle.com"), kHighEngagement);
+  // Advance clock to force a fetch of new engaged sites list.
+  test_clock()->Advance(base::TimeDelta::FromHours(1));
+
+  // Matches test-site.com.tr but only differs in registry.
+  TestInterstitialNotShown(browser(), GetURL("test-site.com.tw"));
+  CheckNoUkm();
+
+  // Matches gooogle.com but is a top domain itself.
+  TestInterstitialNotShown(browser(), GetURL("google.com"));
+  CheckNoUkm();
+
+  // Matches 1234.com but is too short.
+  TestInterstitialNotShown(browser(), GetURL("123.com"));
+  CheckNoUkm();
+}
+
 // Test that the heuristics are triggered even with net errors.
 IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
                        NetError_Interstitial) {
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc
index 48f6f56..d63b1471 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc
@@ -61,9 +61,9 @@
       {L"google.com", L"goooglé.com", false},
   };
   for (const TestCase& test_case : kTestCases) {
-    bool result = LookalikeUrlNavigationThrottle::IsEditDistanceAtMostOne(
-        base::WideToUTF16(test_case.domain),
-        base::WideToUTF16(test_case.top_domain));
+    bool result =
+        IsEditDistanceAtMostOne(base::WideToUTF16(test_case.domain),
+                                base::WideToUTF16(test_case.top_domain));
     EXPECT_EQ(test_case.expected, result);
   }
 }
diff --git a/chrome/browser/lookalikes/lookalike_url_service.cc b/chrome/browser/lookalikes/lookalike_url_service.cc
index d34f4ed..ec88525 100644
--- a/chrome/browser/lookalikes/lookalike_url_service.cc
+++ b/chrome/browser/lookalikes/lookalike_url_service.cc
@@ -21,6 +21,7 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "components/url_formatter/top_domains/top_domain_util.h"
 #include "components/url_formatter/url_formatter.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 
@@ -78,9 +79,11 @@
 }
 
 DomainInfo::DomainInfo(const std::string& arg_domain_and_registry,
+                       const std::string& arg_domain_without_registry,
                        const url_formatter::IDNConversionResult& arg_idn_result,
                        const url_formatter::Skeletons& arg_skeletons)
     : domain_and_registry(arg_domain_and_registry),
+      domain_without_registry(arg_domain_without_registry),
       idn_result(arg_idn_result),
       skeletons(arg_skeletons) {}
 
@@ -91,10 +94,16 @@
 DomainInfo GetDomainInfo(const GURL& url) {
   // Perform all computations on eTLD+1.
   const std::string domain_and_registry = GetETLDPlusOne(url.host());
+  const std::string domain_without_registry =
+      domain_and_registry.empty()
+          ? std::string()
+          : url_formatter::top_domains::HostnameWithoutRegistry(
+                domain_and_registry);
 
   // eTLD+1 can be empty for private domains.
   if (domain_and_registry.empty()) {
-    return DomainInfo(domain_and_registry, url_formatter::IDNConversionResult(),
+    return DomainInfo(domain_and_registry, domain_without_registry,
+                      url_formatter::IDNConversionResult(),
                       url_formatter::Skeletons());
   }
   // Compute skeletons using eTLD+1, skipping all spoofing checks. Spoofing
@@ -105,7 +114,8 @@
       url_formatter::UnsafeIDNToUnicodeWithDetails(domain_and_registry);
   const url_formatter::Skeletons skeletons =
       url_formatter::GetSkeletons(idn_result.result);
-  return DomainInfo(domain_and_registry, idn_result, skeletons);
+  return DomainInfo(domain_and_registry, domain_without_registry, idn_result,
+                    skeletons);
 }
 
 LookalikeUrlService::LookalikeUrlService(Profile* profile)
diff --git a/chrome/browser/lookalikes/lookalike_url_service.h b/chrome/browser/lookalikes/lookalike_url_service.h
index 4caf2f8..d17ea5b 100644
--- a/chrome/browser/lookalikes/lookalike_url_service.h
+++ b/chrome/browser/lookalikes/lookalike_url_service.h
@@ -34,13 +34,19 @@
 
 struct DomainInfo {
   // eTLD+1, used for skeleton and edit distance comparison. Must be ASCII.
+  // Can be empty.
   const std::string domain_and_registry;
+  // eTLD+1 without the registry part. For "www.google.com", this will be
+  // "google". Used for edit distance comparisons. Can be empty.
+  const std::string domain_without_registry;
+
   // Result of IDN conversion of domain_and_registry field.
   const url_formatter::IDNConversionResult idn_result;
   // Skeletons of domain_and_registry field.
   const url_formatter::Skeletons skeletons;
 
   DomainInfo(const std::string& arg_domain_and_registry,
+             const std::string& arg_domain_without_registry,
              const url_formatter::IDNConversionResult& arg_idn_result,
              const url_formatter::Skeletons& arg_skeletons);
   ~DomainInfo();
diff --git a/chrome/browser/media/webrtc/desktop_media_list_ash.cc b/chrome/browser/media/webrtc/desktop_media_list_ash.cc
index dc7e0606..fe498e184a 100644
--- a/chrome/browser/media/webrtc/desktop_media_list_ash.cc
+++ b/chrome/browser/media/webrtc/desktop_media_list_ash.cc
@@ -108,12 +108,15 @@
 
       CaptureThumbnail(screen_source.id, root_windows[i]);
     } else {
-      EnumerateWindowsForRoot(
-          sources, root_windows[i], ash::kShellWindowId_DefaultContainer);
-      EnumerateWindowsForRoot(
-          sources, root_windows[i], ash::kShellWindowId_AlwaysOnTopContainer);
-      EnumerateWindowsForRoot(
-        sources, root_windows[i], ash::kShellWindowId_PipContainer);
+      constexpr int kContainersIds[] = {
+          ash::kShellWindowId_DefaultContainerDeprecated,
+          // TODO(afakhry): Add rest of desks containers.
+          ash::kShellWindowId_AlwaysOnTopContainer,
+          ash::kShellWindowId_PipContainer,
+      };
+
+      for (int id : kContainersIds)
+        EnumerateWindowsForRoot(sources, root_windows[i], id);
     }
   }
 }
diff --git a/chrome/browser/net/chrome_extensions_network_delegate.cc b/chrome/browser/net/chrome_extensions_network_delegate.cc
index 1181d56..feee2e8 100644
--- a/chrome/browser/net/chrome_extensions_network_delegate.cc
+++ b/chrome/browser/net/chrome_extensions_network_delegate.cc
@@ -197,13 +197,22 @@
   return result;
 }
 
+namespace {
+void OnHeadersReceivedAdapter(net::CompletionOnceCallback callback,
+                              const std::set<std::string>& removed_headers,
+                              const std::set<std::string>& set_headers,
+                              int error_code) {
+  std::move(callback).Run(error_code);
+}
+}  // namespace
+
 int ChromeExtensionsNetworkDelegateImpl::OnBeforeStartTransaction(
     net::URLRequest* request,
     net::CompletionOnceCallback callback,
     net::HttpRequestHeaders* headers) {
   return ExtensionWebRequestEventRouter::GetInstance()->OnBeforeSendHeaders(
       profile_, extension_info_map_.get(), GetWebRequestInfo(request),
-      std::move(callback), headers);
+      base::BindOnce(OnHeadersReceivedAdapter, std::move(callback)), headers);
 }
 
 void ChromeExtensionsNetworkDelegateImpl::OnStartTransaction(
diff --git a/chrome/browser/notifications/scheduler/BUILD.gn b/chrome/browser/notifications/scheduler/BUILD.gn
index a3af336..195b07d 100644
--- a/chrome/browser/notifications/scheduler/BUILD.gn
+++ b/chrome/browser/notifications/scheduler/BUILD.gn
@@ -73,6 +73,8 @@
     "icon_store.h",
     "impression_history_tracker.cc",
     "impression_history_tracker.h",
+    "impression_store.cc",
+    "impression_store.h",
     "impression_types.cc",
     "impression_types.h",
     "internal_types.h",
@@ -112,6 +114,7 @@
     "distribution_policy_unittest.cc",
     "icon_store_unittest.cc",
     "impression_history_tracker_unittest.cc",
+    "impression_store_unittest.cc",
     "proto_conversion_unittest.cc",
     "scheduler_utils_unittest.cc",
   ]
diff --git a/chrome/browser/notifications/scheduler/impression_store.cc b/chrome/browser/notifications/scheduler/impression_store.cc
new file mode 100644
index 0000000..8b79c62
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/impression_store.cc
@@ -0,0 +1,100 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/notifications/scheduler/impression_store.h"
+
+#include "chrome/browser/notifications/scheduler/proto_conversion.h"
+
+namespace leveldb_proto {
+
+void DataToProto(notifications::ClientState* client_state,
+                 notifications::proto::ClientState* proto) {
+  ClientStateToProto(client_state, proto);
+}
+
+void ProtoToData(notifications::proto::ClientState* proto,
+                 notifications::ClientState* client_state) {
+  ClientStateFromProto(proto, client_state);
+}
+
+}  // namespace leveldb_proto
+
+namespace notifications {
+
+ImpressionStore::ImpressionStore(
+    std::unique_ptr<
+        leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>> db)
+    : db_(std::move(db)), weak_ptr_factory_(this) {}
+
+ImpressionStore::~ImpressionStore() = default;
+
+void ImpressionStore::InitAndLoad(LoadCallback callback) {
+  db_->Init(base::BindOnce(&ImpressionStore::OnDbInitialized,
+                           weak_ptr_factory_.GetWeakPtr(),
+                           std::move(callback)));
+}
+
+void ImpressionStore::OnDbInitialized(LoadCallback callback,
+                                      leveldb_proto::Enums::InitStatus status) {
+  if (status != leveldb_proto::Enums::InitStatus::kOK) {
+    std::move(callback).Run(false, Entries());
+    return;
+  }
+
+  // Load the data after a successful initialization.
+  db_->LoadEntries(base::BindOnce(&ImpressionStore::OnDataLoaded,
+                                  weak_ptr_factory_.GetWeakPtr(),
+                                  std::move(callback)));
+}
+
+void ImpressionStore::OnDataLoaded(LoadCallback callback,
+                                   bool success,
+                                   std::unique_ptr<EntryVector> entry_vector) {
+  // The database failed to load.
+  if (!success) {
+    std::move(callback).Run(false, Entries());
+    return;
+  }
+
+  // Success to load but no data.
+  if (!entry_vector) {
+    std::move(callback).Run(true, Entries());
+    return;
+  }
+
+  // Load data.
+  Entries entries;
+  for (auto it = entry_vector->begin(); it != entry_vector->end(); ++it) {
+    std::unique_ptr<ClientState> client_state = std::make_unique<ClientState>();
+    *client_state = std::move(*it);
+    entries.emplace_back(std::move(client_state));
+  }
+
+  std::move(callback).Run(true, std::move(entries));
+}
+
+void ImpressionStore::Add(const std::string& key,
+                          const ClientState& client_state,
+                          UpdateCallback callback) {
+  Update(key, client_state, std::move(callback));
+}
+
+void ImpressionStore::Update(const std::string& key,
+                             const ClientState& client_state,
+                             UpdateCallback callback) {
+  auto entries_to_save = std::make_unique<KeyEntryVector>();
+  entries_to_save->emplace_back(std::make_pair(key, client_state));
+  db_->UpdateEntries(std::move(entries_to_save),
+                     std::make_unique<KeyVector>() /*keys_to_remove*/,
+                     std::move(callback));
+}
+
+void ImpressionStore::Delete(const std::string& key, UpdateCallback callback) {
+  auto keys_to_delete = std::make_unique<KeyVector>();
+  keys_to_delete->emplace_back(key);
+  db_->UpdateEntries(std::make_unique<KeyEntryVector>() /*entries_to_save*/,
+                     std::move(keys_to_delete), std::move(callback));
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_store.h b/chrome/browser/notifications/scheduler/impression_store.h
new file mode 100644
index 0000000..3749720a
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/impression_store.h
@@ -0,0 +1,73 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_STORE_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_STORE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/notifications/proto/client_state.pb.h"
+#include "chrome/browser/notifications/scheduler/collection_store.h"
+#include "chrome/browser/notifications/scheduler/impression_types.h"
+#include "components/leveldb_proto/public/proto_database.h"
+
+// Forward declaration for proto conversion.
+namespace leveldb_proto {
+void DataToProto(notifications::ClientState* client_state,
+                 notifications::proto::ClientState* proto);
+
+void ProtoToData(notifications::proto::ClientState* proto,
+                 notifications::ClientState* client_state);
+}  // namespace leveldb_proto
+
+namespace notifications {
+
+// An impression storage using a proto database to persist data.
+class ImpressionStore : public CollectionStore<ClientState> {
+ public:
+  ImpressionStore(
+      std::unique_ptr<
+          leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>> db);
+  ~ImpressionStore() override;
+
+ private:
+  using KeyEntryVector = std::vector<std::pair<std::string, ClientState>>;
+  using KeyVector = std::vector<std::string>;
+  using EntryVector = std::vector<ClientState>;
+
+  // CollectionStore implementation.
+  void InitAndLoad(LoadCallback callback) override;
+  void Add(const std::string& key,
+           const ClientState& client_state,
+           UpdateCallback callback) override;
+  void Update(const std::string& key,
+              const ClientState& client_state,
+              UpdateCallback callback) override;
+  void Delete(const std::string& key, UpdateCallback callback) override;
+
+  // Called when the proto database is initialized but no yet loading the data
+  // into memory.
+  void OnDbInitialized(LoadCallback callback,
+                       leveldb_proto::Enums::InitStatus status);
+
+  // Called after loading the data from database.
+  void OnDataLoaded(LoadCallback callback,
+                    bool success,
+                    std::unique_ptr<EntryVector> entry_vector);
+
+  std::unique_ptr<leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>>
+      db_;
+  base::WeakPtrFactory<ImpressionStore> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ImpressionStore);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/impression_store_unittest.cc b/chrome/browser/notifications/scheduler/impression_store_unittest.cc
new file mode 100644
index 0000000..4872afe
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/impression_store_unittest.cc
@@ -0,0 +1,207 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/notifications/scheduler/impression_store.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/test/scoped_task_environment.h"
+#include "chrome/browser/notifications/proto/client_state.pb.h"
+#include "chrome/browser/notifications/scheduler/impression_types.h"
+#include "chrome/browser/notifications/scheduler/proto_conversion.h"
+#include "components/leveldb_proto/public/proto_database.h"
+#include "components/leveldb_proto/testing/fake_db.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using leveldb_proto::test::FakeDB;
+using InitStatus = leveldb_proto::Enums::InitStatus;
+using Entries = notifications::ImpressionStore::Entries;
+using DbEntries = std::vector<notifications::ClientState>;
+using DbEntriesPtr = std::unique_ptr<std::vector<notifications::ClientState>>;
+using TestClientStates = std::map<std::string, notifications::ClientState>;
+
+namespace notifications {
+namespace {
+
+const char kClientStateKey[] = "guid_client_state_key1";
+const ClientState kDefaultClientState;
+
+// Test fixture to verify impression store.
+class ImpressionStoreTest : public testing::Test {
+ public:
+  ImpressionStoreTest() : load_result_(false), db_(nullptr) {}
+  ~ImpressionStoreTest() override = default;
+
+  void SetUp() override {}
+
+ protected:
+  // Initialize the store with test data.
+  void Init(const TestClientStates& test_data, InitStatus status) {
+    CreateTestProto(test_data);
+
+    auto db =
+        std::make_unique<FakeDB<proto::ClientState, ClientState>>(&db_entries_);
+    db_ = db.get();
+    store_ = std::make_unique<ImpressionStore>(std::move(db));
+    store_->InitAndLoad(base::BindOnce(&ImpressionStoreTest::OnEntriesLoaded,
+                                       base::Unretained(this)));
+    db_->InitStatusCallback(status);
+  }
+
+  bool load_result() const { return load_result_; }
+  const Entries& loaded_entries() const { return loaded_entries_; }
+  FakeDB<proto::ClientState, ClientState>* db() { return db_; }
+
+  CollectionStore<ClientState>* store() { return store_.get(); }
+
+  void OnEntriesLoaded(bool success, Entries entries) {
+    loaded_entries_ = std::move(entries);
+    load_result_ = success;
+  }
+
+  // Verifies the entries in the db is |expected|.
+  void VerifyDataInDb(DbEntriesPtr expected) {
+    db_->LoadEntries(base::BindOnce(
+        [](DbEntriesPtr expected, bool success, DbEntriesPtr entries) {
+          EXPECT_TRUE(success);
+          DCHECK(entries);
+          DCHECK(expected);
+          EXPECT_EQ(entries->size(), expected->size());
+          for (size_t i = 0, size = entries->size(); i < size; ++i) {
+            EXPECT_EQ((*entries)[i], (*expected)[i]);
+          }
+        },
+        std::move(expected)));
+    db_->LoadCallback(true);
+  }
+
+ private:
+  void CreateTestProto(const TestClientStates& client_states) {
+    for (const auto& pair : client_states) {
+      auto client_state(pair.second);
+      auto key = pair.first;
+      notifications::proto::ClientState proto;
+      ClientStateToProto(&client_state, &proto);
+      db_entries_.emplace(key, proto);
+    }
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  std::map<std::string, proto::ClientState> db_entries_;
+  bool load_result_;
+  Entries loaded_entries_;
+
+  FakeDB<proto::ClientState, ClientState>* db_;
+  std::unique_ptr<CollectionStore<ClientState>> store_;
+
+  DISALLOW_COPY_AND_ASSIGN(ImpressionStoreTest);
+};
+
+// Initializes an empty database should success.
+TEST_F(ImpressionStoreTest, InitSuccessEmptyDb) {
+  Init(TestClientStates(), InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+  EXPECT_TRUE(loaded_entries().empty());
+}
+
+// Initialize non-empty database should success.
+TEST_F(ImpressionStoreTest, InitSuccessWithData) {
+  auto test_data = TestClientStates();
+  test_data.emplace(kClientStateKey, kDefaultClientState);
+  Init(test_data, InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+  EXPECT_EQ(loaded_entries().size(), 1u);
+  EXPECT_EQ(*loaded_entries().back(), kDefaultClientState);
+}
+
+// Failure when loading the data will result in error.
+TEST_F(ImpressionStoreTest, InitSuccessLoadFailed) {
+  Init(TestClientStates(), InitStatus::kOK);
+  db()->LoadCallback(false);
+  EXPECT_EQ(load_result(), false);
+  EXPECT_TRUE(loaded_entries().empty());
+}
+
+// Failed database initialization will result in error.
+TEST_F(ImpressionStoreTest, InitFailed) {
+  Init(TestClientStates(), InitStatus::kCorrupt);
+  EXPECT_EQ(load_result(), false);
+  EXPECT_TRUE(loaded_entries().empty());
+}
+
+// Verifies adding data.
+TEST_F(ImpressionStoreTest, Add) {
+  Init(TestClientStates(), InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+  EXPECT_TRUE(loaded_entries().empty());
+
+  // Add data to the store.
+  store()->Add(kClientStateKey, kDefaultClientState,
+               base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+  db()->UpdateCallback(true);
+
+  // Verify the new data is in the database.
+  auto expected = std::make_unique<DbEntries>();
+  expected->emplace_back(kDefaultClientState);
+  VerifyDataInDb(std::move(expected));
+}
+
+// Verifies failure when adding data will result in error.
+TEST_F(ImpressionStoreTest, AddFailed) {
+  Init(TestClientStates(), InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+  EXPECT_TRUE(loaded_entries().empty());
+
+  store()->Add(kClientStateKey, kDefaultClientState,
+               base::BindOnce([](bool success) { EXPECT_FALSE(success); }));
+  db()->UpdateCallback(false);
+}
+
+// Verifies updating data.
+TEST_F(ImpressionStoreTest, Update) {
+  auto test_data = TestClientStates();
+  test_data.emplace(kClientStateKey, kDefaultClientState);
+  Init(test_data, InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+
+  ClientState new_client_state;
+  new_client_state.current_max_daily_show = 100;
+
+  // Update the database.
+  store()->Update(kClientStateKey, new_client_state,
+                  base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+  db()->UpdateCallback(true);
+
+  // Verify the updated data is in the database.
+  auto expected = std::make_unique<DbEntries>();
+  expected->emplace_back(new_client_state);
+  VerifyDataInDb(std::move(expected));
+}
+
+// Verifies deleting data.
+TEST_F(ImpressionStoreTest, Delete) {
+  auto test_data = TestClientStates();
+  test_data.emplace(kClientStateKey, kDefaultClientState);
+  Init(test_data, InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+
+  // Delete the entry.
+  store()->Delete(kClientStateKey,
+                  base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+
+  // Verify there is no data in the database.
+  VerifyDataInDb(std::make_unique<DbEntries>());
+}
+
+}  // namespace
+}  // namespace notifications
diff --git a/chrome/browser/resource_coordinator/tab_ranker/tab_score_predictor.cc b/chrome/browser/resource_coordinator/tab_ranker/tab_score_predictor.cc
index 94084a4c..54394a2 100644
--- a/chrome/browser/resource_coordinator/tab_ranker/tab_score_predictor.cc
+++ b/chrome/browser/resource_coordinator/tab_ranker/tab_score_predictor.cc
@@ -26,10 +26,21 @@
 using resource_coordinator::GetMRUScorerPenaltyTabRanker;
 using resource_coordinator::GetScorerTypeForTabRanker;
 
-// ReverseRank maps a positive number to the range (0.0, 1.0).
-inline float ReverseRank(const float a) {
-  DCHECK_GE(a, 0.0f);
-  return 1.0f / (1.0f + a);
+// Maps the |mru_index| to it's reverse rank in (0.0, 1.0).
+// High score means more likely to be reactivated.
+// We use inverse rank because we think that the first several |mru_index| is
+// more significant than the larger ones.
+inline float MruToScore(const float mru_index) {
+  DCHECK_GE(mru_index, 0.0f);
+  return 1.0f / (1.0f + mru_index);
+}
+
+// Maps the |discard_count| to a score in (0.0, 1.0), for which
+// High score means more likely to be reactivated.
+// We use std::exp because we think that the first several |discard_count| is
+// not as significant as the larger ones.
+inline float DiscardCountToScore(const float discard_count) {
+  return std::exp(discard_count);
 }
 
 // Loads the preprocessor config protobuf, which lists each feature, their
@@ -83,8 +94,8 @@
   // The default value of discard_count_penalty_ is 0.0f, which will not change
   // the score.
   // The larger the |discard_count_penalty_| is (set from Finch), the quicker
-  // the score decreases based on the discard_count.
-  *score *= ReverseRank(tab.discard_count * discard_count_penalty_);
+  // the score increases based on the discard_count.
+  *score *= DiscardCountToScore(tab.discard_count * discard_count_penalty_);
 
   return result;
 }
@@ -142,7 +153,7 @@
 
 TabRankerResult TabScorePredictor::ScoreTabWithMRUScorer(const TabFeatures& tab,
                                                          float* score) {
-  *score = ReverseRank(tab.mru_index * mru_scorer_penalty_);
+  *score = MruToScore(tab.mru_index * mru_scorer_penalty_);
   return TabRankerResult::kSuccess;
 }
 
diff --git a/chrome/browser/resource_coordinator/tab_ranker/tab_score_predictor_unittest.cc b/chrome/browser/resource_coordinator/tab_ranker/tab_score_predictor_unittest.cc
index d883647..ac22f52 100644
--- a/chrome/browser/resource_coordinator/tab_ranker/tab_score_predictor_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_ranker/tab_score_predictor_unittest.cc
@@ -65,13 +65,13 @@
   scoped_feature_list_.InitAndEnableFeatureWithParameters(
       features::kTabRanker, {{"scorer_type", "0"},
                              {"mru_scorer_penalty", "0.2345"},
-                             {"discard_count_penalty", "0.5678"}});
+                             {"discard_count_penalty", "0.2468"}});
   // Pre-calculated score using the generated model outside of Chrome.
   auto tab = GetFullTabFeaturesForTesting();
   EXPECT_FLOAT_EQ(ScoreTab(tab), 0.13639774);
 
   tab.discard_count = 3;
-  EXPECT_FLOAT_EQ(ScoreTab(tab), 0.05045415);
+  EXPECT_FLOAT_EQ(ScoreTab(tab), 0.28599524);
 }
 
 }  // namespace tab_ranker
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
index 54527b7e..3f16cc7 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
@@ -676,10 +676,12 @@
             true);
       } else {
         var root = ChromeVoxState.instance.currentRange.start.node.root;
-        if (root && root.anchorObject && root.focusObject) {
+        if (root && root.selectionStartObject && root.selectionEndObject) {
           var sel = new cursors.Range(
-              new cursors.Cursor(root.anchorObject, root.anchorOffset),
-              new cursors.Cursor(root.focusObject, root.focusOffset));
+              new cursors.Cursor(
+                  root.selectionStartObject, root.selectionStartOffset),
+              new cursors.Cursor(
+                  root.selectionEndObject, root.selectionEndOffset));
           var o = new Output()
                       .format('@end_selection')
                       .withSpeechAndBraille(sel, sel, Output.EventType.NAVIGATE)
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
index 9a84912..97d21f8 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
@@ -344,18 +344,18 @@
         new cursors.Cursor(p2.firstChild, 4));
 
     function verifySel() {
-      if (root.anchorObject == link.firstChild) {
-        assertEquals(link.firstChild, root.anchorObject);
-        assertEquals(0, root.anchorOffset);
-        assertEquals(link.firstChild, root.focusObject);
-        assertEquals(1, root.focusOffset);
+      if (root.selectionStartObject == link.firstChild) {
+        assertEquals(link.firstChild, root.selectionStartObject);
+        assertEquals(0, root.selectionStartOffset);
+        assertEquals(link.firstChild, root.selectionEndObject);
+        assertEquals(1, root.selectionEndOffset);
         this.listenOnce(root, 'textSelectionChanged', verifySel);
         multiSel.select();
-      } else if (root.anchorObject == p1.firstChild) {
-        assertEquals(p1.firstChild, root.anchorObject);
-        assertEquals(2, root.anchorOffset);
-        assertEquals(p2.firstChild, root.focusObject);
-        assertEquals(4, root.focusOffset);
+      } else if (root.selectionStartObject == p1.firstChild) {
+        assertEquals(p1.firstChild, root.selectionStartObject);
+        assertEquals(2, root.selectionStartOffset);
+        assertEquals(p2.firstChild, root.selectionEndObject);
+        assertEquals(4, root.selectionEndOffset);
       }
     }
 
@@ -400,14 +400,14 @@
   */}, function(root) {
     root.addEventListener('textSelectionChanged', this.newCallback(function(evt) {
       // Test setup moves initial focus; ensure we don't test that here.
-      if (testNode != root.anchorObject)
+      if (testNode != root.selectionStartObject)
         return;
 
       // This is a little unexpected though not really incorrect; Ctrl+C works.
-      assertEquals(testNode, root.anchorObject);
-      assertEquals(ofSelectionNode, root.focusObject);
-      assertEquals(4, root.anchorOffset);
-      assertEquals(1, root.focusOffset);
+      assertEquals(testNode, root.selectionStartObject);
+      assertEquals(ofSelectionNode, root.selectionEndObject);
+      assertEquals(4, root.selectionStartOffset);
+      assertEquals(1, root.selectionEndOffset);
     }));
 
     // This is the link's static text.
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js
index cd92f93..20e843e41a 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js
@@ -372,10 +372,10 @@
    * @param {!AutomationEvent} evt
    */
   onDocumentSelectionChanged: function(evt) {
-    var anchor = evt.target.anchorObject;
+    var selectionStart = evt.target.selectionStartObject;
 
     // No selection.
-    if (!anchor)
+    if (!selectionStart)
       return;
 
     // A caller requested this event be ignored.
@@ -384,10 +384,11 @@
       return;
 
     // Editable selection.
-    if (anchor.state[StateType.EDITABLE]) {
-      anchor = AutomationUtil.getEditableRoot(anchor) || anchor;
+    if (selectionStart.state[StateType.EDITABLE]) {
+      selectionStart =
+          AutomationUtil.getEditableRoot(selectionStart) || selectionStart;
       this.onEditableChanged_(
-          new CustomAutomationEvent(evt.type, anchor, evt.eventFrom));
+          new CustomAutomationEvent(evt.type, selectionStart, evt.eventFrom));
     }
 
     // Non-editable selections are handled in |Background|.
@@ -536,14 +537,15 @@
     }
 
     // Sync the ChromeVox range to the editable, if a selection exists.
-    var anchorObject = evt.target.root.anchorObject;
-    var anchorOffset = evt.target.root.anchorOffset || 0;
-    var focusObject = evt.target.root.focusObject;
-    var focusOffset = evt.target.root.focusOffset || 0;
-    if (anchorObject && focusObject) {
+    var selectionStartObject = evt.target.root.selectionStartObject;
+    var selectionStartOffset = evt.target.root.selectionStartOffset || 0;
+    var selectionEndObject = evt.target.root.selectionEndObject;
+    var selectionEndOffset = evt.target.root.selectionEndOffset || 0;
+    if (selectionStartObject && selectionEndObject) {
       var selectedRange = new cursors.Range(
-          new cursors.WrappingCursor(anchorObject, anchorOffset),
-          new cursors.WrappingCursor(focusObject, focusOffset));
+          new cursors.WrappingCursor(
+              selectionStartObject, selectionStartOffset),
+          new cursors.WrappingCursor(selectionEndObject, selectionEndOffset));
 
       // Sync ChromeVox range with selection.
       ChromeVoxState.instance.setCurrentRange(selectedRange);
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
index cab980a..ada6cbc 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
@@ -265,18 +265,20 @@
   AutomationEditableText.call(this, node);
 
   var root = this.node_.root;
-  if (!root || !root.anchorObject || !root.focusObject ||
-      root.anchorOffset === undefined || root.focusOffset === undefined)
+  if (!root || !root.selectionStartObject || !root.selectionEndObject ||
+      root.selectionStartOffset === undefined ||
+      root.selectionEndOffset === undefined)
     return;
 
   this.anchorLine_ = new editing.EditableLine(
-      root.anchorObject, root.anchorOffset, root.anchorObject,
-      root.anchorOffset);
+      root.selectionStartObject, root.selectionStartOffset,
+      root.selectionStartObject, root.selectionStartOffset);
   this.focusLine_ = new editing.EditableLine(
       root.focusObject, root.focusOffset, root.focusObject, root.focusOffset);
 
   this.line_ = new editing.EditableLine(
-      root.anchorObject, root.anchorOffset, root.focusObject, root.focusOffset);
+      root.selectionStartObject, root.selectionStartOffset,
+      root.selectionEndObject, root.selectionEndOffset);
 
   this.updateIntraLineState_(this.line_);
 
@@ -358,13 +360,14 @@
   /** @override */
   onUpdate: function(eventFrom) {
     var root = this.node_.root;
-    if (!root.anchorObject || !root.focusObject ||
-        root.anchorOffset === undefined || root.focusOffset === undefined)
+    if (!root.selectionStartObject || !root.selectionEndObject ||
+        root.selectionStartOffset === undefined ||
+        root.selectionEndOffset === undefined)
       return;
 
     var anchorLine = new editing.EditableLine(
-        root.anchorObject, root.anchorOffset, root.anchorObject,
-        root.anchorOffset);
+        root.selectionStartObject, root.selectionStartOffset,
+        root.selectionStartObject, root.selectionStartOffset);
     var focusLine = new editing.EditableLine(
         root.focusObject, root.focusOffset, root.focusObject, root.focusOffset);
 
@@ -386,8 +389,8 @@
       return;
     } else {
       cur = new editing.EditableLine(
-          root.anchorObject, root.anchorOffset, root.focusObject,
-          root.focusOffset, baseLineOnStart);
+          root.selectionStartObject, root.selectionStartOffset,
+          root.selectionEndObject, root.selectionEndOffset, baseLineOnStart);
     }
     var prev = this.line_;
     this.line_ = cur;
diff --git a/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js b/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js
index 3428953e5..37d71be 100644
--- a/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js
+++ b/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js
@@ -184,20 +184,21 @@
    */
   requestSpeakSelectedText_: function(focusedNode) {
     // If nothing is selected, return early.
-    if (!focusedNode || !focusedNode.root || !focusedNode.root.anchorObject ||
-        !focusedNode.root.focusObject) {
+    if (!focusedNode || !focusedNode.root ||
+        !focusedNode.root.selectionStartObject ||
+        !focusedNode.root.selectionEndObject) {
       this.onNullSelection_();
       return;
     }
-    let anchorObject = focusedNode.root.anchorObject;
-    let anchorOffset = focusedNode.root.anchorOffset || 0;
-    let focusObject = focusedNode.root.focusObject;
-    let focusOffset = focusedNode.root.focusOffset || 0;
+    let anchorObject = focusedNode.root.selectionStartObject;
+    let anchorOffset = focusedNode.root.selectionStartOffset || 0;
+    let focusObject = focusedNode.root.selectionEndObject;
+    let focusOffset = focusedNode.root.selectionEndOffset || 0;
     if (anchorObject === focusObject && anchorOffset == focusOffset) {
       this.onNullSelection_();
       return;
     }
-    // First calculate the equivalant position for this selection.
+    // First calculate the equivalent position for this selection.
     // Sometimes the automation selection returns a offset into a root
     // node rather than a child node, which may be a bug. This allows us to
     // work around that bug until it is fixed or redefined.
diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resources/settings/BUILD.gn
index b1c488c..1772ec2c 100644
--- a/chrome/browser/resources/settings/BUILD.gn
+++ b/chrome/browser/resources/settings/BUILD.gn
@@ -112,6 +112,7 @@
       "internet_page:closure_compile",
       "kiosk_next_shell_page:closure_compile",
       "multidevice_page:closure_compile",
+      "plugin_vm_page:closure_compile",
     ]
   }
 }
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html
index 22d4e7f2..4ce82aa8 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.html
+++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -19,8 +19,8 @@
 <link rel="import" href="../android_apps_page/android_apps_browser_proxy.html">
 <link rel="import" href="../android_apps_page/android_apps_page.html">
 <link rel="import" href="../bluetooth_page/bluetooth_page.html">
-<link rel="import" href="../kiosk_next_shell_page/kiosk_next_shell_page.html">
 <link rel="import" href="../crostini_page/crostini_page.html">
+<link rel="import" href="../plugin_vm_page/plugin_vm_page.html">
 <link rel="import" href="../kiosk_next_shell_page/kiosk_next_shell_page.html">
 <link rel="import" href="../device_page/device_page.html">
 <link rel="import" href="../internet_page/internet_page.html">
@@ -214,6 +214,13 @@
             </settings-crostini-page>
           </settings-section>
         </template>
+        <template is="dom-if" if="[[showPluginVm]]" restamp>
+          <settings-section page-title="$i18n{pluginVmPageTitle}"
+              section="pluginVm">
+            <settings-plugin-vm-page prefs="{{prefs}}"
+            </settings-plugin-vm-page>
+          </settings-section>
+        </template>
 </if>
 <if expr="not chromeos">
         <template is="dom-if" if="[[showPage_(pageVisibility.defaultBrowser)]]"
diff --git a/chrome/browser/resources/settings/plugin_vm_page/BUILD.gn b/chrome/browser/resources/settings/plugin_vm_page/BUILD.gn
new file mode 100644
index 0000000..d414bf60
--- /dev/null
+++ b/chrome/browser/resources/settings/plugin_vm_page/BUILD.gn
@@ -0,0 +1,28 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+js_type_check("closure_compile") {
+  deps = [
+    ":plugin_vm_page",
+    ":plugin_vm_subpage",
+  ]
+}
+
+js_library("plugin_vm_page") {
+  deps = [
+    "..:route",
+    "../prefs:prefs_behavior",
+    "//ui/webui/resources/js:i18n_behavior",
+  ]
+  externs_list = [ "$externs_path/settings_private.js" ]
+}
+
+js_library("plugin_vm_subpage") {
+  deps = [
+    "..:route",
+    "../prefs:prefs_behavior",
+  ]
+}
diff --git a/chrome/browser/resources/settings/plugin_vm_page/plugin_vm_page.html b/chrome/browser/resources/settings/plugin_vm_page/plugin_vm_page.html
new file mode 100644
index 0000000..4587790
--- /dev/null
+++ b/chrome/browser/resources/settings/plugin_vm_page/plugin_vm_page.html
@@ -0,0 +1,36 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="../i18n_setup.html">
+<link rel="import" href="../settings_page/settings_animated_pages.html">
+<link rel="import" href="../settings_page/settings_subpage.html">
+<link rel="import" href="../settings_shared_css.html">
+<link rel="import" href="plugin_vm_subpage.html">
+
+<dom-module id="settings-plugin-vm-page">
+  <template>
+    <style include="settings-shared"></style>
+
+    <settings-animated-pages id="pages" section="pluginVm"
+        focus-config="[[focusConfig_]]">
+      <div route-path="default">
+        <cr-link-row id="pluginVmRow"
+            label="$i18n{pluginVmPageLabel}"
+            sub-label="$i18n{pluginVmPageSubtext}"
+            on-click="onSubpageClick_"></cr-link-row>
+      </div>
+
+      <template is="dom-if" route-path="/pluginVm/details">
+        <settings-subpage
+            associated-control="[[$$('#pluginVmRow')]]"
+            page-title="$i18n{pluginVmPageLabel}">
+          <settings-plugin-vm-subpage prefs="{{prefs}}">
+          </settings-plugin-vm-subpage>
+        </settings-subpage>
+      </template>
+    </settings-animated-pages>
+
+  </template>
+  <script src="plugin_vm_page.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/plugin_vm_page/plugin_vm_page.js b/chrome/browser/resources/settings/plugin_vm_page/plugin_vm_page.js
new file mode 100644
index 0000000..476442b
--- /dev/null
+++ b/chrome/browser/resources/settings/plugin_vm_page/plugin_vm_page.js
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * 'plugin-vm-page' is the settings page for Plugin VM.
+ */
+
+Polymer({
+  is: 'settings-plugin-vm-page',
+
+  behaviors: [PrefsBehavior],
+
+  properties: {
+    /** Preferences state. */
+    prefs: {
+      type: Object,
+      notify: true,
+    },
+
+    /** @private {!Map<string, string>} */
+    focusConfig_: {
+      type: Object,
+      value: function() {
+        const map = new Map();
+        if (settings.routes.PLUGIN_VM_DETAILS) {
+          map.set(settings.routes.PLUGIN_VM_DETAILS.path, '#pluginVmRow');
+        }
+        return map;
+      },
+    },
+  },
+
+  /** @private */
+  onSubpageClick_: function(event) {
+    settings.navigateTo(settings.routes.PLUGIN_VM_DETAILS);
+  },
+});
diff --git a/chrome/browser/resources/settings/plugin_vm_page/plugin_vm_subpage.html b/chrome/browser/resources/settings/plugin_vm_page/plugin_vm_subpage.html
new file mode 100644
index 0000000..3da3e1d
--- /dev/null
+++ b/chrome/browser/resources/settings/plugin_vm_page/plugin_vm_subpage.html
@@ -0,0 +1,14 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="../settings_shared_css.html">
+<link rel="import" href="../controls/settings_toggle_button.html">
+
+<dom-module id="settings-plugin-vm-subpage">
+  <template>
+    <style include="settings-shared"></style>
+    <!-- TODO(timloh): Wire this up to a pref. -->
+    <settings-toggle-button label="$i18n{pluginVmPrinterAccess}">
+    </settings-toggle-button>
+  </template>
+  <script src="plugin_vm_subpage.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/plugin_vm_page/plugin_vm_subpage.js b/chrome/browser/resources/settings/plugin_vm_page/plugin_vm_subpage.js
new file mode 100644
index 0000000..b4f3423
--- /dev/null
+++ b/chrome/browser/resources/settings/plugin_vm_page/plugin_vm_subpage.js
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * 'plugin-vm-subpage' is the settings subpage for managing Plugin VM.
+ */
+
+Polymer({
+  is: 'settings-plugin-vm-subpage',
+
+  behaviors: [PrefsBehavior],
+
+  properties: {
+    /** Preferences state. */
+    prefs: {
+      type: Object,
+      notify: true,
+    },
+  },
+});
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html
index c446473..a145333 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -146,6 +146,13 @@
             sub-label="$i18n{manageCertificatesDescription}"
             on-click="onManageCertificatesTap_"></cr-link-row>
 </if>
+        <template is="dom-if" if="[[enableSecurityKeysSubpage_]]">
+          <cr-link-row id="security-keys-subpage-trigger"
+            class="hr"
+            label="$i18n{securityKeysTitle}"
+            sub-label="$i18n{securityKeysDesc}"
+            on-click="onSecurityKeysTap_"></cr-link-row>
+        </template>
         <cr-link-row id="site-settings-subpage-trigger"
             class="hr"
             label="[[siteSettingsPageTitle_()]]"
@@ -156,13 +163,6 @@
             label="$i18n{clearBrowsingData}"
             sub-label="$i18n{clearBrowsingDataDescription}"
             on-click="onClearBrowsingDataTap_"></cr-link-row>
-        <template is="dom-if" if="[[enableSecurityKeysSubpage_]]">
-          <cr-link-row id="security-keys-subpage-trigger"
-            class="hr"
-            label="$i18n{securityKeysTitle}"
-            sub-label="$i18n{securityKeysDesc}"
-            on-click="onSecurityKeysTap_"></cr-link-row>
-        </template>
       </div>
 
 <if expr="use_nss_certs">
diff --git a/chrome/browser/resources/settings/route.js b/chrome/browser/resources/settings/route.js
index 5507464..b647462a 100644
--- a/chrome/browser/resources/settings/route.js
+++ b/chrome/browser/resources/settings/route.js
@@ -61,6 +61,8 @@
  *   PASSWORDS: (undefined|!settings.Route),
  *   PAYMENTS: (undefined|!settings.Route),
  *   PEOPLE: (undefined|!settings.Route),
+ *   PLUGIN_VM: (undefined|!settings.Route),
+ *   PLUGIN_VM_DETAILS: (undefined|!settings.Route),
  *   POINTERS: (undefined|!settings.Route),
  *   POWER: (undefined|!settings.Route),
  *   PRINTING: (undefined|!settings.Route),
@@ -285,6 +287,12 @@
       r.CROSTINI_SHARED_USB_DEVICES =
           r.CROSTINI.createChild('/crostini/sharedUsbDevices');
     }
+
+    if (loadTimeData.valueExists('showPluginVm') &&
+        loadTimeData.getBoolean('showPluginVm')) {
+      r.PLUGIN_VM = r.BASIC.createSection('/pluginVm', 'pluginVm');
+      r.PLUGIN_VM_DETAILS = r.PLUGIN_VM.createChild('/pluginVm/details');
+    }
     // </if>
 
     if (pageVisibility.onStartup !== false) {
diff --git a/chrome/browser/resources/settings/settings_main/settings_main.html b/chrome/browser/resources/settings/settings_main/settings_main.html
index e9be4ea..1d8db206 100644
--- a/chrome/browser/resources/settings/settings_main/settings_main.html
+++ b/chrome/browser/resources/settings/settings_main/settings_main.html
@@ -66,6 +66,7 @@
           show-android-apps="[[showAndroidApps]]"
           show-kiosk-next-shell="[[showKioskNextShell]]"
           show-crostini="[[showCrostini]]"
+          show-plugin-vm="[[showPluginVm]]"
           have-play-store-app="[[havePlayStoreApp]]"
           on-showing-section="onShowingSection_"
           on-subpage-expand="onShowingSubpage_"
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.html b/chrome/browser/resources/settings/settings_menu/settings_menu.html
index 8c2ebc1..e5c162ee 100644
--- a/chrome/browser/resources/settings/settings_menu/settings_menu.html
+++ b/chrome/browser/resources/settings/settings_menu/settings_menu.html
@@ -138,12 +138,16 @@
         <iron-icon icon="settings:play-prism"></iron-icon>
         $i18n{androidAppsPageTitle}
       </a>
-</if>
-<if expr="chromeos">
       <a href="/crostini" hidden="[[!showCrostini]]">
         <iron-icon icon="settings:crostini-mascot"></iron-icon>
         $i18n{crostiniPageTitle}
       </a>
+      <a href="/pluginVm" hidden="[[!showPluginVm]]">
+        <!-- TODO(crbug.com/950738): The placeholder iron-icon here is needed
+          for spacing. Replace this once we get the proper icon. -->
+        <iron-icon icon="cr:info"></iron-icon>
+        $i18n{pluginVmPageTitle}
+      </a>
 </if>
 <if expr="not chromeos">
       <a id="defaultBrowser" href="/defaultBrowser"
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index b2b4234..830ccc5 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -1544,6 +1544,18 @@
         <structure name="IDR_SETTINGS_PEOPLE_FINGERPRINT_BROWSER_PROXY_HTML"
                    file="people_page/fingerprint_browser_proxy.html"
                    type="chrome_html" />
+        <structure name="IDR_SETTINGS_PLUGIN_VM_PAGE_HTML"
+                   file="plugin_vm_page/plugin_vm_page.html"
+                   type="chrome_html" />
+        <structure name="IDR_SETTINGS_PLUGIN_VM_PAGE_JS"
+                   file="plugin_vm_page/plugin_vm_page.js"
+                   type="chrome_html" />
+        <structure name="IDR_SETTINGS_PLUGIN_VM_SUBPAGE_HTML"
+                   file="plugin_vm_page/plugin_vm_subpage.html"
+                   type="chrome_html" />
+        <structure name="IDR_SETTINGS_PLUGIN_VM_SUBPAGE_JS"
+                   file="plugin_vm_page/plugin_vm_subpage.js"
+                   type="chrome_html" />
         <structure name="IDR_SETTINGS_USERS_PAGE_ADD_USER_DIALOG_JS"
                    file="people_page/users_add_user_dialog.js"
                    type="chrome_html" />
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chrome/browser/resources/settings/settings_ui/settings_ui.html
index 372b651..6e1e81d8 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.html
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -70,6 +70,7 @@
           <settings-menu page-visibility="[[pageVisibility_]]"
               show-android-apps="[[showAndroidApps_]]"
               show-crostini="[[showCrostini_]]"
+              show-plugin-vm="[[showPluginVm_]]"
               show-multidevice="[[showMultidevice_]]"
               have-play-store-app="[[havePlayStoreApp_]]"
               on-iron-activate="onIronActivate_"
@@ -85,6 +86,7 @@
           show-android-apps="[[showAndroidApps_]]"
           show-kiosk-next-shell="[[showKioskNextShell_]]"
           show-crostini="[[showCrostini_]]"
+          show-plugin-vm="[[showPluginVm_]]"
           show-multidevice="[[showMultidevice_]]"
           have-play-store-app="[[havePlayStoreApp_]]"
           advanced-toggle-expanded="{{advancedOpened_}}">
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js
index 14c3b80..90e150c 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.js
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -60,6 +60,9 @@
     showCrostini_: Boolean,
 
     /** @private */
+    showPluginVm_: Boolean,
+
+    /** @private */
     havePlayStoreApp_: Boolean,
 
     /** @private */
@@ -146,6 +149,8 @@
         loadTimeData.getBoolean('showKioskNextShell');
     this.showCrostini_ = loadTimeData.valueExists('showCrostini') &&
         loadTimeData.getBoolean('showCrostini');
+    this.showPluginVm_ = loadTimeData.valueExists('showPluginVm') &&
+        loadTimeData.getBoolean('showPluginVm');
     this.havePlayStoreApp_ = loadTimeData.valueExists('havePlayStoreApp') &&
         loadTimeData.getBoolean('havePlayStoreApp');
 
diff --git a/chrome/browser/sessions/session_restore_android.cc b/chrome/browser/sessions/session_restore_android.cc
index 93632ba..fda4f5fd 100644
--- a/chrome/browser/sessions/session_restore_android.cc
+++ b/chrome/browser/sessions/session_restore_android.cc
@@ -49,8 +49,7 @@
   } else {
     DCHECK(disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
            disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB);
-    tab_model->CreateTab(current_tab, new_web_contents.release(),
-                         current_tab->GetAndroidId());
+    tab_model->CreateTab(current_tab, new_web_contents.release());
   }
   return raw_new_web_contents;
 }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index ebd2bb8..e69ebb0 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1292,6 +1292,7 @@
       "//chrome/browser/ui/webui/app_management:mojo_bindings",
       "//chrome/common:buildflags",
       "//chrome/common:search_mojom",
+      "//chrome/services/app_service/public/cpp:app_service_proxy",
       "//chrome/services/app_service/public/cpp:app_update",
       "//chrome/services/app_service/public/mojom",
       "//components/feedback/proto",
diff --git a/chrome/browser/ui/android/tab_model/android_live_tab_context.cc b/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
index cf2cd82..0223dab 100644
--- a/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
+++ b/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
@@ -100,7 +100,7 @@
 
   // Create new tab. Ownership is passed into java, which in turn creates a new
   // TabAndroid instance to own the WebContents.
-  tab_model_->CreateTab(nullptr, web_contents.release(), -1);
+  tab_model_->CreateTab(nullptr, web_contents.release());
   raw_web_contents->GetController().LoadIfNecessary();
   return sessions::ContentLiveTab::GetForWebContents(raw_web_contents);
 }
diff --git a/chrome/browser/ui/android/tab_model/tab_model.h b/chrome/browser/ui/android/tab_model/tab_model.h
index df4e18e..81e0214 100644
--- a/chrome/browser/ui/android/tab_model/tab_model.h
+++ b/chrome/browser/ui/android/tab_model/tab_model.h
@@ -121,8 +121,7 @@
 
   // Used for restoring tabs from synced foreign sessions.
   virtual void CreateTab(TabAndroid* parent,
-                         content::WebContents* web_contents,
-                         int parent_tab_id) = 0;
+                         content::WebContents* web_contents) = 0;
 
   virtual void HandlePopupNavigation(TabAndroid* parent,
                                      NavigateParams* params) = 0;
diff --git a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
index 595ba1f..ce552b2 100644
--- a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
+++ b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
@@ -90,13 +90,12 @@
 }
 
 void TabModelJniBridge::CreateTab(TabAndroid* parent,
-                                  WebContents* web_contents,
-                                  int parent_tab_id) {
+                                  WebContents* web_contents) {
   JNIEnv* env = AttachCurrentThread();
   Java_TabModelJniBridge_createTabWithWebContents(
       env, java_object_.get(env), (parent ? parent->GetJavaObject() : nullptr),
       web_contents->GetBrowserContext()->IsOffTheRecord(),
-      web_contents->GetJavaWebContents(), parent_tab_id);
+      web_contents->GetJavaWebContents());
 }
 
 void TabModelJniBridge::HandlePopupNavigation(TabAndroid* parent,
diff --git a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.h b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.h
index 443b19b2..d8b0980 100644
--- a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.h
+++ b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.h
@@ -52,8 +52,7 @@
   void CloseTabAt(int index) override;
 
   void CreateTab(TabAndroid* parent,
-                 content::WebContents* web_contents,
-                 int parent_tab_id) override;
+                 content::WebContents* web_contents) override;
   void HandlePopupNavigation(TabAndroid* parent,
                              NavigateParams* params) override;
 
diff --git a/chrome/browser/ui/android/tab_model/tab_model_list_unittest.cc b/chrome/browser/ui/android/tab_model/tab_model_list_unittest.cc
index d603b9ba..ee6eea4 100644
--- a/chrome/browser/ui/android/tab_model/tab_model_list_unittest.cc
+++ b/chrome/browser/ui/android/tab_model/tab_model_list_unittest.cc
@@ -23,8 +23,7 @@
     return nullptr;
   }
   void CreateTab(TabAndroid* parent,
-                 content::WebContents* web_contents,
-                 int parent_tab_id) override {}
+                 content::WebContents* web_contents) override {}
   void HandlePopupNavigation(TabAndroid* parent,
                              NavigateParams* params) override {}
   content::WebContents* CreateNewTabForDevTools(const GURL& url) override {
diff --git a/chrome/browser/ui/android/tab_model/tab_model_unittest.cc b/chrome/browser/ui/android/tab_model/tab_model_unittest.cc
index 1a5e579f..edc31823 100644
--- a/chrome/browser/ui/android/tab_model/tab_model_unittest.cc
+++ b/chrome/browser/ui/android/tab_model/tab_model_unittest.cc
@@ -36,8 +36,7 @@
     return NULL;
   }
   void CreateTab(TabAndroid* parent,
-                 content::WebContents* web_contents,
-                 int parent_tab_id) override {}
+                 content::WebContents* web_contents) override {}
   void HandlePopupNavigation(TabAndroid* parent,
                              NavigateParams* params) override {}
   content::WebContents* CreateNewTabForDevTools(const GURL& url) override {
diff --git a/chrome/browser/ui/app_list/app_service_app_item.cc b/chrome/browser/ui/app_list/app_service_app_item.cc
index f993165f..09858783 100644
--- a/chrome/browser/ui/app_list/app_service_app_item.cc
+++ b/chrome/browser/ui/app_list/app_service_app_item.cc
@@ -6,11 +6,12 @@
 
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "base/bind.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_context_menu.h"
 #include "chrome/browser/ui/app_list/crostini/crostini_app_context_menu.h"
 #include "chrome/browser/ui/app_list/extension_app_context_menu.h"
+#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 
 // static
 const char AppServiceAppItem::kItemType[] = "AppServiceAppItem";
@@ -118,7 +119,8 @@
 
 void AppServiceAppItem::Launch(int event_flags,
                                apps::mojom::LaunchSource launch_source) {
-  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile());
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile());
   if (proxy) {
     proxy->Launch(id(), event_flags, launch_source,
                   GetController()->GetAppListDisplayId());
@@ -126,7 +128,8 @@
 }
 
 void AppServiceAppItem::CallLoadIcon(bool allow_placeholder_icon) {
-  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile());
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile());
   if (proxy) {
     proxy->LoadIcon(app_type_, id(),
                     apps::mojom::IconCompression::kUncompressed,
diff --git a/chrome/browser/ui/app_list/app_service_app_model_builder.cc b/chrome/browser/ui/app_list/app_service_app_model_builder.cc
index 00377d9b..5ad32f2 100644
--- a/chrome/browser/ui/app_list/app_service_app_model_builder.cc
+++ b/chrome/browser/ui/app_list/app_service_app_model_builder.cc
@@ -4,8 +4,9 @@
 
 #include "chrome/browser/ui/app_list/app_service_app_model_builder.h"
 
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ui/app_list/app_service_app_item.h"
+#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 
 AppServiceAppModelBuilder::AppServiceAppModelBuilder(
     AppListControllerDelegate* controller)
@@ -14,7 +15,8 @@
 AppServiceAppModelBuilder::~AppServiceAppModelBuilder() = default;
 
 void AppServiceAppModelBuilder::BuildModel() {
-  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile());
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile());
   if (proxy) {
     proxy->AppRegistryCache().ForEachApp(
         [this](const apps::AppUpdate& update) { OnAppUpdate(update); });
diff --git a/chrome/browser/ui/app_list/extension_app_context_menu.cc b/chrome/browser/ui/app_list/extension_app_context_menu.cc
index 1c5513f..2d83f5b 100644
--- a/chrome/browser/ui/app_list/extension_app_context_menu.cc
+++ b/chrome/browser/ui/app_list/extension_app_context_menu.cc
@@ -12,6 +12,8 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_context_menu_delegate.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
+#include "chrome/browser/web_applications/system_web_app_manager.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/common/context_menu_params.h"
@@ -86,8 +88,12 @@
         profile(), this, menu_model,
         base::Bind(MenuItemHasLauncherContext));
 
+    bool is_system_web_app = web_app::WebAppProvider::Get(profile())
+                                 ->system_web_app_manager()
+                                 .IsSystemWebApp(app_id());
+
     // First, add the primary actions.
-    if (!is_platform_app_)
+    if (!is_platform_app_ && !is_system_web_app)
       CreateOpenNewSubmenu(menu_model);
 
     // Create default items.
@@ -107,7 +113,7 @@
                          is_platform_app_ ? IDS_APP_LIST_UNINSTALL_ITEM
                                           : IDS_APP_LIST_EXTENSIONS_UNINSTALL);
 
-    if (controller()->CanDoShowAppInfoFlow()) {
+    if (controller()->CanDoShowAppInfoFlow() && !is_system_web_app) {
       AddContextMenuOption(menu_model, ash::SHOW_APP_INFO,
                            IDS_APP_CONTEXT_MENU_SHOW_INFO);
     }
diff --git a/chrome/browser/ui/app_list/search/app_search_provider.cc b/chrome/browser/ui/app_list/search/app_search_provider.cc
index 1cd381a..7a73c38 100644
--- a/chrome/browser/ui/app_list/search/app_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/app_search_provider.cc
@@ -28,7 +28,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/clock.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service.h"
@@ -56,6 +56,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
+#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync_sessions/session_sync_service.h"
 #include "extensions/browser/extension_prefs.h"
@@ -288,9 +289,10 @@
  public:
   AppServiceDataSource(Profile* profile, AppSearchProvider* owner)
       : AppSearchProvider::DataSource(profile, owner),
-        icon_cache_(apps::AppServiceProxy::Get(profile),
+        icon_cache_(apps::AppServiceProxyFactory::GetForProfile(profile),
                     apps::IconCache::GarbageCollectionPolicy::kExplicit) {
-    apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile);
+    apps::AppServiceProxy* proxy =
+        apps::AppServiceProxyFactory::GetForProfile(profile);
     if (proxy) {
       Observe(&proxy->AppRegistryCache());
     }
@@ -300,7 +302,8 @@
 
   // AppSearchProvider::DataSource overrides:
   void AddApps(AppSearchProvider::Apps* apps_vector) override {
-    apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile());
+    apps::AppServiceProxy* proxy =
+        apps::AppServiceProxyFactory::GetForProfile(profile());
     if (!proxy) {
       return;
     }
diff --git a/chrome/browser/ui/app_list/search/app_service_app_result.cc b/chrome/browser/ui/app_list/search/app_service_app_result.cc
index 6b8a7ea..125397eb 100644
--- a/chrome/browser/ui/app_list/search/app_service_app_result.cc
+++ b/chrome/browser/ui/app_list/search/app_service_app_result.cc
@@ -7,10 +7,11 @@
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "ash/public/cpp/app_list/app_list_types.h"
 #include "base/bind.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ui/app_list/app_list_client_impl.h"
 #include "chrome/browser/ui/app_list/app_service_app_item.h"
 #include "chrome/browser/ui/app_list/search/internal_app_result.h"
+#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "extensions/common/extension.h"
 
 namespace app_list {
@@ -26,7 +27,8 @@
       is_platform_app_(false),
       show_in_launcher_(false),
       weak_ptr_factory_(this) {
-  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile);
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile);
 
   if (proxy) {
     proxy->AppRegistryCache().ForOneApp(
@@ -117,7 +119,8 @@
 
 void AppServiceAppResult::Launch(int event_flags,
                                  apps::mojom::LaunchSource launch_source) {
-  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile());
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile());
   if (proxy) {
     proxy->Launch(app_id(), event_flags, launch_source,
                   controller()->GetAppListDisplayId());
diff --git a/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
index 038984be..7b8a5ee 100644
--- a/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
@@ -20,7 +20,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/time/time.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_impl.h"
 #include "chrome/browser/chromeos/crostini/crostini_test_helper.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/sync/session_sync_service_factory.h"
@@ -795,7 +795,8 @@
     return;
   }
 
-  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile());
+  apps::AppServiceProxyImpl* proxy =
+      apps::AppServiceProxyImpl::GetImplForTesting(profile());
   ASSERT_NE(proxy, nullptr);
 
   apps::StubIconLoader stub_icon_loader;
diff --git a/chrome/browser/ui/ash/drag_to_overview_interactive_uitest.cc b/chrome/browser/ui/ash/drag_to_overview_interactive_uitest.cc
index cad041f..3fa3da0e 100644
--- a/chrome/browser/ui/ash/drag_to_overview_interactive_uitest.cc
+++ b/chrome/browser/ui/ash/drag_to_overview_interactive_uitest.cc
@@ -201,6 +201,7 @@
   gfx::Vector2d delta(0, drag_length / kSteps);
 
   // Drag tab far enough to detach.
+  test::WaitForNoPointerHoldLock();
   drag_position.Offset(0, GetDetachY(browser_view->tabstrip()));
   ui_controls::SendMouseMoveNotifyWhenDone(
       drag_position.x(), drag_position.y(),
diff --git a/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.cc b/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.cc
index 609610a..d7c5ef2 100644
--- a/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.cc
+++ b/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.cc
@@ -240,8 +240,7 @@
     return cached_keyboard_config_->overscroll_behavior ==
            keyboard::mojom::KeyboardOverscrollBehavior::kEnabled;
   }
-  return !base::CommandLine::ForCurrentProcess()->HasSwitch(
-      keyboard::switches::kDisableVirtualKeyboardOverscroll);
+  return true;
 }
 
 GURL ChromeKeyboardControllerClient::GetVirtualKeyboardUrl() {
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
index aa2619b..ca6c88b 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
@@ -26,7 +26,6 @@
 #include "components/account_id/account_id.h"
 #include "components/arc/arc_util.h"
 #include "components/arc/session/arc_bridge_service.h"
-#include "components/exo/shell_surface_util.h"
 #include "components/user_manager/user_manager.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/env.h"
@@ -172,9 +171,13 @@
 void ArcAppWindowLauncherController::OnWindowVisibilityChanged(
     aura::Window* window,
     bool visible) {
+  const int task_id = arc::GetWindowTaskId(window);
+  if (task_id == arc::kNoTaskId)
+    return;
+
   // Attach window to multi-user manager now to let it manage visibility state
   // of the ARC window correctly.
-  if (GetWindowTaskId(window) > 0) {
+  if (task_id != arc::kSystemWindowTaskId) {
     MultiUserWindowManagerClient::GetInstance()->SetWindowOwner(
         window,
         user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId());
@@ -228,14 +231,15 @@
 
 void ArcAppWindowLauncherController::AttachControllerToWindowIfNeeded(
     aura::Window* window) {
-  const int task_id = GetWindowTaskId(window);
-  if (task_id >= 0) {
-    // System windows are also arc apps.
-    window->SetProperty(aura::client::kAppType,
-                        static_cast<int>(ash::AppType::ARC_APP));
-  }
+  const int task_id = arc::GetWindowTaskId(window);
+  if (task_id == arc::kNoTaskId)
+    return;
 
-  if (task_id <= 0)
+  // System windows are also arc apps.
+  window->SetProperty(aura::client::kAppType,
+                      static_cast<int>(ash::AppType::ARC_APP));
+
+  if (task_id == arc::kSystemWindowTaskId)
     return;
 
   // Check if we have controller for this task.
@@ -540,16 +544,3 @@
   app_window->SetController(nullptr);
   app_window_info->set_app_window(nullptr);
 }
-
-// static
-int ArcAppWindowLauncherController::GetWindowTaskId(aura::Window* window) {
-  const std::string* arc_app_id = exo::GetShellApplicationId(window);
-  if (!arc_app_id)
-    return -1;
-
-  int task_id = -1;
-  if (sscanf(arc_app_id->c_str(), "org.chromium.arc.%d", &task_id) != 1)
-    return -1;
-
-  return task_id;
-}
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h
index 5507e9b..486958b 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h
@@ -39,9 +39,6 @@
   explicit ArcAppWindowLauncherController(ChromeLauncherController* owner);
   ~ArcAppWindowLauncherController() override;
 
-  // Returns ARC task id for the window.
-  static int GetWindowTaskId(aura::Window* window);
-
   // AppWindowLauncherController:
   void ActiveUserChanged(const std::string& user_email) override;
   void AdditionalUserAddedToSession(Profile* profile) override;
diff --git a/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc
index 69414f4..6fc603e1 100644
--- a/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc
+++ b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/chromeos/crostini/crostini_registry_service.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
+#include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/launcher/app_window_base.h"
 #include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h"
@@ -26,6 +27,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "components/arc/arc_util.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/user_manager/user_manager.h"
 #include "extensions/browser/app_window/app_window.h"
@@ -172,7 +174,11 @@
     return;
 
   // Transient windows are set up after window Init, so remove them here.
-  if (wm::GetTransientParent(window)) {
+  // TODO(timloh): ARC and Plugin VM are similar, but Crostini shouldn't need to
+  // know about them.
+  if (wm::GetTransientParent(window) ||
+      arc::GetWindowTaskId(window) != arc::kNoTaskId ||
+      plugin_vm::IsPluginVmWindow(window)) {
     DCHECK(aura_window_to_app_window_.find(window) ==
            aura_window_to_app_window_.end());
     auto it = observed_windows_.find(window);
@@ -199,15 +205,12 @@
     return;
   }
 
-  // Handle genuine Crostini app windows.
-  const std::string* window_app_id = exo::GetShellApplicationId(window);
-
   crostini::CrostiniRegistryService* registry_service =
       crostini::CrostiniRegistryServiceFactory::GetForProfile(
           owner()->profile());
   const std::string& shelf_app_id = registry_service->GetCrostiniShelfAppId(
-      window_app_id, exo::GetShellStartupId(window));
-  // Non-crostini apps (i.e. arc++) are filtered out here.
+      exo::GetShellApplicationId(window), exo::GetShellStartupId(window));
+  // Windows without an application id set will get filtered out here.
   if (shelf_app_id.empty())
     return;
 
diff --git a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
index 454b57d..5783b8b 100644
--- a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
@@ -17,6 +17,8 @@
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h"
 #include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/web_applications/system_web_app_manager.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/common/context_menu_params.h"
@@ -170,12 +172,17 @@
 }
 
 void ExtensionLauncherContextMenu::BuildMenu(ui::SimpleMenuModel* menu_model) {
+  Profile* profile = controller()->profile();
   extension_items_.reset(new extensions::ContextMenuMatcher(
-      controller()->profile(), this, menu_model,
-      base::Bind(MenuItemHasLauncherContext)));
+      profile, this, menu_model,
+      base::BindRepeating(MenuItemHasLauncherContext)));
   if (item().type == ash::TYPE_PINNED_APP || item().type == ash::TYPE_APP) {
-    // V1 apps can be started from the menu - but V2 apps should not.
-    if (!controller()->IsPlatformApp(item().id))
+    // V1 apps can be started from the menu - but V2 apps and system web apps
+    // should not.
+    bool is_system_web_app = web_app::WebAppProvider::Get(profile)
+                                 ->system_web_app_manager()
+                                 .IsSystemWebApp(item().id.app_id);
+    if (!controller()->IsPlatformApp(item().id) && !is_system_web_app)
       CreateOpenNewSubmenu(menu_model);
     AddPinMenu(menu_model);
 
@@ -186,7 +193,7 @@
   } else if (item().type == ash::TYPE_BROWSER_SHORTCUT) {
     AddContextMenuOption(menu_model, ash::MENU_NEW_WINDOW,
                          IDS_APP_LIST_NEW_WINDOW);
-    if (!controller()->profile()->IsGuestSession()) {
+    if (!profile->IsGuestSession()) {
       AddContextMenuOption(menu_model, ash::MENU_NEW_INCOGNITO_WINDOW,
                            IDS_APP_LIST_NEW_INCOGNITO_WINDOW);
     }
diff --git a/chrome/browser/ui/ash/launcher/internal_app_window_shelf_controller.cc b/chrome/browser/ui/ash/launcher/internal_app_window_shelf_controller.cc
index 0d79bbe..5b10b35 100644
--- a/chrome/browser/ui/ash/launcher/internal_app_window_shelf_controller.cc
+++ b/chrome/browser/ui/ash/launcher/internal_app_window_shelf_controller.cc
@@ -102,11 +102,8 @@
       ash::ShelfID::Deserialize(window->GetProperty(ash::kShelfIDKey));
 
   if (shelf_id.IsNull()) {
-    const std::string* window_app_id = exo::GetShellApplicationId(window);
-    if (!window_app_id ||
-        !plugin_vm::IsPluginVmExoApplicationId(*window_app_id)) {
+    if (!plugin_vm::IsPluginVmWindow(window))
       return;
-    }
     shelf_id = ash::ShelfID(plugin_vm::kPluginVmAppId);
     window->SetProperty(ash::kShelfIDKey,
                         new std::string(shelf_id.Serialize()));
diff --git a/chrome/browser/ui/ash/launcher_animations_interactive_uitest.cc b/chrome/browser/ui/ash/launcher_animations_interactive_uitest.cc
new file mode 100644
index 0000000..632e8373
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher_animations_interactive_uitest.cc
@@ -0,0 +1,106 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/task/post_task.h"
+#include "chrome/browser/ui/ash/ash_test_util.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/test/base/perf/performance_test.h"
+#include "content/public/test/test_utils.h"
+#include "ui/base/test/ui_controls.h"
+
+// TODO(oshima): Add tablet mode overview transition.
+class LauncherAnimationsTest : public UIPerformanceTest {
+ public:
+  LauncherAnimationsTest() = default;
+  ~LauncherAnimationsTest() override = default;
+
+  // UIPerformanceTest:
+  void SetUpOnMainThread() override {
+    UIPerformanceTest::SetUpOnMainThread();
+
+    if (base::SysInfo::IsRunningOnChromeOS()) {
+      base::RunLoop run_loop;
+      base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(),
+                            base::TimeDelta::FromSeconds(5));
+      run_loop.Run();
+    }
+  }
+
+  // UIPerformanceTest:
+  std::vector<std::string> GetUMAHistogramNames() const override {
+    return {
+        "Apps.StateTransition.AnimationSmoothness.TabletMode",
+    };
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LauncherAnimationsTest);
+};
+
+IN_PROC_BROWSER_TEST_F(LauncherAnimationsTest, Fullscreen) {
+  // Browser window is used to identify display, so we can use
+  // use the 1st browser window regardless of number of windows created.
+  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
+  aura::Window* browser_window = browser_view->GetWidget()->GetNativeWindow();
+  ash::mojom::ShellTestApiPtr shell_test_api = test::GetShellTestApi();
+  {
+    base::RunLoop waiter;
+    shell_test_api->WaitForLauncherAnimationState(
+        ash::mojom::LauncherAnimationState::kFullscreenAllApps,
+        waiter.QuitClosure());
+    ui_controls::SendKeyPress(browser_window, ui::VKEY_BROWSER_SEARCH,
+                              /*control=*/false,
+                              /*shift=*/true,
+                              /*alt=*/false,
+                              /* command = */ false);
+    waiter.Run();
+  }
+  {
+    base::RunLoop waiter;
+    shell_test_api->WaitForLauncherAnimationState(
+        ash::mojom::LauncherAnimationState::kClosed, waiter.QuitClosure());
+    ui_controls::SendKeyPress(browser_window, ui::VKEY_BROWSER_SEARCH,
+                              /*control=*/false,
+                              /*shift=*/true,
+                              /*alt=*/false,
+                              /* command = */ false);
+
+    waiter.Run();
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(LauncherAnimationsTest, Peeking) {
+  // Browser window is used to identify display, so we can use
+  // use the 1st browser window regardless of number of windows created.
+  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
+  aura::Window* browser_window = browser_view->GetWidget()->GetNativeWindow();
+  ash::mojom::ShellTestApiPtr shell_test_api = test::GetShellTestApi();
+  {
+    base::RunLoop waiter;
+    shell_test_api->WaitForLauncherAnimationState(
+        ash::mojom::LauncherAnimationState::kPeeking, waiter.QuitClosure());
+    ui_controls::SendKeyPress(browser_window, ui::VKEY_BROWSER_SEARCH,
+                              /*control=*/false,
+                              /*shift=*/false,
+                              /*alt=*/false,
+                              /* command = */ false);
+    waiter.Run();
+  }
+  {
+    base::RunLoop waiter;
+    shell_test_api->WaitForLauncherAnimationState(
+        ash::mojom::LauncherAnimationState::kClosed, waiter.QuitClosure());
+    ui_controls::SendKeyPress(browser_window, ui::VKEY_BROWSER_SEARCH,
+                              /*control=*/false,
+                              /*shift=*/false,
+                              /*alt=*/false,
+                              /* command = */ false);
+    waiter.Run();
+  }
+}
diff --git a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc
index c30e271..06c6d214 100644
--- a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc
+++ b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc
@@ -11,6 +11,7 @@
 
 #include "base/macros.h"
 #include "base/process/process_handle.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/win/windows_version.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/themes/theme_service.h"
@@ -41,8 +42,7 @@
       browser_view_(browser_view),
       browser_frame_(browser_frame) {}
 
-BrowserDesktopWindowTreeHostWin::~BrowserDesktopWindowTreeHostWin() {
-}
+BrowserDesktopWindowTreeHostWin::~BrowserDesktopWindowTreeHostWin() {}
 
 views::NativeMenuWin* BrowserDesktopWindowTreeHostWin::GetSystemMenu() {
   if (!system_menu_.get()) {
@@ -74,6 +74,52 @@
 ////////////////////////////////////////////////////////////////////////////////
 // BrowserDesktopWindowTreeHostWin, views::DesktopWindowTreeHostWin overrides:
 
+void BrowserDesktopWindowTreeHostWin::Init(
+    const views::Widget::InitParams& params) {
+  DesktopWindowTreeHostWin::Init(params);
+  if (base::win::GetVersion() < base::win::VERSION_WIN10)
+    return;  // VirtualDesktopManager isn't support pre Win-10.
+
+  CHECK(SUCCEEDED(::CoCreateInstance(__uuidof(VirtualDesktopManager), nullptr,
+                                     CLSCTX_ALL,
+                                     IID_PPV_ARGS(&virtual_desktop_manager_))));
+
+  if (!params.workspace.empty()) {
+    GUID guid = GUID_NULL;
+    HRESULT hr =
+        CLSIDFromString(base::UTF8ToUTF16(params.workspace).c_str(), &guid);
+    if (SUCCEEDED(hr)) {
+      // There are valid reasons MoveWindowToDesktop can fail, e.g.,
+      // the desktop was deleted. If it fails, the window will open on the
+      // current desktop.
+      virtual_desktop_manager_->MoveWindowToDesktop(GetHWND(), guid);
+    }
+  }
+  // This will force the window to re-open in this desktop on restart.
+  // We always want to do this even if |params.workspace| is empty, to handle
+  // the case of new windows.
+  OnHostWorkspaceChanged();
+}
+
+std::string BrowserDesktopWindowTreeHostWin::GetWorkspace() const {
+  std::string workspace_id;
+  if (virtual_desktop_manager_) {
+    GUID workspace_guid;
+    HRESULT hr = virtual_desktop_manager_->GetWindowDesktopId(GetHWND(),
+                                                              &workspace_guid);
+    if (SUCCEEDED(hr)) {
+      LPOLESTR workspace_widestr;
+      StringFromCLSID(workspace_guid, &workspace_widestr);
+      workspace_id = base::WideToUTF8(workspace_widestr);
+      workspace_ = workspace_id;
+      CoTaskMemFree(workspace_widestr);
+    } else {
+      return workspace_.value_or("");
+    }
+  }
+  return workspace_id;
+}
+
 int BrowserDesktopWindowTreeHostWin::GetInitialShowState() const {
   STARTUPINFO si = {0};
   si.cb = sizeof(si);
@@ -195,6 +241,11 @@
                                                     WPARAM w_param,
                                                     LPARAM l_param) {
   switch (message) {
+    case WM_ACTIVATE: {
+      if (workspace_.value_or("") != GetWorkspace())
+        OnHostWorkspaceChanged();
+      break;
+    };
     case WM_CREATE:
       minimize_button_metrics_.Init(GetHWND());
       break;
@@ -300,6 +351,5 @@
         BrowserFrame* browser_frame) {
   return new BrowserDesktopWindowTreeHostWin(native_widget_delegate,
                                              desktop_native_widget_aura,
-                                             browser_view,
-                                             browser_frame);
+                                             browser_view, browser_frame);
 }
diff --git a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.h b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.h
index d547eaf..4c93fe9 100644
--- a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.h
+++ b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.h
@@ -5,6 +5,9 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_DESKTOP_WINDOW_TREE_HOST_WIN_H_
 #define CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_DESKTOP_WINDOW_TREE_HOST_WIN_H_
 
+#include <shobjidl.h>
+#include <wrl/client.h>
+
 #include "base/macros.h"
 #include "chrome/browser/ui/views/frame/browser_desktop_window_tree_host.h"
 #include "chrome/browser/ui/views/frame/minimize_button_metrics_win.h"
@@ -38,6 +41,8 @@
   bool UsesNativeSystemMenu() const override;
 
   // Overridden from DesktopWindowTreeHostWin:
+  void Init(const views::Widget::InitParams& params) override;
+  std::string GetWorkspace() const override;
   int GetInitialShowState() const override;
   bool GetClientAreaInsets(gfx::Insets* insets,
                            HMONITOR monitor) const override;
@@ -68,6 +73,16 @@
   // The wrapped system menu itself.
   std::unique_ptr<views::NativeMenuWin> system_menu_;
 
+  // On Windows10, this is the virtual desktop the browser window was on,
+  // last we checked. This is used to tell if the window has moved to a
+  // different desktop, and notify listeners. It will only be set if
+  // we created |virtual_desktop_manager_|.
+  mutable base::Optional<std::string> workspace_;
+
+  // Only set on Windows10. Set by GetOrCreateVirtualDesktopManager().
+  mutable Microsoft::WRL::ComPtr<IVirtualDesktopManager>
+      virtual_desktop_manager_;
+
   DISALLOW_COPY_AND_ASSIGN(BrowserDesktopWindowTreeHostWin);
 };
 
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 9ff25db..cf670e9 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
@@ -763,6 +763,14 @@
   caption_button_container_->SetVisible(should_show_caption_buttons);
   if (hosted_app_button_container())
     hosted_app_button_container()->SetVisible(should_show_caption_buttons);
+
+  // The entire frame should be repainted for v1 apps, since its visibility can
+  // change (see also ShouldPaint()). Do not invoke this on normal browser
+  // windows since it does not have to repaint frame except for the caption
+  // buttons and repainting might cause stuttering of the animation. See
+  // https://crbug.com/949227.
+  if (!browser_view()->IsBrowserTypeNormal())
+    SchedulePaint();
 }
 
 std::unique_ptr<ash::FrameHeader>
diff --git a/chrome/browser/ui/views/tabs/tab_style_views.cc b/chrome/browser/ui/views/tabs/tab_style_views.cc
index 4262c89..6edd5d7 100644
--- a/chrome/browser/ui/views/tabs/tab_style_views.cc
+++ b/chrome/browser/ui/views/tabs/tab_style_views.cc
@@ -29,46 +29,6 @@
 // Opacity of the active tab background painted over inactive selected tabs.
 constexpr float kSelectedTabOpacity = 0.75f;
 
-// Cache of pre-painted backgrounds for tabs.
-class BackgroundCache {
- public:
-  BackgroundCache() = default;
-  ~BackgroundCache() = default;
-
-  // Updates the cache key with the new values.
-  // Returns true if any of the values changed.
-  bool UpdateCacheKey(float scale,
-                      const gfx::Size& size,
-                      SkColor active_color,
-                      SkColor inactive_color,
-                      SkColor stroke_color,
-                      float stroke_thickness);
-
-  const sk_sp<cc::PaintRecord>& fill_record() const { return fill_record_; }
-  void set_fill_record(sk_sp<cc::PaintRecord>&& record) {
-    fill_record_ = record;
-  }
-
-  const sk_sp<cc::PaintRecord>& stroke_record() const { return stroke_record_; }
-  void set_stroke_record(sk_sp<cc::PaintRecord>&& record) {
-    stroke_record_ = record;
-  }
-
- private:
-  // Parameters used to construct the PaintRecords.
-  float scale_ = 0.f;
-  gfx::Size size_;
-  SkColor active_color_ = 0;
-  SkColor inactive_color_ = 0;
-  SkColor stroke_color_ = 0;
-  float stroke_thickness_ = 0.f;
-
-  sk_sp<cc::PaintRecord> fill_record_;
-  sk_sp<cc::PaintRecord> stroke_record_;
-
-  DISALLOW_COPY_AND_ASSIGN(BackgroundCache);
-};
-
 // Tab style implementation for the GM2 refresh (Chrome 69).
 class GM2TabStyle : public TabStyleViews {
  public:
@@ -153,10 +113,6 @@
 
   std::unique_ptr<GlowHoverController> hover_controller_;
 
-  // Cache of the paint output for tab backgrounds.
-  mutable BackgroundCache background_active_cache_;
-  mutable BackgroundCache background_inactive_cache_;
-
   DISALLOW_COPY_AND_ASSIGN(GM2TabStyle);
 };
 
@@ -183,22 +139,6 @@
   return true;
 }
 
-// BackgroundCache -------------------------------------------------------------
-
-bool BackgroundCache::UpdateCacheKey(float scale,
-                                     const gfx::Size& size,
-                                     SkColor active_color,
-                                     SkColor inactive_color,
-                                     SkColor stroke_color,
-                                     float stroke_thickness) {
-  // Use | instead of || to prevent lazy evaluation.
-  return UpdateValue(&scale_, scale) | UpdateValue(&size_, size) |
-         UpdateValue(&active_color_, active_color) |
-         UpdateValue(&inactive_color_, inactive_color) |
-         UpdateValue(&stroke_color_, stroke_color) |
-         UpdateValue(&stroke_thickness_, stroke_thickness);
-}
-
 // GM2TabStyle -----------------------------------------------------------------
 
 GM2TabStyle::GM2TabStyle(Tab* tab)
@@ -731,61 +671,15 @@
   const SkColor stroke_color =
       tab_->controller()->GetToolbarTopSeparatorColor();
   const bool paint_hover_effect = !active && IsHoverActive();
-  const float scale = canvas->image_scale();
   const float stroke_thickness = GetStrokeThickness(active);
 
-  // If there is a |fill_id| we don't try to cache. This could be improved but
-  // would require knowing then the image from the ThemeProvider had been
-  // changed, and invalidating when the tab's x-coordinate or background_offset_
-  // changed.
-  //
-  // If |paint_hover_effect|, we don't try to cache since hover effects change
-  // on every invalidation and we would need to invalidate the cache based on
-  // the hover states.
-  //
-  // Finally, we don't cache for non-integral scale factors, since tabs draw
-  // with slightly different offsets so as to pixel-align the layout rect (see
-  // ScaleAndAlignBounds()).
-  if (fill_id || paint_hover_effect || (std::trunc(scale) != scale)) {
-    PaintTabBackgroundFill(canvas, active, paint_hover_effect, active_color,
-                           inactive_color, fill_id, y_inset);
-    if (stroke_thickness > 0) {
-      gfx::ScopedCanvas scoped_canvas(clip ? canvas : nullptr);
-      if (clip)
-        canvas->sk_canvas()->clipPath(*clip, SkClipOp::kDifference, true);
-      PaintBackgroundStroke(canvas, active, stroke_color);
-    }
-  } else {
-    const gfx::Size& size = tab_->size();
-    BackgroundCache& cache =
-        active ? background_active_cache_ : background_inactive_cache_;
-
-    // If any of the cache key values have changed, update the cached records.
-    if (cache.UpdateCacheKey(scale, size, active_color, inactive_color,
-                             stroke_color, stroke_thickness)) {
-      cc::PaintRecorder recorder;
-      {
-        gfx::Canvas cache_canvas(
-            recorder.beginRecording(size.width(), size.height()), scale);
-        PaintTabBackgroundFill(&cache_canvas, active, paint_hover_effect,
-                               active_color, inactive_color, fill_id, y_inset);
-        cache.set_fill_record(recorder.finishRecordingAsPicture());
-      }
-      if (stroke_thickness > 0) {
-        gfx::Canvas cache_canvas(
-            recorder.beginRecording(size.width(), size.height()), scale);
-        PaintBackgroundStroke(&cache_canvas, active, stroke_color);
-        cache.set_stroke_record(recorder.finishRecordingAsPicture());
-      }
-    }
-
-    canvas->sk_canvas()->drawPicture(cache.fill_record());
-    if (stroke_thickness > 0) {
-      gfx::ScopedCanvas scoped_canvas(clip ? canvas : nullptr);
-      if (clip)
-        canvas->sk_canvas()->clipPath(*clip, SkClipOp::kDifference, true);
-      canvas->sk_canvas()->drawPicture(cache.stroke_record());
-    }
+  PaintTabBackgroundFill(canvas, active, paint_hover_effect, active_color,
+                         inactive_color, fill_id, y_inset);
+  if (stroke_thickness > 0) {
+    gfx::ScopedCanvas scoped_canvas(clip ? canvas : nullptr);
+    if (clip)
+      canvas->sk_canvas()->clipPath(*clip, SkClipOp::kDifference, true);
+    PaintBackgroundStroke(canvas, active, stroke_color);
   }
 
   PaintSeparators(canvas);
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
index 91ff996..1cbf7df 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
@@ -9,10 +9,11 @@
 
 #include "base/containers/flat_map.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/services/app_service/public/cpp/app_registry_cache.h"
+#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "chrome/services/app_service/public/mojom/types.mojom.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
@@ -48,7 +49,8 @@
       shelf_delegate_(this)
 #endif
 {
-  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile_);
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile_);
 
   // TODO(crbug.com/826982): revisit pending decision on AppServiceProxy in
   // incognito
@@ -62,7 +64,8 @@
 
 void AppManagementPageHandler::OnPinnedChanged(const std::string& app_id,
                                                bool pinned) {
-  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile_);
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile_);
 
   // TODO(crbug.com/826982): revisit pending decision on AppServiceProxy in
   // incognito
@@ -87,7 +90,8 @@
 }
 
 void AppManagementPageHandler::GetApps(GetAppsCallback callback) {
-  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile_);
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile_);
 
   // TODO(crbug.com/826982): revisit pending decision on AppServiceProxy in
   // incognito
@@ -136,7 +140,8 @@
 void AppManagementPageHandler::SetPermission(
     const std::string& app_id,
     apps::mojom::PermissionPtr permission) {
-  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile_);
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile_);
 
   // TODO(crbug.com/826982): revisit pending decision on AppServiceProxy in
   // incognito
@@ -147,7 +152,8 @@
 }
 
 void AppManagementPageHandler::Uninstall(const std::string& app_id) {
-  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile_);
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile_);
 
   // TODO(crbug.com/826982): revisit pending decision on AppServiceProxy in
   // incognito
@@ -158,7 +164,8 @@
 }
 
 void AppManagementPageHandler::OpenNativeSettings(const std::string& app_id) {
-  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile_);
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile_);
 
   // TODO(crbug.com/826982): revisit pending decision on AppServiceProxy in
   // incognito
diff --git a/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc
index 239730a..eb22e97 100644
--- a/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h"
 #include "components/arc/arc_prefs.h"
+#include "components/arc/arc_util.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/exo/surface.h"
 #include "components/exo/wm_helper.h"
@@ -226,8 +227,7 @@
   // Handle ARC current active window if any.
   DiscardActiveArcWindow();
 
-  active_task_id_ =
-      ArcAppWindowLauncherController::GetWindowTaskId(gained_active);
+  active_task_id_ = arc::GetWindowTaskId(gained_active);
   if (active_task_id_ <= 0)
     return;
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
index fa40b7e..b083f92 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h"
 #include "chrome/browser/chromeos/multidevice_setup/multidevice_setup_client_factory.h"
+#include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h"
@@ -148,6 +149,9 @@
   html_source->AddBoolean("allowCrostini",
                           crostini::IsCrostiniUIAllowedForProfile(profile));
 
+  html_source->AddBoolean("showPluginVm",
+                          plugin_vm::IsPluginVmEnabled(profile));
+
   html_source->AddBoolean("isDemoSession",
                           chromeos::DemoSession::IsDeviceInDemoMode());
 
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 4259c13..ba0f3da 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -490,6 +490,21 @@
       base::FeatureList::IsEnabled(chromeos::features::kCrostiniUsbSupport));
 }
 
+void AddPluginVmStrings(content::WebUIDataSource* html_source,
+                        Profile* profile) {
+  static constexpr LocalizedString kLocalizedStrings[] = {
+      {"pluginVmPageTitle", IDS_SETTINGS_PLUGIN_VM_PAGE_TITLE},
+      {"pluginVmPageLabel", IDS_SETTINGS_PLUGIN_VM_PAGE_LABEL},
+      {"pluginVmPrinterAccess", IDS_SETTINGS_PLUGIN_VM_PRINTER_ACCESS},
+  };
+  AddLocalizedStringsBulk(html_source, kLocalizedStrings,
+                          base::size(kLocalizedStrings));
+  html_source->AddString(
+      "pluginVmPageSubtext",
+      l10n_util::GetStringFUTF16(IDS_SETTINGS_PLUGIN_VM_PAGE_SUBTEXT,
+                                 ui::GetChromeOSDeviceName()));
+}
+
 void AddKioskNextShellStrings(content::WebUIDataSource* html_source) {
   static constexpr LocalizedString kLocalizedStrings[] = {
       {"kioskNextShellPageTitle", IDS_SETTINGS_KIOSK_NEXT_SHELL_TITLE},
@@ -2861,6 +2876,7 @@
 
 #if defined(OS_CHROMEOS)
   AddCrostiniStrings(html_source, profile);
+  AddPluginVmStrings(html_source, profile);
   AddKioskNextShellStrings(html_source);
   AddAndroidAppStrings(html_source);
   AddBluetoothStrings(html_source);
diff --git a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc
index bc07536..1ea5ba7 100644
--- a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc
@@ -196,6 +196,9 @@
             WebAppProvider::Get(browser()->profile())
                 ->system_web_app_manager()
                 .GetAppIdForSystemApp(web_app::SystemAppType::SETTINGS));
+  EXPECT_TRUE(WebAppProvider::Get(browser()->profile())
+                  ->system_web_app_manager()
+                  .IsSystemWebApp(app->id()));
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/pending_app_manager.h b/chrome/browser/web_applications/components/pending_app_manager.h
index 1b4c158a..afd16b0 100644
--- a/chrome/browser/web_applications/components/pending_app_manager.h
+++ b/chrome/browser/web_applications/components/pending_app_manager.h
@@ -14,6 +14,7 @@
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "chrome/browser/web_applications/components/install_options.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "url/gurl.h"
 
 namespace web_app {
@@ -104,7 +105,13 @@
       SynchronizeCallback callback);
 
   // Returns the app id for |url| if the PendingAppManager is aware of it.
-  virtual base::Optional<std::string> LookupAppId(const GURL& url) const = 0;
+  virtual base::Optional<AppId> LookupAppId(const GURL& url) const = 0;
+
+  // Returns whether the PendingAppManager has installed an app with |app_id|
+  // from |install_source|.
+  virtual bool HasAppIdWithInstallSource(
+      const AppId& app_id,
+      web_app::InstallSource install_source) const = 0;
 
  private:
   struct SynchronizeRequest {
diff --git a/chrome/browser/web_applications/components/test_pending_app_manager.cc b/chrome/browser/web_applications/components/test_pending_app_manager.cc
index b05481c..3c0443e 100644
--- a/chrome/browser/web_applications/components/test_pending_app_manager.cc
+++ b/chrome/browser/web_applications/components/test_pending_app_manager.cc
@@ -91,9 +91,15 @@
   return urls;
 }
 
-base::Optional<std::string> TestPendingAppManager::LookupAppId(
+base::Optional<AppId> TestPendingAppManager::LookupAppId(
     const GURL& url) const {
   return base::Optional<std::string>();
 }
 
+bool TestPendingAppManager::HasAppIdWithInstallSource(
+    const AppId& app_id,
+    web_app::InstallSource install_source) const {
+  return false;
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/test_pending_app_manager.h b/chrome/browser/web_applications/components/test_pending_app_manager.h
index 69ebab7..d635d2a 100644
--- a/chrome/browser/web_applications/components/test_pending_app_manager.h
+++ b/chrome/browser/web_applications/components/test_pending_app_manager.h
@@ -56,7 +56,10 @@
                                OnceInstallCallback callback) override;
   std::vector<GURL> GetInstalledAppUrls(
       InstallSource install_source) const override;
-  base::Optional<std::string> LookupAppId(const GURL& url) const override;
+  base::Optional<AppId> LookupAppId(const GURL& url) const override;
+  bool HasAppIdWithInstallSource(
+      const AppId& app_id,
+      web_app::InstallSource install_source) const override;
 
  private:
   void DoInstall(InstallOptions install_options, OnceInstallCallback callback);
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
index 4480c40..002a6642 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
@@ -131,11 +131,18 @@
                                                        install_source);
 }
 
-base::Optional<std::string> PendingBookmarkAppManager::LookupAppId(
+base::Optional<web_app::AppId> PendingBookmarkAppManager::LookupAppId(
     const GURL& url) const {
   return extension_ids_map_.LookupExtensionId(url);
 }
 
+bool PendingBookmarkAppManager::HasAppIdWithInstallSource(
+    const web_app::AppId& app_id,
+    web_app::InstallSource install_source) const {
+  return web_app::ExtensionIdsMap::HasExtensionIdWithInstallSource(
+      profile_->GetPrefs(), app_id, install_source);
+}
+
 void PendingBookmarkAppManager::SetTaskFactoryForTesting(
     TaskFactory task_factory) {
   task_factory_ = std::move(task_factory);
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
index f9637e08..693bda2 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
@@ -67,7 +67,10 @@
                                OnceInstallCallback callback) override;
   std::vector<GURL> GetInstalledAppUrls(
       web_app::InstallSource install_source) const override;
-  base::Optional<std::string> LookupAppId(const GURL& url) const override;
+  base::Optional<web_app::AppId> LookupAppId(const GURL& url) const override;
+  bool HasAppIdWithInstallSource(
+      const web_app::AppId& app_id,
+      web_app::InstallSource install_source) const override;
 
   void SetTaskFactoryForTesting(TaskFactory task_factory);
   void SetUninstallerForTesting(
diff --git a/chrome/browser/web_applications/system_web_app_manager.cc b/chrome/browser/web_applications/system_web_app_manager.cc
index 6583d62..0785fd12f 100644
--- a/chrome/browser/web_applications/system_web_app_manager.cc
+++ b/chrome/browser/web_applications/system_web_app_manager.cc
@@ -77,6 +77,11 @@
   return pending_app_manager_->LookupAppId(app->second);
 }
 
+bool SystemWebAppManager::IsSystemWebApp(const AppId& app_id) const {
+  return pending_app_manager_->HasAppIdWithInstallSource(
+      app_id, InstallSource::kSystemInstalled);
+}
+
 void SystemWebAppManager::SetSystemAppsForTesting(
     base::flat_map<SystemAppType, GURL> system_app_urls) {
   system_app_urls_ = std::move(system_app_urls);
diff --git a/chrome/browser/web_applications/system_web_app_manager.h b/chrome/browser/web_applications/system_web_app_manager.h
index d6fef20..538de46 100644
--- a/chrome/browser/web_applications/system_web_app_manager.h
+++ b/chrome/browser/web_applications/system_web_app_manager.h
@@ -40,6 +40,9 @@
   // Returns the app id for the given System App |id|.
   base::Optional<std::string> GetAppIdForSystemApp(SystemAppType id) const;
 
+  // Returns whether |app_id| points to an installed System App.
+  bool IsSystemWebApp(const AppId& app_id) const;
+
  protected:
   void SetSystemAppsForTesting(
       base::flat_map<SystemAppType, GURL> system_app_urls);
diff --git a/chrome/renderer/extensions/automation_internal_custom_bindings.cc b/chrome/renderer/extensions/automation_internal_custom_bindings.cc
index 99c7e1a0..463a90e 100644
--- a/chrome/renderer/extensions/automation_internal_custom_bindings.cc
+++ b/chrome/renderer/extensions/automation_internal_custom_bindings.cc
@@ -572,45 +572,104 @@
       "GetAnchorObjectID",
       [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
          AutomationAXTreeWrapper* tree_wrapper) {
-        result.Set(v8::Number::New(
-            isolate, tree_wrapper->tree()->data().sel_anchor_object_id));
+        const ui::AXTreeData& tree_data = tree_wrapper->tree()->data();
+        result.Set(v8::Number::New(isolate, tree_data.sel_anchor_object_id));
       });
   RouteTreeIDFunction(
       "GetAnchorOffset",
       [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
          AutomationAXTreeWrapper* tree_wrapper) {
-        result.Set(v8::Number::New(
-            isolate, tree_wrapper->tree()->data().sel_anchor_offset));
+        const ui::AXTreeData& tree_data = tree_wrapper->tree()->data();
+        result.Set(v8::Number::New(isolate, tree_data.sel_anchor_offset));
       });
   RouteTreeIDFunction(
       "GetAnchorAffinity",
       [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
          AutomationAXTreeWrapper* tree_wrapper) {
-        result.Set(CreateV8String(
-            isolate,
-            ui::ToString(tree_wrapper->tree()->data().sel_anchor_affinity)));
+        const ui::AXTreeData& tree_data = tree_wrapper->tree()->data();
+        result.Set(CreateV8String(isolate,
+                                  ui::ToString(tree_data.sel_anchor_affinity)));
       });
   RouteTreeIDFunction(
       "GetFocusObjectID",
       [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
          AutomationAXTreeWrapper* tree_wrapper) {
-        result.Set(v8::Number::New(
-            isolate, tree_wrapper->tree()->data().sel_focus_object_id));
+        const ui::AXTreeData& tree_data = tree_wrapper->tree()->data();
+        result.Set(v8::Number::New(isolate, tree_data.sel_focus_object_id));
       });
   RouteTreeIDFunction(
       "GetFocusOffset",
       [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
          AutomationAXTreeWrapper* tree_wrapper) {
-        result.Set(v8::Number::New(
-            isolate, tree_wrapper->tree()->data().sel_focus_offset));
+        const ui::AXTreeData& tree_data = tree_wrapper->tree()->data();
+        result.Set(v8::Number::New(isolate, tree_data.sel_focus_offset));
       });
   RouteTreeIDFunction(
       "GetFocusAffinity",
       [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
          AutomationAXTreeWrapper* tree_wrapper) {
-        result.Set(CreateV8String(
-            isolate,
-            ui::ToString(tree_wrapper->tree()->data().sel_focus_affinity)));
+        const ui::AXTreeData& tree_data = tree_wrapper->tree()->data();
+        result.Set(CreateV8String(isolate,
+                                  ui::ToString(tree_data.sel_focus_affinity)));
+      });
+  RouteTreeIDFunction(
+      "GetSelectionStartObjectID",
+      [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
+         AutomationAXTreeWrapper* tree_wrapper) {
+        const ui::AXTreeData& tree_data = tree_wrapper->tree()->data();
+        int32_t start_object_id = tree_data.sel_is_backward
+                                      ? tree_data.sel_focus_object_id
+                                      : tree_data.sel_anchor_object_id;
+        result.Set(v8::Number::New(isolate, start_object_id));
+      });
+  RouteTreeIDFunction(
+      "GetSelectionStartOffset",
+      [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
+         AutomationAXTreeWrapper* tree_wrapper) {
+        const ui::AXTreeData& tree_data = tree_wrapper->tree()->data();
+        int start_offset = tree_data.sel_is_backward
+                               ? tree_data.sel_focus_offset
+                               : tree_data.sel_anchor_offset;
+        result.Set(v8::Number::New(isolate, start_offset));
+      });
+  RouteTreeIDFunction(
+      "GetSelectionStartAffinity",
+      [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
+         AutomationAXTreeWrapper* tree_wrapper) {
+        const ui::AXTreeData& tree_data = tree_wrapper->tree()->data();
+        ax::mojom::TextAffinity start_affinity =
+            tree_data.sel_is_backward ? tree_data.sel_focus_affinity
+                                      : tree_data.sel_anchor_affinity;
+        result.Set(CreateV8String(isolate, ui::ToString(start_affinity)));
+      });
+  RouteTreeIDFunction(
+      "GetSelectionEndObjectID",
+      [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
+         AutomationAXTreeWrapper* tree_wrapper) {
+        const ui::AXTreeData& tree_data = tree_wrapper->tree()->data();
+        int32_t end_object_id = tree_data.sel_is_backward
+                                    ? tree_data.sel_anchor_object_id
+                                    : tree_data.sel_focus_object_id;
+        result.Set(v8::Number::New(isolate, end_object_id));
+      });
+  RouteTreeIDFunction(
+      "GetSelectionEndOffset",
+      [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
+         AutomationAXTreeWrapper* tree_wrapper) {
+        const ui::AXTreeData& tree_data = tree_wrapper->tree()->data();
+        int end_offset = tree_data.sel_is_backward ? tree_data.sel_anchor_offset
+                                                   : tree_data.sel_focus_offset;
+        result.Set(v8::Number::New(isolate, end_offset));
+      });
+  RouteTreeIDFunction(
+      "GetSelectionEndAffinity",
+      [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
+         AutomationAXTreeWrapper* tree_wrapper) {
+        const ui::AXTreeData& tree_data = tree_wrapper->tree()->data();
+        ax::mojom::TextAffinity end_affinity =
+            tree_data.sel_is_backward ? tree_data.sel_anchor_affinity
+                                      : tree_data.sel_focus_affinity;
+        result.Set(CreateV8String(isolate, ui::ToString(end_affinity)));
       });
 
   // Bindings that take a Tree ID and Node ID and return a property of the node.
diff --git a/chrome/renderer/resources/extensions/automation/automation_node.js b/chrome/renderer/resources/extensions/automation/automation_node.js
index b3896fa..3e78ab9e 100644
--- a/chrome/renderer/resources/extensions/automation/automation_node.js
+++ b/chrome/renderer/resources/extensions/automation/automation_node.js
@@ -78,6 +78,55 @@
 var GetFocusAffinity = natives.GetFocusAffinity;
 
 /**
+ * The start of the selection always comes before its end in the accessibility
+ * tree.
+ * @param {string} axTreeID The id of the accessibility tree.
+ * @return {?number} The ID of the object at the start of the
+ *     selection.
+ */
+var GetSelectionStartObjectID = natives.GetSelectionStartObjectID;
+
+/**
+ * The start of the selection always comes before its end in the accessibility
+ * tree.
+ * @param {string} axTreeID The id of the accessibility tree.
+ * @return {?number} The offset at the start of the selection.
+ */
+var GetSelectionStartOffset = natives.GetSelectionStartOffset;
+
+/**
+ * The start of the selection always comes before its end in the accessibility
+ * tree.
+ * @param {string} axTreeID The id of the accessibility tree.
+ * @return {?string} The affinity at the start of the selection.
+ */
+var GetSelectionStartAffinity = natives.GetSelectionStartAffinity;
+
+/**
+ * The end of the selection always comes after its start in the accessibility
+ * tree.
+ * @param {string} axTreeID The id of the accessibility tree.
+ * @return {?number} The ID of the object at the end of the selection.
+ */
+var GetSelectionEndObjectID = natives.GetSelectionEndObjectID;
+
+/**
+ * The end of the selection always comes after its start in the accessibility
+ * tree.
+ * @param {string} axTreeID The id of the accessibility tree.
+ * @return {?number} The offset at the end of the selection.
+ */
+var GetSelectionEndOffset = natives.GetSelectionEndOffset;
+
+/**
+ * The end of the selection always comes after its start in the accessibility
+ * tree.
+ * @param {string} axTreeID The id of the accessibility tree.
+ * @return {?string} The affinity at the end of the selection.
+ */
+var GetSelectionEndAffinity = natives.GetSelectionEndAffinity;
+
+/**
  * @param {string} axTreeID The id of the accessibility tree.
  * @param {number} nodeID The id of a node.
  * @return {?number} The id of the node's parent, or undefined if it's the
@@ -1374,43 +1423,87 @@
   },
 
   get anchorObject() {
-    var id = GetAnchorObjectID(this.treeID);
+    const id = GetAnchorObjectID(this.treeID);
     if (id && id != -1)
       return this.get(id);
-    else
-      return undefined;
+    return undefined;
   },
 
   get anchorOffset() {
-    var id = GetAnchorObjectID(this.treeID);
+    const id = GetAnchorObjectID(this.treeID);
     if (id && id != -1)
       return GetAnchorOffset(this.treeID);
+    return undefined;
   },
 
   get anchorAffinity() {
-    var id = GetAnchorObjectID(this.treeID);
+    const id = GetAnchorObjectID(this.treeID);
     if (id && id != -1)
       return GetAnchorAffinity(this.treeID);
+    return undefined;
   },
 
   get focusObject() {
-    var id = GetFocusObjectID(this.treeID);
+    const id = GetFocusObjectID(this.treeID);
     if (id && id != -1)
       return this.get(id);
-    else
-      return undefined;
+    return undefined;
   },
 
   get focusOffset() {
-    var id = GetFocusObjectID(this.treeID);
+    const id = GetFocusObjectID(this.treeID);
     if (id && id != -1)
       return GetFocusOffset(this.treeID);
+    return undefined;
   },
 
   get focusAffinity() {
-    var id = GetFocusObjectID(this.treeID);
+    const id = GetFocusObjectID(this.treeID);
     if (id && id != -1)
       return GetFocusAffinity(this.treeID);
+    return undefined;
+  },
+
+  get selectionStartObject() {
+    const id = GetSelectionStartObjectID(this.treeID);
+    if (id && id != -1)
+      return this.get(id);
+    return undefined;
+  },
+
+  get selectionStartOffset() {
+    const id = GetSelectionStartObjectID(this.treeID);
+    if (id && id != -1)
+      return GetSelectionStartOffset(this.treeID);
+    return undefined;
+  },
+
+  get selectionStartAffinity() {
+    const id = GetSelectionStartObjectID(this.treeID);
+    if (id && id != -1)
+      return GetSelectionStartAffinity(this.treeID);
+    return undefined;
+  },
+
+  get selectionEndObject() {
+    const id = GetSelectionEndObjectID(this.treeID);
+    if (id && id != -1)
+      return this.get(id);
+    return undefined;
+  },
+
+  get selectionEndOffset() {
+    const id = GetSelectionEndObjectID(this.treeID);
+    if (id && id != -1)
+      return GetSelectionEndOffset(this.treeID);
+    return undefined;
+  },
+
+  get selectionEndAffinity() {
+    const id = GetSelectionEndObjectID(this.treeID);
+    if (id && id != -1)
+      return GetSelectionEndAffinity(this.treeID);
+    return undefined;
   },
 
   get: function(id) {
@@ -1614,6 +1707,12 @@
     'focusObject',
     'focusOffset',
     'focusAffinity',
+    'selectionStartObject',
+    'selectionStartOffset',
+    'selectionStartAffinity',
+    'selectionEndObject',
+    'selectionEndOffset',
+    'selectionEndAffinity',
   ],
 });
 
diff --git a/chrome/services/app_service/public/cpp/BUILD.gn b/chrome/services/app_service/public/cpp/BUILD.gn
index 9431776a..93f59cd 100644
--- a/chrome/services/app_service/public/cpp/BUILD.gn
+++ b/chrome/services/app_service/public/cpp/BUILD.gn
@@ -2,6 +2,18 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+source_set("app_service_proxy") {
+  sources = [
+    "app_service_proxy.cc",
+    "app_service_proxy.h",
+  ]
+
+  deps = [
+    ":app_update",
+    ":icon_loader",
+  ]
+}
+
 source_set("app_update") {
   sources = [
     "app_registry_cache.cc",
diff --git a/chrome/services/app_service/public/cpp/app_service_proxy.cc b/chrome/services/app_service/public/cpp/app_service_proxy.cc
new file mode 100644
index 0000000..6d2fe0d
--- /dev/null
+++ b/chrome/services/app_service/public/cpp/app_service_proxy.cc
@@ -0,0 +1,52 @@
+// 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/services/app_service/public/cpp/app_service_proxy.h"
+
+#include <utility>
+
+namespace apps {
+
+AppServiceProxy::AppServiceProxy() = default;
+
+AppServiceProxy::~AppServiceProxy() = default;
+
+apps::mojom::AppServicePtr& AppServiceProxy::AppService() {
+  return app_service_;
+}
+
+apps::AppRegistryCache& AppServiceProxy::AppRegistryCache() {
+  return cache_;
+}
+
+apps::mojom::IconKeyPtr AppServiceProxy::GetIconKey(const std::string& app_id) {
+  return apps::mojom::IconKey::New();
+}
+
+std::unique_ptr<apps::IconLoader::Releaser>
+AppServiceProxy::LoadIconFromIconKey(
+    apps::mojom::AppType app_type,
+    const std::string& app_id,
+    apps::mojom::IconKeyPtr icon_key,
+    apps::mojom::IconCompression icon_compression,
+    int32_t size_hint_in_dip,
+    bool allow_placeholder_icon,
+    apps::mojom::Publisher::LoadIconCallback callback) {
+  std::move(callback).Run(apps::mojom::IconValue::New());
+  return nullptr;
+}
+
+void AppServiceProxy::Launch(const std::string& app_id,
+                             int32_t event_flags,
+                             apps::mojom::LaunchSource launch_source,
+                             int64_t display_id) {}
+
+void AppServiceProxy::SetPermission(const std::string& app_id,
+                                    apps::mojom::PermissionPtr permission) {}
+
+void AppServiceProxy::Uninstall(const std::string& app_id) {}
+
+void AppServiceProxy::OpenNativeSettings(const std::string& app_id) {}
+
+}  // namespace apps
diff --git a/chrome/services/app_service/public/cpp/app_service_proxy.h b/chrome/services/app_service/public/cpp/app_service_proxy.h
new file mode 100644
index 0000000..38adb8b1
--- /dev/null
+++ b/chrome/services/app_service/public/cpp/app_service_proxy.h
@@ -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.
+
+#ifndef CHROME_SERVICES_APP_SERVICE_PUBLIC_CPP_APP_SERVICE_PROXY_H_
+#define CHROME_SERVICES_APP_SERVICE_PUBLIC_CPP_APP_SERVICE_PROXY_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/services/app_service/public/cpp/app_registry_cache.h"
+#include "chrome/services/app_service/public/cpp/icon_loader.h"
+#include "chrome/services/app_service/public/mojom/app_service.mojom.h"
+
+namespace apps {
+
+// Abstract superclass (with default no-op methods) for the App Service proxy.
+// See chrome/services/app_service/README.md.
+//
+// Most code, outside of tests, should use an AppServiceProxyImpl.
+class AppServiceProxy : public apps::IconLoader {
+ public:
+  AppServiceProxy();
+  ~AppServiceProxy() override;
+
+  apps::mojom::AppServicePtr& AppService();
+  apps::AppRegistryCache& AppRegistryCache();
+
+  // apps::IconLoader overrides.
+  apps::mojom::IconKeyPtr GetIconKey(const std::string& app_id) override;
+  std::unique_ptr<IconLoader::Releaser> LoadIconFromIconKey(
+      apps::mojom::AppType app_type,
+      const std::string& app_id,
+      apps::mojom::IconKeyPtr icon_key,
+      apps::mojom::IconCompression icon_compression,
+      int32_t size_hint_in_dip,
+      bool allow_placeholder_icon,
+      apps::mojom::Publisher::LoadIconCallback callback) override;
+
+  virtual void Launch(const std::string& app_id,
+                      int32_t event_flags,
+                      apps::mojom::LaunchSource launch_source,
+                      int64_t display_id);
+
+  virtual void SetPermission(const std::string& app_id,
+                             apps::mojom::PermissionPtr permission);
+
+  virtual void Uninstall(const std::string& app_id);
+
+  virtual void OpenNativeSettings(const std::string& app_id);
+
+ protected:
+  apps::mojom::AppServicePtr app_service_;
+  apps::AppRegistryCache cache_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppServiceProxy);
+};
+
+}  // namespace apps
+
+#endif  // CHROME_SERVICES_APP_SERVICE_PUBLIC_CPP_APP_SERVICE_PROXY_H_
diff --git a/chrome/services/app_service/public/cpp/icon_cache.h b/chrome/services/app_service/public/cpp/icon_cache.h
index 6ee992d..41d5b25 100644
--- a/chrome/services/app_service/public/cpp/icon_cache.h
+++ b/chrome/services/app_service/public/cpp/icon_cache.h
@@ -67,7 +67,7 @@
   };
 
   IconCache(IconLoader* wrapped_loader, GarbageCollectionPolicy gc_policy);
-  ~IconCache();
+  ~IconCache() override;
 
   // IconLoader overrides.
   apps::mojom::IconKeyPtr GetIconKey(const std::string& app_id) override;
diff --git a/chrome/services/app_service/public/cpp/icon_loader.cc b/chrome/services/app_service/public/cpp/icon_loader.cc
index 8ffef2b..f455c3b 100644
--- a/chrome/services/app_service/public/cpp/icon_loader.cc
+++ b/chrome/services/app_service/public/cpp/icon_loader.cc
@@ -60,6 +60,10 @@
   return this->app_id_ < that.app_id_;
 }
 
+IconLoader::IconLoader() = default;
+
+IconLoader::~IconLoader() = default;
+
 std::unique_ptr<IconLoader::Releaser> IconLoader::LoadIcon(
     apps::mojom::AppType app_type,
     const std::string& app_id,
diff --git a/chrome/services/app_service/public/cpp/icon_loader.h b/chrome/services/app_service/public/cpp/icon_loader.h
index ca18e90..9223cd8 100644
--- a/chrome/services/app_service/public/cpp/icon_loader.h
+++ b/chrome/services/app_service/public/cpp/icon_loader.h
@@ -45,6 +45,9 @@
     DISALLOW_COPY_AND_ASSIGN(Releaser);
   };
 
+  IconLoader();
+  virtual ~IconLoader();
+
   // Looks up the IconKey for the given app ID.
   virtual apps::mojom::IconKeyPtr GetIconKey(const std::string& app_id) = 0;
 
diff --git a/chrome/services/app_service/public/cpp/stub_icon_loader.h b/chrome/services/app_service/public/cpp/stub_icon_loader.h
index 81c282bd1..07cf37e 100644
--- a/chrome/services/app_service/public/cpp/stub_icon_loader.h
+++ b/chrome/services/app_service/public/cpp/stub_icon_loader.h
@@ -17,7 +17,7 @@
 class StubIconLoader : public IconLoader {
  public:
   StubIconLoader();
-  ~StubIconLoader();
+  ~StubIconLoader() override;
 
   // IconLoader overrides.
   apps::mojom::IconKeyPtr GetIconKey(const std::string& app_id) override;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 0ae4c287..6bf33111 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3551,7 +3551,7 @@
 
   if (!is_android) {
     sources += [
-      "../browser/apps/app_service/app_service_proxy_unittest.cc",
+      "../browser/apps/app_service/app_service_proxy_impl_unittest.cc",
       "../browser/apps/intent_helper/apps_navigation_throttle_unittest.cc",
       "../browser/apps/intent_helper/page_transition_util_unittest.cc",
       "../browser/devtools/devtools_file_system_indexer_unittest.cc",
@@ -5225,6 +5225,7 @@
       ]
       sources += [
         "../browser/ui/ash/drag_to_overview_interactive_uitest.cc",
+        "../browser/ui/ash/launcher_animations_interactive_uitest.cc",
         "../browser/ui/ash/overview_animations_interactive_uitest.cc",
         "../browser/ui/ash/split_view_interactive_uitest.cc",
       ]
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js b/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js
index 1d012b63..e0936f2 100644
--- a/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js
+++ b/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js
@@ -8,6 +8,56 @@
   return getServerURL('set-cookie?' + name + '=' + value);
 }
 
+function testModifyHeadersOnRedirect(useExtraHeaders) {
+  // Use /echoheader instead of observing headers in onSendHeaders to
+  // ensure we're looking at what the server receives. This avoids bugs in the
+  // webRequest implementation from being masked.
+  var finalURL = getServerURL('echoheader?User-Agent&Accept&X-New-Header');
+  var url = getServerURL('server-redirect?' + finalURL);
+  var listener = callbackPass(function(details) {
+    var headers = details.requestHeaders;
+
+    // Test modification.
+    var accept_value;
+    for (var i = 0; i < headers.length; i++) {
+      if (headers[i].name.toLowerCase() === 'user-agent') {
+        headers[i].value = 'foo';
+      } else if (headers[i].name.toLowerCase() === 'accept') {
+        accept_value = headers[i].value;
+      }
+    }
+
+    // Test removal.
+    chrome.test.assertTrue(accept_value.indexOf('image/webp') >= 0);
+    removeHeader(headers, 'accept');
+
+    // Test addition.
+    headers.push({name: 'X-New-Header', value: 'Baz'});
+
+    return {requestHeaders: headers};
+  });
+
+  var extraInfo = ['requestHeaders', 'blocking'];
+  if (useExtraHeaders)
+    extraInfo.push('extraHeaders');
+  chrome.webRequest.onBeforeSendHeaders.addListener(listener,
+      {urls: [finalURL]}, extraInfo);
+
+  navigateAndWait(url, function(tab) {
+    chrome.webRequest.onBeforeSendHeaders.removeListener(listener);
+    chrome.tabs.executeScript(tab.id, {
+      code: 'document.body.innerText'
+    }, callbackPass(function(results) {
+      chrome.test.assertTrue(results[0].indexOf('foo') >= 0,
+          'User-Agent should be modified.');
+        chrome.test.assertTrue(results[0].indexOf('image/webp') == -1,
+          'Accept should be removed.');
+      chrome.test.assertTrue(results[0].indexOf('Baz') >= 0,
+          'X-New-Header should be added.');
+    }));
+  });
+}
+
 runTests([
   function testSpecialRequestHeadersVisible() {
     // Set a cookie so the cookie request header is set.
@@ -200,6 +250,14 @@
     });
   },
 
+  function testModifyHeadersOnRedirectWithoutExtraHeaders() {
+    testModifyHeadersOnRedirect(false);
+  },
+
+  function testModifyHeadersOnRedirectWithExtraHeaders() {
+    testModifyHeadersOnRedirect(true);
+  },
+
   // Successful Set-Cookie modification is tested in test_blocking_cookie.js.
   function testCannotModifySpecialResponseHeadersWithoutExtraHeaders() {
     // Use unique name and value so other tests don't interfere.
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 066600df..e21e0a8d 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -469,6 +469,7 @@
   sources = [
     "cast_content_gesture_handler_test.cc",
     "cast_media_blocker_browsertest.cc",
+    "cast_web_contents_browsertest.cc",
     "renderer_prelauncher_test.cc",
     "test/cast_features_browsertest.cc",
     "test/cast_navigation_browsertest.cc",
@@ -478,15 +479,20 @@
 
   deps = [
     ":test_support",
+    "//base",
     "//chromecast:chromecast_buildflags",
     "//chromecast/base",
     "//chromecast/base:chromecast_switches",
     "//chromecast/base/metrics",
     "//components/prefs",
+    "//content/public/browser",
+    "//content/test:test_support",
     "//media:test_support",
+    "//net:test_support",
   ]
 
   data = [
+    "//chromecast/browser/test/data/",
     "//media/test/data/",
   ]
 }
diff --git a/chromecast/browser/cast_web_contents.h b/chromecast/browser/cast_web_contents.h
index d60a174..4c64868 100644
--- a/chromecast/browser/cast_web_contents.h
+++ b/chromecast/browser/cast_web_contents.h
@@ -28,6 +28,70 @@
 };
 
 // Simplified WebContents wrapper class for Cast platforms.
+//
+// Proper usage of content::WebContents relies on understanding the meaning
+// behind various WebContentsObserver methods, and then translating those
+// signals into some concrete state. CastWebContents does *not* own the
+// underlying WebContents (usually whatever class implements
+// content::WebContentsDelegate is the actual owner).
+//
+// =============================================================================
+// Lifetime
+// =============================================================================
+// CastWebContents *must* be created before WebContents begins loading any
+// content. Once content begins loading (via CWC::LoadUrl() or one of the
+// WebContents navigation APIs), CastWebContents will calculate its state based
+// on the status of the WebContents' *main* RenderFrame. Events from sub-frames
+// (e.g. iframes) are ignored, since we expect the web app to take care of
+// sub-frame errors.
+//
+// We consider the CastWebContents to be in a LOADED state when the content of
+// the main frame is fully loaded and running (all resources fetched, JS is
+// running). Iframes might still be loading in this case, but in general we
+// consider the page to be in a presentable state at this stage. It is
+// appropriate to display the WebContents to the user.
+//
+// During or after the page is loaded, there are multiple error conditions that
+// can occur. The following events will cause the page to enter an ERROR state:
+//
+// 1. If the main frame is served an HTTP error page (such as a 404 page), then
+//    it means the desired content wasn't loaded.
+//
+// 2. If the main frame fails to load, such as when the browser blocked the URL
+//    request, we treat this as an error.
+//
+// 3. The RenderProcess for the main frame could crash, so the page is not in a
+//    usable state.
+//
+// The CastWebContents user can respond to these errors in a few ways: The
+// content can be reloaded, or the entire page activity can be cancelled. If we
+// totally cancel the activity, we prefer to notify the user with an error
+// screen or visible/audible error message. Otherwise, a silent retry is
+// preferred.
+//
+// CastWebContents can be used to close the underlying WebContents gracefully
+// via CWC::Close(). This initiates web page tear-down logic so that the web
+// app has a chance to perform its own finalization logic in JS. Next, we call
+// WebContents::ClosePage(), which defers the page closure logic to the
+// content::WebContentsDelegate. Usually, it will run its own finalization
+// logic and then destroy the WebContents. CastWebContents will be notified of
+// the WebContents destruction and enter the DESTROYED state. In the event
+// the page isn't destroyed, the page will enter the CLOSED state automatically
+// after a timeout. CastWebContents users should not try to reload the page, as
+// page closure is intentional.
+//
+// The web app may decide to close itself (such as via "window.close()" in JS).
+// This is similar to initiating the close flow via CWC::Close(), with the end
+// result being the same. We consider this an intentional closure, and should
+// not attempt to reload the page.
+//
+// Once CastWebContents is in the DESTROYED state, it is not really usable
+// anymore; most of the methods will simply no-op, and no more observer signals
+// will be emitted.
+//
+// CastWebContents can be deleted at any time, *except* during Observer
+// notifications. If the owner wants to destroy CastWebContents as a result of
+// an Observer event, it should post a task to destroy CastWebContents.
 class CastWebContents {
  public:
   class Delegate {
@@ -39,17 +103,19 @@
     // Called when the page has stopped. e.g.: A 404 occurred when loading the
     // page or if the render process for the main frame crashes. |error_code|
     // will return a net::Error describing the failure, or net::OK if the page
-    // closed naturally.
+    // closed intentionally.
     //
     // After this method, the page state will be one of the following:
-    // CLOSED: Page was closed as expected and the WebContents exists.
+    // CLOSED: Page was closed as expected and the WebContents exists. The page
+    //     should generally not be reloaded, since the page closure was
+    //     triggered intentionally.
+    // ERROR: Page is in an error state. It should be reloaded or deleted.
     // DESTROYED: Page was closed due to deletion of WebContents. The
     //     CastWebContents instance is no longer usable and should be deleted.
-    // ERROR: Page is in an error state. It should be reloaded or deleted.
     virtual void OnPageStopped(CastWebContents* cast_web_contents,
                                int error_code) = 0;
 
-    // Notify that a inner WebContents was created. |inner_contents| is created
+    // Notify that an inner WebContents was created. |inner_contents| is created
     // in a default-initialized state with no delegate, and can be safely
     // initialized by the delegate.
     virtual void InnerContentsCreated(CastWebContents* inner_contents,
@@ -66,6 +132,9 @@
     virtual void RenderFrameCreated(int render_process_id,
                                     int render_frame_id) {}
 
+    // Notifies that a resource for the main frame failed to load.
+    virtual void ResourceLoadFailed(CastWebContents* cast_web_contents) {}
+
     // Adds |this| to the ObserverList in the implementation of
     // |cast_web_contents|.
     void Observe(CastWebContents* cast_web_contents);
@@ -83,10 +152,15 @@
     CastWebContents* cast_web_contents_;
   };
 
+  // Initialization parameters for CastWebContents.
   struct InitParams {
     Delegate* delegate;
+    // Whether the underlying WebContents is exposed to the remote debugger.
     bool enabled_for_dev;
+    // Chooses a media renderer for the WebContents.
     bool use_cma_renderer;
+    // Whether the WebContents is a root native window, or if it is embedded in
+    // another WebContents (see Delegate::InnerContentsCreated()).
     bool is_root_window = false;
   };
 
@@ -142,8 +216,8 @@
 
   // Stop the page immediately. This will automatically invoke
   // Delegate::OnPageStopped(error_code), allowing the delegate to delete or
-  // reload the page without waiting for page teardown, which may be handled
-  // independently.
+  // reload the page without waiting for the WebContents owner to tear down the
+  // page.
   virtual void Stop(int error_code) = 0;
 
   // Used to add or remove |observer| to the ObserverList in the implementation.
diff --git a/chromecast/browser/cast_web_contents_browsertest.cc b/chromecast/browser/cast_web_contents_browsertest.cc
new file mode 100644
index 0000000..7773646
--- /dev/null
+++ b/chromecast/browser/cast_web_contents_browsertest.cc
@@ -0,0 +1,604 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_BROWSER_CAST_WEB_CONTENTS_BROWSERTEST_H_
+#define CHROMECAST_BROWSER_CAST_WEB_CONTENTS_BROWSERTEST_H_
+
+#include <algorithm>
+#include <memory>
+
+#include "base/command_line.h"
+#include "base/containers/flat_set.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "chromecast/base/chromecast_switches.h"
+#include "chromecast/base/metrics/cast_metrics_helper.h"
+#include "chromecast/browser/cast_browser_context.h"
+#include "chromecast/browser/cast_browser_process.h"
+#include "chromecast/browser/cast_web_contents_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_base.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/url_loader_interceptor.h"
+#include "net/http/http_status_code.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::AtLeast;
+using ::testing::Eq;
+using ::testing::Expectation;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Mock;
+using ::testing::Property;
+
+namespace content {
+class WebContents;
+}
+
+namespace chromecast {
+
+namespace {
+
+const base::FilePath::CharType kTestDataPath[] =
+    FILE_PATH_LITERAL("chromecast/browser/test/data");
+
+base::FilePath GetTestDataPath() {
+  return base::FilePath(kTestDataPath);
+}
+
+base::FilePath GetTestDataFilePath(const std::string& name) {
+  base::FilePath file_path;
+  CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
+  return file_path.Append(GetTestDataPath()).AppendASCII(name);
+}
+
+std::unique_ptr<net::test_server::HttpResponse> DefaultHandler(
+    net::HttpStatusCode status_code,
+    const net::test_server::HttpRequest& request) {
+  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
+  http_response->set_code(status_code);
+  return http_response;
+}
+
+// =============================================================================
+// Mocks
+// =============================================================================
+class MockCastWebContentsDelegate : public CastWebContents::Delegate {
+ public:
+  MockCastWebContentsDelegate() {}
+  ~MockCastWebContentsDelegate() override = default;
+
+  MOCK_METHOD1(OnPageStateChanged, void(CastWebContents* cast_web_contents));
+  MOCK_METHOD2(OnPageStopped,
+               void(CastWebContents* cast_web_contents, int error_code));
+  MOCK_METHOD2(InnerContentsCreated,
+               void(CastWebContents* inner_contents,
+                    CastWebContents* outer_contents));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCastWebContentsDelegate);
+};
+
+class MockCastWebContentsObserver : public CastWebContents::Observer {
+ public:
+  MockCastWebContentsObserver() {}
+  ~MockCastWebContentsObserver() override = default;
+
+  MOCK_METHOD2(RenderFrameCreated,
+               void(int render_process_id, int render_frame_id));
+  MOCK_METHOD1(ResourceLoadFailed, void(CastWebContents* cast_web_contents));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCastWebContentsObserver);
+};
+
+class MockWebContentsDelegate : public content::WebContentsDelegate {
+ public:
+  MockWebContentsDelegate() = default;
+  ~MockWebContentsDelegate() override = default;
+
+  MOCK_METHOD1(CloseContents, void(content::WebContents* source));
+};
+
+}  // namespace
+
+// =============================================================================
+// Test class
+// =============================================================================
+class CastWebContentsBrowserTest : public content::BrowserTestBase,
+                                   public content::WebContentsObserver {
+ protected:
+  CastWebContentsBrowserTest() = default;
+  ~CastWebContentsBrowserTest() override = default;
+
+  void SetUp() final {
+    SetUpCommandLine(base::CommandLine::ForCurrentProcess());
+    BrowserTestBase::SetUp();
+  }
+  void SetUpCommandLine(base::CommandLine* command_line) final {
+    command_line->AppendSwitch(switches::kNoWifi);
+    command_line->AppendSwitchASCII(switches::kTestType, "browser");
+  }
+  void PreRunTestOnMainThread() override {
+    // Pump startup related events.
+    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+    base::RunLoop().RunUntilIdle();
+
+    metrics::CastMetricsHelper::GetInstance()->SetDummySessionIdForTesting();
+    content::WebContents::CreateParams create_params(
+        shell::CastBrowserProcess::GetInstance()->browser_context(), nullptr);
+    web_contents_ = content::WebContents::Create(create_params);
+    web_contents_->SetDelegate(&mock_wc_delegate_);
+
+    CastWebContents::InitParams init_params = {
+        &mock_cast_wc_delegate_, false /* enabled_for_dev */,
+        false /* use_cma_renderer */, true /* is_root_window */};
+    cast_web_contents_ =
+        std::make_unique<CastWebContentsImpl>(web_contents_.get(), init_params);
+    mock_cast_wc_observer_.Observe(cast_web_contents_.get());
+
+    render_frames_.clear();
+    content::WebContentsObserver::Observe(web_contents_.get());
+  }
+  void PostRunTestOnMainThread() override {
+    cast_web_contents_.reset();
+    web_contents_.reset();
+  }
+
+  // content::WebContentsObserver implementation:
+  void RenderFrameCreated(content::RenderFrameHost* render_frame_host) final {
+    render_frames_.insert(render_frame_host);
+  }
+
+  void StartTestServer() {
+    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
+    embedded_test_server()->StartAcceptingConnections();
+  }
+
+  MockWebContentsDelegate mock_wc_delegate_;
+  MockCastWebContentsDelegate mock_cast_wc_delegate_;
+  MockCastWebContentsObserver mock_cast_wc_observer_;
+  std::unique_ptr<content::WebContents> web_contents_;
+  std::unique_ptr<CastWebContentsImpl> cast_web_contents_;
+
+  base::flat_set<content::RenderFrameHost*> render_frames_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CastWebContentsBrowserTest);
+};
+
+MATCHER_P2(CheckPageState, cwc_ptr, expected_state, "") {
+  if (arg != cwc_ptr)
+    return false;
+  return arg->page_state() == expected_state;
+}
+
+// =============================================================================
+// Test cases
+// =============================================================================
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest, Lifecycle) {
+  auto run_loop = std::make_unique<base::RunLoop>();
+  auto quit_closure = [&run_loop]() {
+    if (run_loop->running()) {
+      run_loop->QuitWhenIdle();
+    }
+  };
+
+  // ===========================================================================
+  // Test: Load a blank page successfully, verify LOADED state.
+  // ===========================================================================
+  {
+    InSequence seq;
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(
+            cast_web_contents_.get(), CastWebContents::PageState::LOADING)));
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(cast_web_contents_.get(),
+                                          CastWebContents::PageState::LOADED)))
+        .WillOnce(InvokeWithoutArgs(quit_closure));
+  }
+
+  cast_web_contents_->LoadUrl(GURL(url::kAboutBlankURL));
+  run_loop->Run();
+
+  // ===========================================================================
+  // Test: Load a blank page via WebContents API, verify LOADED state.
+  // ===========================================================================
+  {
+    InSequence seq;
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(
+            cast_web_contents_.get(), CastWebContents::PageState::LOADING)));
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(cast_web_contents_.get(),
+                                          CastWebContents::PageState::LOADED)))
+        .WillOnce(InvokeWithoutArgs(quit_closure));
+  }
+
+  run_loop = std::make_unique<base::RunLoop>();
+  web_contents_->GetController().LoadURL(GURL(url::kAboutBlankURL),
+                                         content::Referrer(),
+                                         ui::PAGE_TRANSITION_TYPED, "");
+  run_loop->Run();
+
+  // ===========================================================================
+  // Test: Inject an iframe, verify no events are received for the frame.
+  // ===========================================================================
+  EXPECT_CALL(mock_cast_wc_delegate_, OnPageStateChanged(_)).Times(0);
+  EXPECT_CALL(mock_cast_wc_delegate_, OnPageStopped(_, _)).Times(0);
+  std::string script =
+      "var iframe = document.createElement('iframe');"
+      "document.body.appendChild(iframe);"
+      "iframe.src = 'about:blank';";
+  ASSERT_TRUE(ExecJs(web_contents_.get(), script));
+
+  // ===========================================================================
+  // Test: Inject an iframe and navigate it to an error page. Verify no events.
+  // ===========================================================================
+  EXPECT_CALL(mock_cast_wc_delegate_, OnPageStateChanged(_)).Times(0);
+  EXPECT_CALL(mock_cast_wc_delegate_, OnPageStopped(_, _)).Times(0);
+  script = "iframe.src = 'https://www.fake-non-existent-cast-page.com';";
+  ASSERT_TRUE(ExecJs(web_contents_.get(), script));
+
+  // ===========================================================================
+  // Test: Close the CastWebContents. WebContentsDelegate will be told to close
+  // the page, and then after the timeout elapses CWC will enter the CLOSED
+  // state and notify that the page has stopped.
+  // ===========================================================================
+  EXPECT_CALL(mock_wc_delegate_, CloseContents(web_contents_.get()))
+      .Times(AtLeast(1));
+  EXPECT_CALL(mock_cast_wc_delegate_,
+              OnPageStopped(CheckPageState(cast_web_contents_.get(),
+                                           CastWebContents::PageState::CLOSED),
+                            net::OK))
+      .WillOnce(InvokeWithoutArgs(quit_closure));
+  run_loop = std::make_unique<base::RunLoop>();
+  cast_web_contents_->ClosePage();
+  run_loop->Run();
+
+  // ===========================================================================
+  // Test: Destroy the underlying WebContents. Verify DESTROYED state.
+  // ===========================================================================
+  EXPECT_CALL(
+      mock_cast_wc_delegate_,
+      OnPageStateChanged(CheckPageState(
+          cast_web_contents_.get(), CastWebContents::PageState::DESTROYED)));
+  web_contents_.reset();
+  cast_web_contents_.reset();
+}
+
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest, WebContentsDestroyed) {
+  auto run_loop = std::make_unique<base::RunLoop>();
+  auto quit_closure = [&run_loop]() {
+    if (run_loop->running()) {
+      run_loop->QuitWhenIdle();
+    }
+  };
+
+  {
+    InSequence seq;
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(
+            cast_web_contents_.get(), CastWebContents::PageState::LOADING)));
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(cast_web_contents_.get(),
+                                          CastWebContents::PageState::LOADED)))
+        .WillOnce(InvokeWithoutArgs(quit_closure));
+  }
+
+  cast_web_contents_->LoadUrl(GURL(url::kAboutBlankURL));
+  run_loop->Run();
+
+  // ===========================================================================
+  // Test: Destroy the WebContents. Verify OnPageStopped(DESTROYED, net::OK).
+  // ===========================================================================
+  EXPECT_CALL(
+      mock_cast_wc_delegate_,
+      OnPageStopped(CheckPageState(cast_web_contents_.get(),
+                                   CastWebContents::PageState::DESTROYED),
+                    net::OK));
+  web_contents_.reset();
+}
+
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest, ErrorPageCrash) {
+  auto run_loop = std::make_unique<base::RunLoop>();
+  auto quit_closure = [&run_loop]() {
+    if (run_loop->running()) {
+      run_loop->QuitWhenIdle();
+    }
+  };
+
+  // ===========================================================================
+  // Test: If the page's main render process crashes, enter ERROR state.
+  // ===========================================================================
+  {
+    InSequence seq;
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(
+            cast_web_contents_.get(), CastWebContents::PageState::LOADING)));
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(cast_web_contents_.get(),
+                                          CastWebContents::PageState::LOADED)))
+        .WillOnce(InvokeWithoutArgs(quit_closure));
+  }
+
+  cast_web_contents_->LoadUrl(GURL(url::kAboutBlankURL));
+  run_loop->Run();
+
+  EXPECT_CALL(mock_cast_wc_delegate_,
+              OnPageStopped(CheckPageState(cast_web_contents_.get(),
+                                           CastWebContents::PageState::ERROR),
+                            net::ERR_UNEXPECTED));
+  CrashTab(web_contents_.get());
+}
+
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest, ErrorLocalFileMissing) {
+  auto run_loop = std::make_unique<base::RunLoop>();
+  auto quit_closure = [&run_loop]() {
+    if (run_loop->running()) {
+      run_loop->QuitWhenIdle();
+    }
+  };
+
+  // ===========================================================================
+  // Test: Loading a page with an HTTP error should enter ERROR state.
+  // ===========================================================================
+  {
+    InSequence seq;
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(
+            cast_web_contents_.get(), CastWebContents::PageState::LOADING)));
+    EXPECT_CALL(mock_cast_wc_delegate_,
+                OnPageStopped(CheckPageState(cast_web_contents_.get(),
+                                             CastWebContents::PageState::ERROR),
+                              _))
+        .WillOnce(InvokeWithoutArgs(quit_closure));
+  }
+
+  base::FilePath path = GetTestDataFilePath("this_file_does_not_exist.html");
+  cast_web_contents_->LoadUrl(content::GetFileUrlWithQuery(path, ""));
+  run_loop->Run();
+}
+
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest, ErrorLoadFailSubFrames) {
+  auto run_loop = std::make_unique<base::RunLoop>();
+  auto quit_closure = [&run_loop]() {
+    if (run_loop->running()) {
+      run_loop->QuitWhenIdle();
+    }
+  };
+
+  // ===========================================================================
+  // Test: Ignore load errors in sub-frames.
+  // ===========================================================================
+  {
+    InSequence seq;
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(
+            cast_web_contents_.get(), CastWebContents::PageState::LOADING)));
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(cast_web_contents_.get(),
+                                          CastWebContents::PageState::LOADED)))
+        .WillOnce(InvokeWithoutArgs(quit_closure));
+  }
+
+  cast_web_contents_->LoadUrl(GURL(url::kAboutBlankURL));
+  run_loop->Run();
+
+  // Create a sub-frame.
+  EXPECT_CALL(mock_cast_wc_delegate_, OnPageStateChanged(_)).Times(0);
+  EXPECT_CALL(mock_cast_wc_delegate_, OnPageStopped(_, _)).Times(0);
+  std::string script =
+      "var iframe = document.createElement('iframe');"
+      "document.body.appendChild(iframe);"
+      "iframe.src = 'about:blank';";
+  ASSERT_TRUE(ExecJs(web_contents_.get(), script));
+
+  ASSERT_EQ(2, (int)render_frames_.size());
+  auto it =
+      std::find_if(render_frames_.begin(), render_frames_.end(),
+                   [this](content::RenderFrameHost* frame) {
+                     return frame->GetParent() == web_contents_->GetMainFrame();
+                   });
+  ASSERT_NE(render_frames_.end(), it);
+  content::RenderFrameHost* sub_frame = *it;
+  ASSERT_NE(nullptr, sub_frame);
+  cast_web_contents_->DidFailLoad(sub_frame, sub_frame->GetLastCommittedURL(),
+                                  net::ERR_FAILED, base::string16());
+
+  // ===========================================================================
+  // Test: Ignore main frame load failures with net::ERR_ABORTED.
+  // ===========================================================================
+  EXPECT_CALL(mock_cast_wc_delegate_, OnPageStateChanged(_)).Times(0);
+  EXPECT_CALL(mock_cast_wc_delegate_, OnPageStopped(_, _)).Times(0);
+  cast_web_contents_->DidFailLoad(
+      web_contents_->GetMainFrame(),
+      web_contents_->GetMainFrame()->GetLastCommittedURL(), net::ERR_ABORTED,
+      base::string16());
+
+  // ===========================================================================
+  // Test: If main frame fails to load, page should enter ERROR state.
+  // ===========================================================================
+  EXPECT_CALL(mock_cast_wc_delegate_,
+              OnPageStopped(CheckPageState(cast_web_contents_.get(),
+                                           CastWebContents::PageState::ERROR),
+                            net::ERR_FAILED));
+  cast_web_contents_->DidFailLoad(
+      web_contents_->GetMainFrame(),
+      web_contents_->GetMainFrame()->GetLastCommittedURL(), net::ERR_FAILED,
+      base::string16());
+}
+
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest, ErrorHttp4XX) {
+  auto run_loop = std::make_unique<base::RunLoop>();
+  auto quit_closure = [&run_loop]() {
+    if (run_loop->running()) {
+      run_loop->QuitWhenIdle();
+    }
+  };
+
+  // ===========================================================================
+  // Test: If a server responds with an HTTP 4XX error, page should enter ERROR
+  // state.
+  // ===========================================================================
+  embedded_test_server()->RegisterRequestHandler(
+      base::BindRepeating(&DefaultHandler, net::HTTP_NOT_FOUND));
+  StartTestServer();
+
+  {
+    InSequence seq;
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(
+            cast_web_contents_.get(), CastWebContents::PageState::LOADING)));
+    EXPECT_CALL(mock_cast_wc_delegate_,
+                OnPageStopped(CheckPageState(cast_web_contents_.get(),
+                                             CastWebContents::PageState::ERROR),
+                              net::ERR_FAILED))
+        .WillOnce(InvokeWithoutArgs(quit_closure));
+  }
+
+  cast_web_contents_->LoadUrl(embedded_test_server()->GetURL("/dummy.html"));
+  run_loop->Run();
+}
+
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest, ErrorLoadFailed) {
+  auto run_loop = std::make_unique<base::RunLoop>();
+  auto quit_closure = [&run_loop]() {
+    if (run_loop->running()) {
+      run_loop->QuitWhenIdle();
+    }
+  };
+
+  // ===========================================================================
+  // Test: When main frame load fails, enter ERROR state. This test simulates a
+  // load error by intercepting the URL request and failing it with an arbitrary
+  // error code.
+  // ===========================================================================
+  base::FilePath path = GetTestDataFilePath("dummy.html");
+  GURL gurl = content::GetFileUrlWithQuery(path, "");
+  content::URLLoaderInterceptor url_interceptor(base::BindRepeating(
+      [](const GURL& url,
+         content::URLLoaderInterceptor::RequestParams* params) {
+        if (params->url_request.url != url)
+          return false;
+        network::URLLoaderCompletionStatus status;
+        status.error_code = net::ERR_ADDRESS_UNREACHABLE;
+        params->client->OnComplete(status);
+        return true;
+      },
+      gurl));
+
+  {
+    InSequence seq;
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(
+            cast_web_contents_.get(), CastWebContents::PageState::LOADING)));
+    EXPECT_CALL(mock_cast_wc_delegate_,
+                OnPageStopped(CheckPageState(cast_web_contents_.get(),
+                                             CastWebContents::PageState::ERROR),
+                              net::ERR_ADDRESS_UNREACHABLE))
+        .WillOnce(InvokeWithoutArgs(quit_closure));
+  }
+
+  cast_web_contents_->LoadUrl(gurl);
+  run_loop->Run();
+}
+
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest, LoadCanceledByApp) {
+  auto run_loop = std::make_unique<base::RunLoop>();
+  auto quit_closure = [&run_loop]() {
+    if (run_loop->running()) {
+      run_loop->QuitWhenIdle();
+    }
+  };
+
+  // ===========================================================================
+  // Test: When the app calls window.stop(), the page should not enter the ERROR
+  // state. Instead, we treat it as LOADED. This is a historical behavior for
+  // some apps which intentionally stop the page and reload content.
+  // ===========================================================================
+  embedded_test_server()->ServeFilesFromSourceDirectory(GetTestDataPath());
+  StartTestServer();
+
+  {
+    InSequence seq;
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(
+            cast_web_contents_.get(), CastWebContents::PageState::LOADING)));
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(cast_web_contents_.get(),
+                                          CastWebContents::PageState::LOADED)))
+        .WillOnce(InvokeWithoutArgs(quit_closure));
+  }
+
+  cast_web_contents_->LoadUrl(
+      embedded_test_server()->GetURL("/load_cancel.html"));
+  run_loop->Run();
+}
+
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest, NotifyMissingResource) {
+  auto run_loop = std::make_unique<base::RunLoop>();
+  auto quit_closure = [&run_loop]() {
+    if (run_loop->running()) {
+      run_loop->QuitWhenIdle();
+    }
+  };
+
+  // ===========================================================================
+  // Test: Loading a page with a missing resource should notify observers.
+  // ===========================================================================
+  {
+    InSequence seq;
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(
+            cast_web_contents_.get(), CastWebContents::PageState::LOADING)));
+    EXPECT_CALL(
+        mock_cast_wc_delegate_,
+        OnPageStateChanged(CheckPageState(cast_web_contents_.get(),
+                                          CastWebContents::PageState::LOADED)))
+        .WillOnce(InvokeWithoutArgs(quit_closure));
+  }
+  EXPECT_CALL(mock_cast_wc_observer_,
+              ResourceLoadFailed(cast_web_contents_.get()));
+
+  base::FilePath path = GetTestDataFilePath("missing_resource.html");
+  cast_web_contents_->LoadUrl(content::GetFileUrlWithQuery(path, ""));
+  run_loop->Run();
+}
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_CAST_WEB_CONTENTS_BROWSERTEST_H_
diff --git a/chromecast/browser/cast_web_contents_impl.cc b/chromecast/browser/cast_web_contents_impl.cc
index b961102..41ee8b4f 100644
--- a/chromecast/browser/cast_web_contents_impl.cc
+++ b/chromecast/browser/cast_web_contents_impl.cc
@@ -20,6 +20,7 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/common/bindings_policy.h"
+#include "content/public/common/resource_load_info.mojom.h"
 #include "net/base/net_errors.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
@@ -61,6 +62,7 @@
       remote_debugging_server_(
           shell::CastBrowserProcess::GetInstance()->remote_debugging_server()),
       tab_id_(init_params.is_root_window ? 0 : next_tab_id++),
+      main_frame_loaded_(false),
       closing_(false),
       stopped_(false),
       stop_notified_(false),
@@ -127,13 +129,8 @@
     LOG(ERROR) << "Cannot load URL for WebContents while closing";
     return;
   }
-  closing_ = false;
-  stopped_ = false;
-  stop_notified_ = false;
-  last_error_ = net::OK;
-  start_loading_ticks_ = base::TimeTicks::Now();
+  OnPageLoading();
   LOG(INFO) << "Load url: " << url.possibly_invalid_spec();
-  TracePageLoadBegin(url);
   web_contents_->GetController().LoadURL(url, content::Referrer(),
                                          ui::PAGE_TRANSITION_TYPED, "");
   UpdatePageState();
@@ -299,6 +296,29 @@
   Stop(net::ERR_UNEXPECTED);
 }
 
+void CastWebContentsImpl::DidStartNavigation(
+    content::NavigationHandle* navigation_handle) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(navigation_handle);
+  if (!web_contents_ || closing_ || stopped_)
+    return;
+  if (!navigation_handle->IsInMainFrame())
+    return;
+  // Main frame has begun navigating/loading.
+  OnPageLoading();
+  start_loading_ticks_ = base::TimeTicks::Now();
+  GURL loading_url;
+  content::NavigationEntry* nav_entry =
+      web_contents()->GetController().GetVisibleEntry();
+  if (nav_entry) {
+    loading_url = nav_entry->GetVirtualURL();
+  }
+  TracePageLoadBegin(loading_url);
+  UpdatePageState();
+  DCHECK_EQ(page_state_, PageState::LOADING);
+  NotifyObservers();
+}
+
 void CastWebContentsImpl::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -311,6 +331,10 @@
     return;
   }
 
+  // Return early if we didn't navigate to an error page. Note that even if we
+  // haven't navigated to an error page, there could still be errors in loading
+  // the desired content: e.g. if the server returned HTTP 404, or if there is
+  // an error with the content itself.
   if (!navigation_handle->IsErrorPage())
     return;
 
@@ -328,48 +352,91 @@
 
   LOG(ERROR) << "Got error on navigation: url=" << navigation_handle->GetURL()
              << ", error_code=" << error_code
-             << ", description= " << net::ErrorToShortString(error_code);
+             << ", description=" << net::ErrorToShortString(error_code);
 
   Stop(error_code);
   DCHECK_EQ(page_state_, PageState::ERROR);
 }
 
-void CastWebContentsImpl::DidStartLoading() {
+void CastWebContentsImpl::DidFinishLoad(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& validated_url) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  UpdatePageState();
-  DCHECK_EQ(page_state_, PageState::LOADING);
-  NotifyObservers();
-}
-
-void CastWebContentsImpl::DidStopLoading() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (page_state_ != PageState::LOADING || !web_contents_ ||
+      render_frame_host != web_contents_->GetMainFrame()) {
+    return;
+  }
+  // The main frame finished loading. Before proceeding, we need to verify that
+  // the loaded page is the one that was requested.
+  TracePageLoadEnd(validated_url);
   int http_status_code = 0;
-  GURL final_url;
   content::NavigationEntry* nav_entry =
       web_contents()->GetController().GetVisibleEntry();
   if (nav_entry) {
     http_status_code = nav_entry->GetHttpStatusCode();
-    final_url = nav_entry->GetVirtualURL();
   }
-  TracePageLoadEnd(final_url);
 
   if (http_status_code != 0 && http_status_code / 100 != 2) {
-    // We successfully loaded an error HTML page.
-    LOG(INFO) << "Failed loading page for: " << final_url
-              << "; http status code: " << http_status_code;
+    // An error HTML page was loaded instead of the content we requested.
+    LOG(ERROR) << "Failed loading page for: " << validated_url
+               << "; http status code: " << http_status_code;
     Stop(net::ERR_FAILED);
     DCHECK_EQ(page_state_, PageState::ERROR);
     return;
   }
-  // Main frame finished loading.
+  // Main frame finished loading properly.
   base::TimeDelta load_time = base::TimeTicks::Now() - start_loading_ticks_;
   LOG(INFO) << "Finished loading page after " << load_time.InMilliseconds()
-            << " ms, url=" << final_url;
-  PageState previous = page_state_;
+            << " ms, url=" << validated_url;
+  OnPageLoaded();
+}
+
+void CastWebContentsImpl::DidFailLoad(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& validated_url,
+    int error_code,
+    const base::string16& error_description) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Only report an error if we are the main frame.  See b/8433611.
+  if (render_frame_host->GetParent()) {
+    LOG(ERROR) << "Got error on sub-iframe: url=" << validated_url.spec()
+               << ", error=" << error_code;
+    return;
+  }
+  if (error_code == net::ERR_ABORTED) {
+    // ERR_ABORTED means download was aborted by the app, typically this happens
+    // when flinging URL for direct playback, the initial URLRequest gets
+    // cancelled/aborted and then the same URL is requested via the buffered
+    // data source for media::Pipeline playback.
+    LOG(INFO) << "Load canceled: url=" << validated_url.spec();
+
+    // We consider the page to be fully loaded in this case, since the app has
+    // intentionally entered this state. If the app wanted to stop, it would
+    // have called window.close() instead.
+    OnPageLoaded();
+    return;
+  }
+
+  LOG(ERROR) << "Got error on load: url=" << validated_url.spec()
+             << ", error_code=" << error_code;
+
+  TracePageLoadEnd(validated_url);
+  Stop(error_code);
+  DCHECK_EQ(PageState::ERROR, page_state_);
+}
+
+void CastWebContentsImpl::OnPageLoading() {
+  closing_ = false;
+  stopped_ = false;
+  stop_notified_ = false;
+  main_frame_loaded_ = false;
+  last_error_ = net::OK;
+}
+
+void CastWebContentsImpl::OnPageLoaded() {
+  main_frame_loaded_ = true;
   UpdatePageState();
-  DCHECK((previous == PageState::ERROR && page_state_ == PageState::ERROR) ||
-         page_state_ == PageState::LOADED)
-      << "Page is in unexpected state: " << page_state_;
+  DCHECK(page_state_ == PageState::LOADED);
   NotifyObservers();
 }
 
@@ -380,10 +447,10 @@
     DCHECK(stopped_);
     page_state_ = PageState::DESTROYED;
   } else if (!stopped_) {
-    if (web_contents_->IsLoading()) {
-      page_state_ = PageState::LOADING;
-    } else {
+    if (main_frame_loaded_) {
       page_state_ = PageState::LOADED;
+    } else {
+      page_state_ = PageState::LOADING;
     }
   } else if (stopped_) {
     if (last_error_ != net::OK) {
@@ -413,33 +480,21 @@
   notifying_ = false;
 }
 
-void CastWebContentsImpl::DidFailLoad(
+void CastWebContentsImpl::ResourceLoadComplete(
     content::RenderFrameHost* render_frame_host,
-    const GURL& validated_url,
-    int error_code,
-    const base::string16& error_description) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Only report an error if we are the main frame.  See b/8433611.
-  if (render_frame_host->GetParent()) {
-    LOG(ERROR) << "Got error on sub-iframe: url=" << validated_url.spec()
-               << ", error=" << error_code;
+    const content::GlobalRequestID& request_id,
+    const content::mojom::ResourceLoadInfo& resource_load_info) {
+  if (!web_contents_ || render_frame_host != web_contents_->GetMainFrame())
     return;
-  }
-  if (error_code == net::ERR_ABORTED) {
-    // ERR_ABORTED means download was aborted by the app, typically this happens
-    // when flinging URL for direct playback, the initial URLRequest gets
-    // cancelled/aborted and then the same URL is requested via the buffered
-    // data source for media::Pipeline playback.
-    LOG(INFO) << "Load canceled: url=" << validated_url.spec();
+  int net_error = resource_load_info.net_error;
+  if (net_error == net::OK)
     return;
+  LOG(ERROR) << "Resource \"" << resource_load_info.url << "\" failed to load "
+             << " with net_error=" << net_error
+             << ", description=" << net::ErrorToShortString(net_error);
+  for (auto& observer : observer_list_) {
+    observer.ResourceLoadFailed(this);
   }
-
-  LOG(ERROR) << "Got error on load: url=" << validated_url.spec()
-             << ", error_code=" << error_code;
-
-  TracePageLoadEnd(validated_url);
-  Stop(error_code);
-  DCHECK_EQ(PageState::ERROR, page_state_);
 }
 
 void CastWebContentsImpl::InnerWebContentsCreated(
diff --git a/chromecast/browser/cast_web_contents_impl.h b/chromecast/browser/cast_web_contents_impl.h
index e4cd03f..07f8008 100644
--- a/chromecast/browser/cast_web_contents_impl.h
+++ b/chromecast/browser/cast_web_contents_impl.h
@@ -57,26 +57,34 @@
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
 
- private:
-  // WebContentsObserver implementation:
+  // content::WebContentsObserver implementation:
   void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
   void OnInterfaceRequestFromFrame(
       content::RenderFrameHost* /* render_frame_host */,
       const std::string& interface_name,
       mojo::ScopedMessagePipeHandle* interface_pipe) override;
   void RenderProcessGone(base::TerminationStatus status) override;
+  void DidStartNavigation(
+      content::NavigationHandle* navigation_handle) override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
-  void DidStartLoading() override;
-  void DidStopLoading() override;
+  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
+                     const GURL& validated_url) override;
   void DidFailLoad(content::RenderFrameHost* render_frame_host,
                    const GURL& validated_url,
                    int error_code,
                    const base::string16& error_description) override;
+  void ResourceLoadComplete(
+      content::RenderFrameHost* render_frame_host,
+      const content::GlobalRequestID& request_id,
+      const content::mojom::ResourceLoadInfo& resource_load_info) override;
   void InnerWebContentsCreated(
       content::WebContents* inner_web_contents) override;
   void WebContentsDestroyed() override;
 
+ private:
+  void OnPageLoading();
+  void OnPageLoaded();
   void UpdatePageState();
   void NotifyObservers();
   void TracePageLoadBegin(const GURL& url);
@@ -98,6 +106,7 @@
 
   const int tab_id_;
   base::TimeTicks start_loading_ticks_;
+  bool main_frame_loaded_;
   bool closing_;
   bool stopped_;
   bool stop_notified_;
diff --git a/chromecast/browser/test/data/load_cancel.html b/chromecast/browser/test/data/load_cancel.html
new file mode 100644
index 0000000..d39e807
--- /dev/null
+++ b/chromecast/browser/test/data/load_cancel.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<head>
+</head>
+<body>
+  <script type="text/javascript">
+    window.stop();
+  </script>
+</body>
diff --git a/chromecast/browser/test/data/missing_resource.html b/chromecast/browser/test/data/missing_resource.html
new file mode 100644
index 0000000..36da320
--- /dev/null
+++ b/chromecast/browser/test/data/missing_resource.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<head>
+</head>
+<body>
+  <script type="text/javascript" src="this_file_does_not_exist.js"></script>
+</body>
diff --git a/chromecast/renderer/cast_content_renderer_client_simple.cc b/chromecast/renderer/cast_content_renderer_client_simple.cc
index 5932e01..315c5171 100644
--- a/chromecast/renderer/cast_content_renderer_client_simple.cc
+++ b/chromecast/renderer/cast_content_renderer_client_simple.cc
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
-#include "ipc/message_filter.h"
 
 namespace chromecast {
 namespace shell {
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index fc93f8e3..86b94e9 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -542,6 +542,11 @@
 // of network packets from whitelisted sources.
 const char kWakeOnWifiPacket[] = "wake-on-wifi-packet";
 
+// Prevents any CPU restrictions being set on the ARC container. Only meant to
+// be used by tests as some tests may time out if the ARC container is
+// throttled.
+const char kDisableArcCpuRestriction[] = "disable-arc-cpu-restriction";
+
 bool WakeOnWifiEnabled() {
   return !base::CommandLine::ForCurrentProcess()->HasSwitch(kDisableWakeOnWifi);
 }
@@ -650,5 +655,10 @@
       kDisableGaiaServices);
 }
 
+bool IsArcCpuRestrictionDisabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      kDisableArcCpuRestriction);
+}
+
 }  // namespace switches
 }  // namespace chromeos
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index 45e7b2af..aed69b8 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -185,6 +185,8 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kOobeSkipPostLogin[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kOobeSkipToLogin[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kOobeTimerInterval[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableArcCpuRestriction[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kProfileRequiresPolicy[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kRedirectLibassistantLogging[];
@@ -290,6 +292,9 @@
 // Returns true if GAIA services has been disabled.
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsGaiaServicesDisabled();
 
+// Returns true if |kDisableArcCpuRestriction| is true.
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsArcCpuRestrictionDisabled();
+
 }  // namespace switches
 }  // namespace chromeos
 
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index ae0a271..f08d9b4 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -171,6 +171,7 @@
     "//base",
     "//chromeos/constants",
     "//chromeos/dbus/session_manager",
+    "//components/exo",
     "//ui/aura",
   ]
 }
diff --git a/components/arc/arc_util.cc b/components/arc/arc_util.cc
index 62f9833..d6e0d2b 100644
--- a/components/arc/arc_util.cc
+++ b/components/arc/arc_util.cc
@@ -5,7 +5,7 @@
 #include "components/arc/arc_util.h"
 
 #include <algorithm>
-#include <string>
+#include <cstdio>
 
 #include "ash/public/cpp/app_types.h"
 #include "base/bind.h"
@@ -14,6 +14,7 @@
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
 #include "components/arc/arc_features.h"
+#include "components/exo/shell_surface_util.h"
 #include "components/user_manager/user_manager.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
@@ -177,11 +178,29 @@
          static_cast<int>(ash::AppType::ARC_APP);
 }
 
+int GetWindowTaskId(const aura::Window* window) {
+  const std::string* arc_app_id = exo::GetShellApplicationId(window);
+  if (!arc_app_id)
+    return kNoTaskId;
+  return GetTaskIdFromWindowAppId(*arc_app_id);
+}
+
+int GetTaskIdFromWindowAppId(const std::string& app_id) {
+  int task_id;
+  if (std::sscanf(app_id.c_str(), "org.chromium.arc.%d", &task_id) != 1)
+    return kNoTaskId;
+  return task_id;
+}
+
 void SetArcCpuRestriction(bool do_restrict) {
   if (!chromeos::SessionManagerClient::Get()) {
     LOG(WARNING) << "SessionManagerClient is not available";
     return;
   }
+
+  if (chromeos::switches::IsArcCpuRestrictionDisabled())
+    return;
+
   const login_manager::ContainerCpuRestrictionState state =
       do_restrict ? login_manager::CONTAINER_CPU_RESTRICTION_BACKGROUND
                   : login_manager::CONTAINER_CPU_RESTRICTION_FOREGROUND;
diff --git a/components/arc/arc_util.h b/components/arc/arc_util.h
index 0cb529de..bea5bf3 100644
--- a/components/arc/arc_util.h
+++ b/components/arc/arc_util.h
@@ -10,6 +10,7 @@
 // users' preferences, and FeatureList.
 
 #include <stdint.h>
+#include <string>
 
 namespace aura {
 class Window;
@@ -105,6 +106,13 @@
 // |window| is nullptr, returns false.
 bool IsArcAppWindow(const aura::Window* window);
 
+constexpr int kNoTaskId = -1;
+constexpr int kSystemWindowTaskId = 0;
+// Returns the task id given by the exo shell's application id, or |kNoTaskId|
+// if not an ARC window.
+int GetWindowTaskId(const aura::Window* window);
+int GetTaskIdFromWindowAppId(const std::string& app_id);
+
 // Returns true if ARC app icons are forced to cache.
 bool IsArcForceCacheAppIcon();
 
diff --git a/components/arc/ime/arc_ime_service.cc b/components/arc/ime/arc_ime_service.cc
index 54ba67f..a06cc25 100644
--- a/components/arc/ime/arc_ime_service.cc
+++ b/components/arc/ime/arc_ime_service.cc
@@ -13,7 +13,6 @@
 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
 #include "components/arc/arc_util.h"
 #include "components/arc/ime/arc_ime_bridge_impl.h"
-#include "components/exo/shell_surface_util.h"
 #include "components/exo/wm_helper.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
@@ -33,9 +32,6 @@
 
 namespace {
 
-// TODO(yhanada): Remove this once IsArcAppWindow is fixed for ARC++ Kiosk app.
-constexpr char kArcAppIdPrefix[] = "org.chromium.arc";
-
 base::Optional<double> g_override_default_device_scale_factor;
 
 double GetDefaultDeviceScaleFactor() {
@@ -66,13 +62,9 @@
       // TODO(yhanada): Make IsArcAppWindow support a window of ARC++ Kiosk.
       // Specifically, a window of ARC++ Kiosk should have ash::AppType::ARC_APP
       // property. Please see implementation of IsArcAppWindow().
-      if (window == active) {
-        const std::string* app_id = exo::GetShellApplicationId(window);
-        if (IsArcKioskMode() && app_id &&
-            base::StartsWith(*app_id, kArcAppIdPrefix,
-                             base::CompareCase::SENSITIVE)) {
-          return true;
-        }
+      if (window == active && IsArcKioskMode() &&
+          GetWindowTaskId(window) != kNoTaskId) {
+        return true;
       }
     }
     return false;
diff --git a/components/download/internal/common/BUILD.gn b/components/download/internal/common/BUILD.gn
index a403d87..e0b03cf 100644
--- a/components/download/internal/common/BUILD.gn
+++ b/components/download/internal/common/BUILD.gn
@@ -18,6 +18,7 @@
   ]
 
   sources = [
+    "all_download_event_notifier.cc",
     "base_file.cc",
     "base_file_win.cc",
     "download_create_info.cc",
diff --git a/components/download/internal/common/all_download_event_notifier.cc b/components/download/internal/common/all_download_event_notifier.cc
new file mode 100644
index 0000000..e28d8b0
--- /dev/null
+++ b/components/download/internal/common/all_download_event_notifier.cc
@@ -0,0 +1,88 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/download/public/common/all_download_event_notifier.h"
+
+namespace download {
+
+AllDownloadEventNotifier::AllDownloadEventNotifier(
+    SimpleDownloadManagerCoordinator* simple_download_manager_coordinator)
+    : simple_download_manager_coordinator_(simple_download_manager_coordinator),
+      download_initialized_(false) {
+  simple_download_manager_coordinator_->AddObserver(this);
+  std::vector<DownloadItem*> downloads;
+  simple_download_manager_coordinator_->GetAllDownloads(&downloads);
+  for (auto* download : downloads) {
+    download->AddObserver(this);
+    observing_.insert(download);
+  }
+}
+
+AllDownloadEventNotifier::~AllDownloadEventNotifier() {
+  if (simple_download_manager_coordinator_)
+    simple_download_manager_coordinator_->RemoveObserver(this);
+  for (auto it = observing_.begin(); it != observing_.end(); ++it) {
+    (*it)->RemoveObserver(this);
+  }
+  observing_.clear();
+}
+
+void AllDownloadEventNotifier::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+  if (download_initialized_) {
+    observer->OnDownloadsInitialized(
+        simple_download_manager_coordinator_,
+        !simple_download_manager_coordinator_->has_all_history_downloads());
+  }
+}
+
+void AllDownloadEventNotifier::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void AllDownloadEventNotifier::OnDownloadsInitialized(
+    bool active_downloads_only) {
+  download_initialized_ = true;
+  for (auto& observer : observers_)
+    observer.OnDownloadsInitialized(simple_download_manager_coordinator_,
+                                    active_downloads_only);
+}
+
+void AllDownloadEventNotifier::OnManagerGoingDown() {
+  for (auto& observer : observers_)
+    observer.OnManagerGoingDown(simple_download_manager_coordinator_);
+  simple_download_manager_coordinator_->RemoveObserver(this);
+  simple_download_manager_coordinator_ = nullptr;
+}
+
+void AllDownloadEventNotifier::OnDownloadCreated(DownloadItem* item) {
+  if (observing_.find(item) != observing_.end())
+    return;
+  item->AddObserver(this);
+  observing_.insert(item);
+  for (auto& observer : observers_)
+    observer.OnDownloadCreated(simple_download_manager_coordinator_, item);
+}
+
+void AllDownloadEventNotifier::OnDownloadUpdated(DownloadItem* item) {
+  for (auto& observer : observers_)
+    observer.OnDownloadUpdated(simple_download_manager_coordinator_, item);
+}
+
+void AllDownloadEventNotifier::OnDownloadOpened(DownloadItem* item) {
+  for (auto& observer : observers_)
+    observer.OnDownloadOpened(simple_download_manager_coordinator_, item);
+}
+
+void AllDownloadEventNotifier::OnDownloadRemoved(DownloadItem* item) {
+  for (auto& observer : observers_)
+    observer.OnDownloadRemoved(simple_download_manager_coordinator_, item);
+}
+
+void AllDownloadEventNotifier::OnDownloadDestroyed(DownloadItem* item) {
+  item->RemoveObserver(this);
+  observing_.erase(item);
+}
+
+}  // namespace download
diff --git a/components/download/internal/common/simple_download_manager_coordinator.cc b/components/download/internal/common/simple_download_manager_coordinator.cc
index e0c3038..ca14a7f4 100644
--- a/components/download/internal/common/simple_download_manager_coordinator.cc
+++ b/components/download/internal/common/simple_download_manager_coordinator.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "components/download/public/common/all_download_event_notifier.h"
 #include "components/download/public/common/download_item.h"
 #include "components/download/public/common/simple_download_manager.h"
 
@@ -82,4 +83,10 @@
     observer.OnDownloadCreated(item);
 }
 
+AllDownloadEventNotifier* SimpleDownloadManagerCoordinator::GetNotifier() {
+  if (!notifier_)
+    notifier_ = std::make_unique<AllDownloadEventNotifier>(this);
+  return notifier_.get();
+}
+
 }  // namespace download
diff --git a/components/download/public/common/BUILD.gn b/components/download/public/common/BUILD.gn
index 30d3d52..71b81807 100644
--- a/components/download/public/common/BUILD.gn
+++ b/components/download/public/common/BUILD.gn
@@ -15,6 +15,7 @@
 
 component("public") {
   sources = [
+    "all_download_event_notifier.h",
     "auto_resumption_handler.cc",
     "auto_resumption_handler.h",
     "base_file.h",
diff --git a/components/download/public/common/all_download_event_notifier.h b/components/download/public/common/all_download_event_notifier.h
new file mode 100644
index 0000000..90275c1
--- /dev/null
+++ b/components/download/public/common/all_download_event_notifier.h
@@ -0,0 +1,80 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DOWNLOAD_PUBLIC_COMMON_ALL_DOWNLOAD_EVENT_NOTIFIER_H_
+#define COMPONENTS_DOWNLOAD_PUBLIC_COMMON_ALL_DOWNLOAD_EVENT_NOTIFIER_H_
+
+#include <set>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/download/public/common/download_item.h"
+#include "components/download/public/common/simple_download_manager_coordinator.h"
+
+namespace download {
+
+// Observes all the download events from a single
+// SimpleDownloadManagerCoordinator.
+class AllDownloadEventNotifier
+    : public SimpleDownloadManagerCoordinator::Observer,
+      public DownloadItem::Observer {
+ public:
+  // All of the methods take the SimpleDownloadManagerCoordinator so that
+  // subclasses can observe multiple managers at once and easily distinguish
+  // which manager a given item belongs to.
+  class Observer {
+   public:
+    Observer() = default;
+    virtual ~Observer() = default;
+
+    virtual void OnDownloadsInitialized(
+        SimpleDownloadManagerCoordinator* coordinator,
+        bool active_downloads_only) {}
+    virtual void OnManagerGoingDown(
+        SimpleDownloadManagerCoordinator* coordinator) {}
+    virtual void OnDownloadCreated(SimpleDownloadManagerCoordinator* manager,
+                                   DownloadItem* item) {}
+    virtual void OnDownloadUpdated(SimpleDownloadManagerCoordinator* manager,
+                                   DownloadItem* item) {}
+    virtual void OnDownloadOpened(SimpleDownloadManagerCoordinator* manager,
+                                  DownloadItem* item) {}
+    virtual void OnDownloadRemoved(SimpleDownloadManagerCoordinator* manager,
+                                   DownloadItem* item) {}
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Observer);
+  };
+
+  explicit AllDownloadEventNotifier(SimpleDownloadManagerCoordinator* manager);
+  ~AllDownloadEventNotifier() override;
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+ private:
+  // SimpleDownloadManagerCoordinator::Observer
+  void OnDownloadsInitialized(bool active_downloads_only) override;
+  void OnManagerGoingDown() override;
+  void OnDownloadCreated(DownloadItem* item) override;
+
+  // DownloadItem::Observer
+  void OnDownloadUpdated(DownloadItem* item) override;
+  void OnDownloadOpened(DownloadItem* item) override;
+  void OnDownloadRemoved(DownloadItem* item) override;
+  void OnDownloadDestroyed(DownloadItem* item) override;
+
+  SimpleDownloadManagerCoordinator* simple_download_manager_coordinator_;
+  std::set<DownloadItem*> observing_;
+
+  bool download_initialized_;
+
+  // Observers that want to be notified of download events.
+  base::ObserverList<Observer>::Unchecked observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(AllDownloadEventNotifier);
+};
+
+}  // namespace download
+
+#endif  // COMPONENTS_DOWNLOAD_PUBLIC_COMMON_ALL_DOWNLOAD_EVENT_NOTIFIER_H_
diff --git a/components/download/public/common/simple_download_manager_coordinator.h b/components/download/public/common/simple_download_manager_coordinator.h
index f8cdf75..03ebf4e6 100644
--- a/components/download/public/common/simple_download_manager_coordinator.h
+++ b/components/download/public/common/simple_download_manager_coordinator.h
@@ -18,6 +18,7 @@
 
 namespace download {
 
+class AllDownloadEventNotifier;
 class DownloadItem;
 
 // This object allows swapping between different SimppleDownloadManager
@@ -65,6 +66,9 @@
   // Return whether this object has download manager set.
   bool HasSetDownloadManager();
 
+  // Returns a non-empty notifier to be used for observing download events.
+  AllDownloadEventNotifier* GetNotifier();
+
   bool has_all_history_downloads() const { return has_all_history_downloads_; }
 
  private:
@@ -77,6 +81,9 @@
 
   SimpleDownloadManager* simple_download_manager_;
 
+  // Object for notifying others about various download events.
+  std::unique_ptr<AllDownloadEventNotifier> notifier_;
+
   // Whether all the history downloads are ready.
   bool has_all_history_downloads_;
 
diff --git a/components/exo/display.cc b/components/exo/display.cc
index c2f533d..8337349 100644
--- a/components/exo/display.cc
+++ b/components/exo/display.cc
@@ -35,6 +35,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/wm/desks/desks_util.h"
 #include "components/exo/client_controlled_shell_surface.h"
 #include "components/exo/input_method_surface.h"
 #include "components/exo/shell_surface.h"
@@ -135,7 +136,7 @@
 
   return std::make_unique<ShellSurface>(
       surface, gfx::Point(), true /* activatable */, false /* can_minimize */,
-      ash::kShellWindowId_DefaultContainer);
+      ash::desks_util::GetActiveDeskContainerId());
 }
 
 std::unique_ptr<XdgShellSurface> Display::CreateXdgShellSurface(
@@ -149,7 +150,7 @@
 
   return std::make_unique<XdgShellSurface>(
       surface, gfx::Point(), true /* activatable */, false /* can_minimize */,
-      ash::kShellWindowId_DefaultContainer);
+      ash::desks_util::GetActiveDeskContainerId());
 }
 
 std::unique_ptr<ClientControlledShellSurface>
diff --git a/components/exo/display_unittest.cc b/components/exo/display_unittest.cc
index 922ee66..3ce4dcd8 100644
--- a/components/exo/display_unittest.cc
+++ b/components/exo/display_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/exo/display.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/wm/desks/desks_util.h"
 #include "components/exo/buffer.h"
 #include "components/exo/client_controlled_shell_surface.h"
 #include "components/exo/data_device.h"
@@ -128,7 +129,7 @@
   // Create a remote shell surface for surface2.
   std::unique_ptr<ShellSurfaceBase> shell_surface2 =
       display->CreateClientControlledShellSurface(
-          surface2.get(), ash::kShellWindowId_DefaultContainer,
+          surface2.get(), ash::desks_util::GetActiveDeskContainerId(),
           1.0 /* default_scale_factor */);
   EXPECT_TRUE(shell_surface2);
 }
diff --git a/components/exo/pointer_unittest.cc b/components/exo/pointer_unittest.cc
index 2b7a8bb..15d63a3e 100644
--- a/components/exo/pointer_unittest.cc
+++ b/components/exo/pointer_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_positioning_utils.h"
 #include "base/bind.h"
 #include "base/run_loop.h"
@@ -481,7 +482,7 @@
   std::unique_ptr<Surface> child_surface(new Surface);
   std::unique_ptr<ShellSurface> child_shell_surface(
       new ShellSurface(child_surface.get(), gfx::Point(9, 9), true, false,
-                       ash::kShellWindowId_DefaultContainer));
+                       ash::desks_util::GetActiveDeskContainerId()));
   child_shell_surface->DisableMovement();
   child_shell_surface->SetParent(shell_surface.get());
   gfx::Size child_buffer_size(15, 15);
diff --git a/components/exo/shell_surface.cc b/components/exo/shell_surface.cc
index aea68e11..4138a75 100644
--- a/components/exo/shell_surface.cc
+++ b/components/exo/shell_surface.cc
@@ -7,6 +7,7 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_state_type.h"
 #include "ash/shell.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/toplevel_window_event_handler.h"
 #include "ash/wm/window_resizer.h"
 #include "ash/wm/window_state.h"
@@ -137,7 +138,7 @@
                        gfx::Point(),
                        true,
                        true,
-                       ash::kShellWindowId_DefaultContainer) {}
+                       ash::desks_util::GetActiveDeskContainerId()) {}
 
 ShellSurface::~ShellSurface() {
   DCHECK(!scoped_configure_);
@@ -273,9 +274,9 @@
       parent ? views::Widget::GetTopLevelWidgetForNativeView(parent->window())
              : nullptr;
   if (parent_widget) {
-    // Set parent window if using default container and the container itself
-    // is not the parent.
-    if (container_ == ash::kShellWindowId_DefaultContainer)
+    // Set parent window if using one of the desks container and the container
+    // itself is not the parent.
+    if (ash::desks_util::IsDeskContainerId(container_))
       SetParentWindow(parent_widget->GetNativeWindow());
 
     origin_ = position;
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index afc54f2..0e134ba 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -13,6 +13,7 @@
 #include "ash/public/cpp/window_state_type.h"
 #include "ash/public/interfaces/window_pin_type.mojom.h"
 #include "ash/shell.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/drag_window_resizer.h"
 #include "ash/wm/window_resizer.h"
 #include "ash/wm/window_state.h"
@@ -673,7 +674,7 @@
 
 bool ShellSurfaceBase::CanMaximize() const {
   // Shell surfaces in system modal container cannot be maximized.
-  if (container_ != ash::kShellWindowId_DefaultContainer)
+  if (!ash::desks_util::IsDeskContainerId(container_))
     return false;
 
   // Non-transient shell surfaces can be maximized.
diff --git a/components/exo/surface.cc b/components/exo/surface.cc
index 06dc3f35..dcca225 100644
--- a/components/exo/surface.cc
+++ b/components/exo/surface.cc
@@ -50,6 +50,10 @@
 #include "ui/gfx/transform_util.h"
 #include "ui/views/widget/widget.h"
 
+#if defined(OS_CHROMEOS)
+#include "ash/wm/desks/desks_util.h"
+#endif  // defined(OS_CHROMEOS)
+
 DEFINE_UI_CLASS_PROPERTY_TYPE(exo::Surface*)
 
 namespace exo {
@@ -112,6 +116,14 @@
   NOTREACHED();
 }
 
+bool IsDeskContainer(aura::Window* container) {
+#if defined(OS_CHROMEOS)
+  return ash::desks_util::IsDeskContainer(container);
+#else
+  return container->id() == ash::kShellWindowId_DefaultContainerDeprecated;
+#endif  // defined(OS_CHROMEOS)
+}
+
 class CustomWindowDelegate : public aura::WindowDelegate {
  public:
   explicit CustomWindowDelegate(Surface* surface) : surface_(surface) {}
@@ -132,9 +144,7 @@
   int GetNonClientComponent(const gfx::Point& point) const override {
     views::Widget* widget =
         views::Widget::GetTopLevelWidgetForNativeView(surface_->window());
-    if (widget &&
-        widget->GetNativeView()->parent()->id() ==
-            ash::kShellWindowId_DefaultContainer &&
+    if (widget && IsDeskContainer(widget->GetNativeView()->parent()) &&
         surface_->HitTest(point)) {
       return HTCLIENT;
     }
diff --git a/components/exo/test/exo_test_helper.cc b/components/exo/test/exo_test_helper.cc
index ad160f7b..6a44e3f 100644
--- a/components/exo/test/exo_test_helper.cc
+++ b/components/exo/test/exo_test_helper.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_positioner.h"
 #include "ash/wm/window_positioning_utils.h"
 #include "components/exo/buffer.h"
@@ -30,7 +31,7 @@
                              bool is_modal) {
   surface_.reset(new Surface());
   int container = is_modal ? ash::kShellWindowId_SystemModalContainer
-                           : ash::kShellWindowId_DefaultContainer;
+                           : ash::desks_util::GetActiveDeskContainerId();
   shell_surface_ = std::make_unique<ShellSurface>(surface_.get(), gfx::Point(),
                                                   true, false, container);
 
@@ -84,7 +85,7 @@
 ExoTestHelper::CreateClientControlledShellSurface(Surface* surface,
                                                   bool is_modal) {
   int container = is_modal ? ash::kShellWindowId_SystemModalContainer
-                           : ash::kShellWindowId_DefaultContainer;
+                           : ash::desks_util::GetActiveDeskContainerId();
   return Display().CreateClientControlledShellSurface(
       surface, container,
       WMHelper::GetInstance()->GetDefaultDeviceScaleFactor());
diff --git a/components/exo/wayland/zaura_shell_unittest.cc b/components/exo/wayland/zaura_shell_unittest.cc
index 1b16962..938ef83 100644
--- a/components/exo/wayland/zaura_shell_unittest.cc
+++ b/components/exo/wayland/zaura_shell_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_util.h"
 #include "base/time/time.h"
 #include "components/exo/test/exo_test_base.h"
@@ -113,7 +114,7 @@
   std::unique_ptr<views::Widget> CreateOpaqueWidget(const gfx::Rect& bounds) {
     return CreateTestWidget(
         /*delegate=*/nullptr,
-        /*container_id=*/ash::kShellWindowId_DefaultContainer, bounds,
+        /*container_id=*/ash::desks_util::GetActiveDeskContainerId(), bounds,
         /*show=*/false);
   }
 
diff --git a/components/exo/wayland/zcr_remote_shell.cc b/components/exo/wayland/zcr_remote_shell.cc
index 8a6a5d2..9aac427c 100644
--- a/components/exo/wayland/zcr_remote_shell.cc
+++ b/components/exo/wayland/zcr_remote_shell.cc
@@ -14,6 +14,7 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shell.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/tablet_mode/tablet_mode_observer.h"
 #include "ash/wm/window_resizer.h"
 #include "ash/wm/window_state.h"
@@ -839,12 +840,12 @@
 int RemoteSurfaceContainer(uint32_t container) {
   switch (container) {
     case ZCR_REMOTE_SHELL_V1_CONTAINER_DEFAULT:
-      return ash::kShellWindowId_DefaultContainer;
+      return ash::desks_util::GetActiveDeskContainerId();
     case ZCR_REMOTE_SHELL_V1_CONTAINER_OVERLAY:
       return ash::kShellWindowId_SystemModalContainer;
     default:
       DLOG(WARNING) << "Unsupported container: " << container;
-      return ash::kShellWindowId_DefaultContainer;
+      return ash::desks_util::GetActiveDeskContainerId();
   }
 }
 
diff --git a/components/infobars/core/infobar_delegate.h b/components/infobars/core/infobar_delegate.h
index 6b19481..91f035b8 100644
--- a/components/infobars/core/infobar_delegate.h
+++ b/components/infobars/core/infobar_delegate.h
@@ -160,6 +160,7 @@
     INLINE_UPDATE_READY_INFOBAR_ANDROID = 89,
     INLINE_UPDATE_FAILED_INFOBAR_ANDROID = 90,
     FLASH_DEPRECATION_INFOBAR_DELEGATE = 91,
+    SEND_TAB_TO_SELF_INFOBAR_DELEGATE = 92,
   };
 
   // Describes navigation events, used to decide whether infobars should be
diff --git a/components/pdf/renderer/pdf_accessibility_tree.cc b/components/pdf/renderer/pdf_accessibility_tree.cc
index 33ea39c7..358d052 100644
--- a/components/pdf/renderer/pdf_accessibility_tree.cc
+++ b/components/pdf/renderer/pdf_accessibility_tree.cc
@@ -224,6 +224,14 @@
 }
 
 void PdfAccessibilityTree::UpdateAXTreeDataFromSelection() {
+  tree_data_.sel_is_backward = false;
+  if (selection_start_page_index_ > selection_end_page_index_) {
+    tree_data_.sel_is_backward = true;
+  } else if (selection_start_page_index_ == selection_end_page_index_ &&
+             selection_start_char_index_ > selection_end_char_index_) {
+    tree_data_.sel_is_backward = true;
+  }
+
   FindNodeOffset(selection_start_page_index_, selection_start_char_index_,
                  &tree_data_.sel_anchor_object_id,
                  &tree_data_.sel_anchor_offset);
@@ -420,6 +428,7 @@
 //
 
 bool PdfAccessibilityTree::GetTreeData(ui::AXTreeData* tree_data) const {
+  tree_data->sel_is_backward = tree_data_.sel_is_backward;
   tree_data->sel_anchor_object_id = tree_data_.sel_anchor_object_id;
   tree_data->sel_anchor_offset = tree_data_.sel_anchor_offset;
   tree_data->sel_focus_object_id = tree_data_.sel_focus_object_id;
diff --git a/components/safe_browsing/password_protection/password_protection_request.cc b/components/safe_browsing/password_protection/password_protection_request.cc
index 7402f85..a3c9f79 100644
--- a/components/safe_browsing/password_protection/password_protection_request.cc
+++ b/components/safe_browsing/password_protection/password_protection_request.cc
@@ -247,8 +247,10 @@
   content::RenderWidgetHostView* view =
       web_contents_ ? web_contents_->GetRenderWidgetHostView() : nullptr;
 
-  if (!view)
+  if (!view) {
     SendRequest();
+    return;
+  }
 
   visual_feature_start_time_ = base::TimeTicks::Now();
 
diff --git a/components/safe_browsing/web_ui/safe_browsing_ui.cc b/components/safe_browsing/web_ui/safe_browsing_ui.cc
index 340eca0..75b72ea6 100644
--- a/components/safe_browsing/web_ui/safe_browsing_ui.cc
+++ b/components/safe_browsing/web_ui/safe_browsing_ui.cc
@@ -1180,6 +1180,7 @@
   if (!provider) {
     AllowJavascript();
     ResolveJavascriptCallback(base::Value(callback_id), base::Value(""));
+    return;
   }
 
   ReferrerChain referrer_chain;
diff --git a/components/send_tab_to_self/BUILD.gn b/components/send_tab_to_self/BUILD.gn
index 7a9d8a73..0c44fc7 100644
--- a/components/send_tab_to_self/BUILD.gn
+++ b/components/send_tab_to_self/BUILD.gn
@@ -28,6 +28,13 @@
   public_deps = [
     "//components/send_tab_to_self/proto:send_tab_to_self_proto",
   ]
+  if (is_ios || is_android) {
+    sources += [
+      "send_tab_to_self_infobar_delegate.cc",
+      "send_tab_to_self_infobar_delegate.h",
+    ]
+    deps += [ "//components/infobars/core" ]
+  }
 }
 
 source_set("test_support") {
diff --git a/components/send_tab_to_self/DEPS b/components/send_tab_to_self/DEPS
index 8da4a5b5..615aeb3 100644
--- a/components/send_tab_to_self/DEPS
+++ b/components/send_tab_to_self/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/infobars",
   "+components/keyed_service/core",
   "+components/sync",
   "+components/version_info",
diff --git a/components/send_tab_to_self/send_tab_to_self_infobar_delegate.cc b/components/send_tab_to_self/send_tab_to_self_infobar_delegate.cc
new file mode 100644
index 0000000..9072e11
--- /dev/null
+++ b/components/send_tab_to_self/send_tab_to_self_infobar_delegate.cc
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/send_tab_to_self/send_tab_to_self_infobar_delegate.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/send_tab_to_self/send_tab_to_self_entry.h"
+#include "url/gurl.h"
+
+namespace send_tab_to_self {
+
+SendTabToSelfInfoBarDelegate::SendTabToSelfInfoBarDelegate(
+    const SendTabToSelfEntry* entry) {
+  entry_ = entry;
+}
+
+std::unique_ptr<SendTabToSelfInfoBarDelegate>
+SendTabToSelfInfoBarDelegate::Create(const SendTabToSelfEntry* entry) {
+  return base::WrapUnique(new SendTabToSelfInfoBarDelegate(entry));
+}
+
+SendTabToSelfInfoBarDelegate::~SendTabToSelfInfoBarDelegate() {}
+
+base::string16 SendTabToSelfInfoBarDelegate::GetInfobarMessage() const {
+  // TODO(crbug.com/944602): Define real string.
+  NOTIMPLEMENTED();
+  return base::UTF8ToUTF16("Open");
+}
+
+void SendTabToSelfInfoBarDelegate::OpenTab() {
+  NOTIMPLEMENTED();
+}
+
+void SendTabToSelfInfoBarDelegate::InfoBarDismissed() {
+  NOTIMPLEMENTED();
+}
+
+infobars::InfoBarDelegate::InfoBarIdentifier
+SendTabToSelfInfoBarDelegate::GetIdentifier() const {
+  return SEND_TAB_TO_SELF_INFOBAR_DELEGATE;
+}
+
+}  // namespace send_tab_to_self
diff --git a/components/send_tab_to_self/send_tab_to_self_infobar_delegate.h b/components/send_tab_to_self/send_tab_to_self_infobar_delegate.h
new file mode 100644
index 0000000..280aaa89
--- /dev/null
+++ b/components/send_tab_to_self/send_tab_to_self_infobar_delegate.h
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_INFOBAR_DELEGATE_H_
+#define COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_INFOBAR_DELEGATE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "components/infobars/core/infobar_delegate.h"
+
+namespace send_tab_to_self {
+
+class SendTabToSelfEntry;
+
+// Delegate containing logic about what to display and how to behave
+// in the SendTabToSelf infobar. Used across Android and iOS.
+// TODO(crbug.com/
+class SendTabToSelfInfoBarDelegate : public infobars::InfoBarDelegate {
+ public:
+  static std::unique_ptr<SendTabToSelfInfoBarDelegate> Create(
+      const SendTabToSelfEntry* entry);
+  ~SendTabToSelfInfoBarDelegate() override;
+
+  // Returns the message to be shown in the infobar.
+  base::string16 GetInfobarMessage() const;
+
+  // Opens a tab to the url of the shared |entry_|.
+  void OpenTab();
+
+  // InfoBarDelegate:
+  void InfoBarDismissed() override;
+  infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
+
+ private:
+  SendTabToSelfInfoBarDelegate(const SendTabToSelfEntry* entry);
+
+  // The entry that was share to this device. Must outlive this instance.
+  const SendTabToSelfEntry* entry_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(SendTabToSelfInfoBarDelegate);
+};
+
+}  // namespace send_tab_to_self
+
+#endif  // COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_INFOBAR_DELEGATE_H_
diff --git a/components/user_manager/known_user.cc b/components/user_manager/known_user.cc
index 52c554b..74ebcdb 100644
--- a/components/user_manager/known_user.cc
+++ b/components/user_manager/known_user.cc
@@ -143,6 +143,19 @@
                  AccountId::AccountTypeToString(account_id.GetAccountType()));
 }
 
+void ClearPref(const AccountId& account_id, const std::string& path) {
+  const base::DictionaryValue* user_pref_dict = nullptr;
+  if (!FindPrefs(account_id, &user_pref_dict))
+    return;
+
+  base::Value updated_user_pref = user_pref_dict->Clone();
+  base::DictionaryValue* updated_user_pref_dict;
+  updated_user_pref.GetAsDictionary(&updated_user_pref_dict);
+
+  updated_user_pref_dict->RemovePath(path);
+  UpdatePrefs(account_id, *updated_user_pref_dict, true);
+}
+
 }  // namespace
 
 bool FindPrefs(const AccountId& account_id,
@@ -285,16 +298,7 @@
   for (const std::string& key : kReservedKeys)
     CHECK_NE(path, key);
 
-  const base::DictionaryValue* user_pref_dict = nullptr;
-  if (!FindPrefs(account_id, &user_pref_dict))
-    return;
-
-  base::Value updated_user_pref = user_pref_dict->Clone();
-  base::DictionaryValue* updated_user_pref_dict;
-  updated_user_pref.GetAsDictionary(&updated_user_pref_dict);
-
-  updated_user_pref_dict->RemovePath(path);
-  UpdatePrefs(account_id, *updated_user_pref_dict, true);
+  ClearPref(account_id, path);
 }
 
 AccountId GetAccountId(const std::string& user_email,
@@ -531,6 +535,10 @@
   return ProfileRequiresPolicy::kUnknown;
 }
 
+void ClearProfileRequiresPolicy(const AccountId& account_id) {
+  ClearPref(account_id, kProfileRequiresPolicy);
+}
+
 void UpdateReauthReason(const AccountId& account_id, const int reauth_reason) {
   SetIntegerPref(account_id, kReauthReasonKey, reauth_reason);
 }
diff --git a/components/user_manager/known_user.h b/components/user_manager/known_user.h
index 1c65bf88..b703c3e9 100644
--- a/components/user_manager/known_user.h
+++ b/components/user_manager/known_user.h
@@ -172,6 +172,10 @@
 SetProfileRequiresPolicy(const AccountId& account_id,
                          ProfileRequiresPolicy policy_required);
 
+// Clears information whether profile requires policy.
+void USER_MANAGER_EXPORT
+ClearProfileRequiresPolicy(const AccountId& account_id);
+
 // Saves why the user has to go through re-auth flow.
 void USER_MANAGER_EXPORT UpdateReauthReason(const AccountId& account_id,
                                             const int reauth_reason);
diff --git a/components/user_manager/user.cc b/components/user_manager/user.cc
index a1f9589..a23a0ae 100644
--- a/components/user_manager/user.cc
+++ b/components/user_manager/user.cc
@@ -14,6 +14,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_restrictions.h"
 #include "components/account_id/account_id.h"
+#include "components/user_manager/known_user.h"
 #include "components/user_manager/user_manager.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 
@@ -382,6 +383,11 @@
       return;
     const bool old_is_child = is_child_;
     is_child_ = user_type == user_manager::USER_TYPE_CHILD;
+
+    // Clear information about profile policy requirements to enforce setting it
+    // again for the new account type.
+    user_manager::known_user::ClearProfileRequiresPolicy(GetAccountId());
+
     LOG(WARNING) << "User type has changed: " << current_type
                  << " (is_child=" << old_is_child << ") => " << user_type
                  << " (is_child=" << is_child_ << ")";
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index 73929b3..d0adc36d 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -1155,5 +1155,13 @@
   return Response::Error("Unidentified lifecycle state");
 }
 
+void PageHandler::GetInstallabilityErrors(
+    std::unique_ptr<GetInstallabilityErrorsCallback> callback) {
+  auto errors = protocol::Array<std::string>::create();
+  // TODO: Use InstallableManager once it moves into content/.
+  // Until then, this code is only used to return empty array in the tests.
+  callback->sendSuccess(std::move(errors));
+}
+
 }  // namespace protocol
 }  // namespace content
diff --git a/content/browser/devtools/protocol/page_handler.h b/content/browser/devtools/protocol/page_handler.h
index b3f45b3..4bee8ba 100644
--- a/content/browser/devtools/protocol/page_handler.h
+++ b/content/browser/devtools/protocol/page_handler.h
@@ -155,6 +155,8 @@
       std::unique_ptr<GetAppManifestCallback> callback) override;
 
   Response SetWebLifecycleState(const std::string& state) override;
+  void GetInstallabilityErrors(
+      std::unique_ptr<GetInstallabilityErrorsCallback> callback) override;
 
  private:
   enum EncodingFormat { PNG, JPEG };
diff --git a/content/browser/devtools/protocol/target_handler.cc b/content/browser/devtools/protocol/target_handler.cc
index b475eff..3ae38448 100644
--- a/content/browser/devtools/protocol/target_handler.cc
+++ b/content/browser/devtools/protocol/target_handler.cc
@@ -203,8 +203,9 @@
 
     std::string encoded;
     base::Base64Encode(message, &encoded);
-    std::string eval_code = "window." + binding_name_ + ".onmessage(atob(\"";
-    std::string eval_suffix = "\"))";
+    std::string eval_code =
+        "try { window." + binding_name_ + ".onmessage(atob(\"";
+    std::string eval_suffix = "\")); } catch(e) { console.error(e); }";
     eval_code.reserve(eval_code.size() + encoded.size() + eval_suffix.size());
     eval_code.append(encoded);
     eval_code.append(eval_suffix);
diff --git a/content/browser/devtools/protocol_config.json b/content/browser/devtools/protocol_config.json
index cdb51646a..3e9a571 100644
--- a/content/browser/devtools/protocol_config.json
+++ b/content/browser/devtools/protocol_config.json
@@ -57,9 +57,9 @@
                 "domain": "Page",
                 "include": ["enable", "disable", "reload", "navigate", "stopLoading", "getNavigationHistory", "navigateToHistoryEntry", "resetNavigationHistory", "captureScreenshot",
                     "startScreencast", "stopScreencast", "screencastFrameAck", "handleJavaScriptDialog", "setColorPickerEnabled",
-                    "printToPDF", "bringToFront", "setDownloadBehavior", "getAppManifest", "crash", "close", "setWebLifecycleState", "captureSnapshot"],
+                    "printToPDF", "bringToFront", "setDownloadBehavior", "getAppManifest", "crash", "close", "setWebLifecycleState", "captureSnapshot", "getInstallabilityErrors"],
                 "include_events": ["colorPicked", "interstitialShown", "interstitialHidden", "javascriptDialogOpening", "javascriptDialogClosed", "screencastVisibilityChanged", "screencastFrame"],
-                "async": ["captureScreenshot", "printToPDF", "navigate", "getAppManifest", "reload", "captureSnapshot"]
+                "async": ["captureScreenshot", "printToPDF", "navigate", "getAppManifest", "reload", "captureSnapshot", "getInstallabilityErrors"]
             },
             {
                 "domain": "Runtime",
diff --git a/content/browser/renderer_host/direct_manipulation_event_handler_win.cc b/content/browser/renderer_host/direct_manipulation_event_handler_win.cc
index eec54fc..c98d4507 100644
--- a/content/browser/renderer_host/direct_manipulation_event_handler_win.cc
+++ b/content/browser/renderer_host/direct_manipulation_event_handler_win.cc
@@ -28,19 +28,15 @@
 }  // namespace
 
 DirectManipulationEventHandler::DirectManipulationEventHandler(
-    DirectManipulationHelper* helper)
-    : helper_(helper) {}
+    ui::WindowEventTarget* event_target)
+    : event_target_(event_target) {}
 
-void DirectManipulationEventHandler::SetWindowEventTarget(
-    ui::WindowEventTarget* event_target) {
-  if (!event_target && LoggingEnabled()) {
-    DebugLogging("Event target is null.", S_OK);
-    if (event_target_)
-      DebugLogging("Previous event target is not null", S_OK);
-    else
-      DebugLogging("Previous event target is null", S_OK);
-  }
-  event_target_ = event_target;
+bool DirectManipulationEventHandler::SetViewportSizeInPixels(
+    const gfx::Size& viewport_size_in_pixels) {
+  if (viewport_size_in_pixels_ == viewport_size_in_pixels)
+    return false;
+  viewport_size_in_pixels_ = viewport_size_in_pixels;
+  return true;
 }
 
 void DirectManipulationEventHandler::SetDeviceScaleFactor(
@@ -175,19 +171,29 @@
   if (current != DIRECTMANIPULATION_READY)
     return S_OK;
 
-  // Reset the viewport when we're idle, so the content transforms always start
-  // at identity.
-  // Every animation will receive 2 ready message, we should stop request
-  // compositor animation at the second ready.
-  first_ready_ = !first_ready_;
-  HRESULT hr = helper_->Reset(first_ready_);
+  // Normally gesture sequence will receive 2 READY message, the first one is
+  // gesture end, the second one is from viewport reset. We don't have content
+  // transform in the second RUNNING -> READY. We should not reset on an empty
+  // RUNNING -> READY sequence.
+  if (!FloatEquals(1.0f, last_scale_) || last_x_offset_ != 0 ||
+      last_y_offset_ != 0) {
+    HRESULT hr = viewport->ZoomToRect(
+        static_cast<float>(0), static_cast<float>(0),
+        static_cast<float>(viewport_size_in_pixels_.width()),
+        static_cast<float>(viewport_size_in_pixels_.height()), FALSE);
+    if (!SUCCEEDED(hr)) {
+      DebugLogging("Viewport zoom to rect failed.", hr);
+      return hr;
+    }
+  }
+
   last_scale_ = 1.0f;
   last_x_offset_ = 0.0f;
   last_y_offset_ = 0.0f;
 
   TransitionToState(GestureState::kNone);
 
-  return hr;
+  return S_OK;
 }
 
 HRESULT DirectManipulationEventHandler::OnViewportUpdated(
diff --git a/content/browser/renderer_host/direct_manipulation_event_handler_win.h b/content/browser/renderer_host/direct_manipulation_event_handler_win.h
index 270e85a..e06c680 100644
--- a/content/browser/renderer_host/direct_manipulation_event_handler_win.h
+++ b/content/browser/renderer_host/direct_manipulation_event_handler_win.h
@@ -11,6 +11,7 @@
 #include <wrl.h>
 
 #include "base/macros.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace ui {
 
@@ -20,7 +21,6 @@
 
 namespace content {
 
-class DirectManipulationHelper;
 class DirectManipulationUnitTest;
 
 // DirectManipulationEventHandler receives status update and gesture events from
@@ -35,11 +35,10 @@
               Microsoft::WRL::FtmBase,
               IDirectManipulationViewportEventHandler>> {
  public:
-  explicit DirectManipulationEventHandler(DirectManipulationHelper* helper);
+  explicit DirectManipulationEventHandler(ui::WindowEventTarget* event_target);
 
-  // WindowEventTarget updates for every DM_POINTERHITTEST in case window
-  // hierarchy changed.
-  void SetWindowEventTarget(ui::WindowEventTarget* event_target);
+  // Return true if viewport_size_in_pixels_ changed.
+  bool SetViewportSizeInPixels(const gfx::Size& viewport_size_in_pixels);
 
   void SetDeviceScaleFactor(float device_scale_factor);
 
@@ -65,18 +64,18 @@
   OnContentUpdated(_In_ IDirectManipulationViewport* viewport,
                    _In_ IDirectManipulationContent* content) override;
 
-  DirectManipulationHelper* helper_ = nullptr;
   ui::WindowEventTarget* event_target_ = nullptr;
   float device_scale_factor_ = 1.0f;
   float last_scale_ = 1.0f;
   int last_x_offset_ = 0;
   int last_y_offset_ = 0;
-  bool first_ready_ = false;
   bool should_send_scroll_begin_ = false;
 
   // Current recognized gesture from Direct Manipulation.
   GestureState gesture_state_ = GestureState::kNone;
 
+  gfx::Size viewport_size_in_pixels_;
+
   DISALLOW_COPY_AND_ASSIGN(DirectManipulationEventHandler);
 };
 
diff --git a/content/browser/renderer_host/direct_manipulation_helper_win.cc b/content/browser/renderer_host/direct_manipulation_helper_win.cc
index d04c3e1..c05fd59 100644
--- a/content/browser/renderer_host/direct_manipulation_helper_win.cc
+++ b/content/browser/renderer_host/direct_manipulation_helper_win.cc
@@ -13,6 +13,8 @@
 #include "base/win/windows_version.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/base/win/window_event_target.h"
+#include "ui/compositor/compositor.h"
+#include "ui/compositor/compositor_animation_observer.h"
 #include "ui/display/win/screen_win.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -38,8 +40,9 @@
 // static
 std::unique_ptr<DirectManipulationHelper>
 DirectManipulationHelper::CreateInstance(HWND window,
+                                         ui::Compositor* compositor,
                                          ui::WindowEventTarget* event_target) {
-  if (!::IsWindow(window))
+  if (!::IsWindow(window) || !compositor || !event_target)
     return nullptr;
 
   if (!base::FeatureList::IsEnabled(features::kPrecisionTouchpad))
@@ -50,8 +53,7 @@
     return nullptr;
 
   std::unique_ptr<DirectManipulationHelper> instance =
-      base::WrapUnique(new DirectManipulationHelper());
-  instance->window_ = window;
+      base::WrapUnique(new DirectManipulationHelper(window, compositor));
 
   if (instance->Initialize(event_target))
     return instance;
@@ -72,11 +74,10 @@
     return nullptr;
 
   std::unique_ptr<DirectManipulationHelper> instance =
-      base::WrapUnique(new DirectManipulationHelper());
+      base::WrapUnique(new DirectManipulationHelper(0, nullptr));
 
   instance->event_handler_ =
-      Microsoft::WRL::Make<DirectManipulationEventHandler>(instance.get());
-  instance->event_handler_->SetWindowEventTarget(event_target);
+      Microsoft::WRL::Make<DirectManipulationEventHandler>(event_target);
 
   instance->viewport_ = viewport;
 
@@ -84,11 +85,25 @@
 }
 
 DirectManipulationHelper::~DirectManipulationHelper() {
-  if (viewport_)
-    viewport_->Abandon();
+  Destroy();
 }
 
-DirectManipulationHelper::DirectManipulationHelper() {}
+DirectManipulationHelper::DirectManipulationHelper(HWND window,
+                                                   ui::Compositor* compositor)
+    : window_(window), compositor_(compositor) {}
+
+void DirectManipulationHelper::OnAnimationStep(base::TimeTicks timestamp) {
+  // Simulate 1 frame in update_manager_.
+  HRESULT hr = update_manager_->Update(nullptr);
+  if (!SUCCEEDED(hr))
+    DebugLogging("UpdateManager update failed.", hr);
+}
+
+void DirectManipulationHelper::OnCompositingShuttingDown(
+    ui::Compositor* compositor) {
+  DCHECK_EQ(compositor, compositor_);
+  Destroy();
+}
 
 bool DirectManipulationHelper::Initialize(ui::WindowEventTarget* event_target) {
   // IDirectManipulationUpdateManager is the first COM object created by the
@@ -141,8 +156,8 @@
     return false;
   }
 
-  event_handler_ = Microsoft::WRL::Make<DirectManipulationEventHandler>(this);
-  event_handler_->SetWindowEventTarget(event_target);
+  event_handler_ =
+      Microsoft::WRL::Make<DirectManipulationEventHandler>(event_target);
 
   // We got Direct Manipulation transform from
   // IDirectManipulationViewportEventHandler.
@@ -154,8 +169,9 @@
   }
 
   // Set default rect for viewport before activate.
-  viewport_size_in_pixels_ = {1000, 1000};
-  RECT rect = gfx::Rect(viewport_size_in_pixels_).ToRECT();
+  gfx::Size viewport_size_in_pixels = {1000, 1000};
+  event_handler_->SetViewportSizeInPixels(viewport_size_in_pixels);
+  RECT rect = gfx::Rect(viewport_size_in_pixels).ToRECT();
   hr = viewport_->SetViewportRect(&rect);
   if (!SUCCEEDED(hr)) {
     DebugLogging("Viewport set rect failed.", hr);
@@ -180,37 +196,16 @@
     return false;
   }
 
+  DCHECK(compositor_);
+  compositor_->AddAnimationObserver(this);
+
   DebugLogging("DirectManipulation initialization complete", S_OK);
   return true;
 }
 
-void DirectManipulationHelper::Activate() {
-  HRESULT hr = viewport_->Stop();
-  if (!SUCCEEDED(hr)) {
-    DebugLogging("Viewport stop failed.", hr);
-    return;
-  }
-
-  hr = manager_->Activate(window_);
-  if (!SUCCEEDED(hr))
-    DebugLogging("DirectManipulationManager activate failed.", hr);
-}
-
-void DirectManipulationHelper::Deactivate() {
-  HRESULT hr = viewport_->Stop();
-  if (!SUCCEEDED(hr)) {
-    DebugLogging("Viewport stop failed.", hr);
-    return;
-  }
-
-  hr = manager_->Deactivate(window_);
-  if (!SUCCEEDED(hr))
-    DebugLogging("DirectManipulationManager deactivate failed.", hr);
-}
-
 void DirectManipulationHelper::SetSizeInPixels(
     const gfx::Size& size_in_pixels) {
-  if (viewport_size_in_pixels_ == size_in_pixels)
+  if (!event_handler_->SetViewportSizeInPixels(size_in_pixels))
     return;
 
   HRESULT hr = viewport_->Stop();
@@ -219,16 +214,13 @@
     return;
   }
 
-  viewport_size_in_pixels_ = size_in_pixels;
-  RECT rect = gfx::Rect(viewport_size_in_pixels_).ToRECT();
+  RECT rect = gfx::Rect(size_in_pixels).ToRECT();
   hr = viewport_->SetViewportRect(&rect);
   if (!SUCCEEDED(hr))
     DebugLogging("Viewport set rect failed.", hr);
 }
 
-bool DirectManipulationHelper::OnPointerHitTest(
-    WPARAM w_param,
-    ui::WindowEventTarget* event_target) {
+void DirectManipulationHelper::OnPointerHitTest(WPARAM w_param) {
   // Update the device scale factor.
   event_handler_->SetDeviceScaleFactor(
       display::win::ScreenWin::GetScaleFactorForHWND(window_));
@@ -239,53 +231,50 @@
   // For WM_POINTER, the pointer type will show the event from mouse.
   // For WM_POINTERACTIVATE, the pointer id will be different with the following
   // message.
-  event_handler_->SetWindowEventTarget(event_target);
-
   using GetPointerTypeFn = BOOL(WINAPI*)(UINT32, POINTER_INPUT_TYPE*);
   UINT32 pointer_id = GET_POINTERID_WPARAM(w_param);
   POINTER_INPUT_TYPE pointer_type;
   static GetPointerTypeFn get_pointer_type = reinterpret_cast<GetPointerTypeFn>(
       GetProcAddress(GetModuleHandleA("user32.dll"), "GetPointerType"));
   if (get_pointer_type && get_pointer_type(pointer_id, &pointer_type) &&
-      pointer_type == PT_TOUCHPAD && event_target) {
+      pointer_type == PT_TOUCHPAD) {
     HRESULT hr = viewport_->SetContact(pointer_id);
-    if (!SUCCEEDED(hr)) {
+    if (!SUCCEEDED(hr))
       DebugLogging("Viewport set contact failed.", hr);
-      return false;
-    }
-
-    // Request begin frame for fake viewport.
-    need_poll_events_ = true;
   }
-  return need_poll_events_;
-}
-
-HRESULT DirectManipulationHelper::Reset(bool need_poll_events) {
-  // By zooming the primary content to a rect that match the viewport rect, we
-  // reset the content's transform to identity.
-  HRESULT hr = viewport_->ZoomToRect(
-      static_cast<float>(0), static_cast<float>(0),
-      static_cast<float>(viewport_size_in_pixels_.width()),
-      static_cast<float>(viewport_size_in_pixels_.height()), FALSE);
-  if (!SUCCEEDED(hr)) {
-    DebugLogging("Viewport zoom to rect failed.", hr);
-    return hr;
-  }
-
-  need_poll_events_ = need_poll_events;
-  return S_OK;
-}
-
-bool DirectManipulationHelper::PollForNextEvent() {
-  // Simulate 1 frame in update_manager_.
-  HRESULT hr = update_manager_->Update(nullptr);
-  if (!SUCCEEDED(hr))
-    DebugLogging("UpdateManager update failed.", hr);
-  return need_poll_events_;
 }
 
 void DirectManipulationHelper::SetDeviceScaleFactorForTesting(float factor) {
   event_handler_->SetDeviceScaleFactor(factor);
 }
 
+void DirectManipulationHelper::Destroy() {
+  if (!compositor_)
+    return;
+
+  compositor_->RemoveAnimationObserver(this);
+  compositor_ = nullptr;
+
+  HRESULT hr;
+  if (viewport_) {
+    hr = viewport_->Stop();
+    if (!SUCCEEDED(hr))
+      DebugLogging("Viewport stop failed.", hr);
+
+    hr = viewport_->RemoveEventHandler(view_port_handler_cookie_);
+    if (!SUCCEEDED(hr))
+      DebugLogging("Viewport remove event handler failed.", hr);
+
+    hr = viewport_->Abandon();
+    if (!SUCCEEDED(hr))
+      DebugLogging("Viewport abandon failed.", hr);
+  }
+
+  if (manager_) {
+    hr = manager_->Deactivate(window_);
+    if (!SUCCEEDED(hr))
+      DebugLogging("DirectManipulationManager deactivate failed.", hr);
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/direct_manipulation_helper_win.h b/content/browser/renderer_host/direct_manipulation_helper_win.h
index 76c889b..c644d4c4 100644
--- a/content/browser/renderer_host/direct_manipulation_helper_win.h
+++ b/content/browser/renderer_host/direct_manipulation_helper_win.h
@@ -16,10 +16,12 @@
 #include "base/macros.h"
 #include "content/browser/renderer_host/direct_manipulation_event_handler_win.h"
 #include "content/common/content_export.h"
+#include "ui/compositor/compositor_animation_observer.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace ui {
 
+class Compositor;
 class WindowEventTarget;
 
 }  // namespace ui
@@ -44,13 +46,15 @@
 //    when DM_POINTERHITTEST.
 // 3. OnViewportStatusChanged will be called when the gesture phase change.
 //    OnContentUpdated will be called when the gesture update.
-class CONTENT_EXPORT DirectManipulationHelper {
+class CONTENT_EXPORT DirectManipulationHelper
+    : public ui::CompositorAnimationObserver {
  public:
   // Creates and initializes an instance of this class if Direct Manipulation is
   // enabled on the platform. Returns nullptr if it disabled or failed on
   // initialization.
   static std::unique_ptr<DirectManipulationHelper> CreateInstance(
       HWND window,
+      ui::Compositor* compositor,
       ui::WindowEventTarget* event_target);
 
   // Creates and initializes an instance for testing.
@@ -58,49 +62,41 @@
       ui::WindowEventTarget* event_target,
       Microsoft::WRL::ComPtr<IDirectManipulationViewport> viewport);
 
-  ~DirectManipulationHelper();
+  ~DirectManipulationHelper() override;
 
-  // Actives Direct Manipulation, call when window show.
-  void Activate();
-
-  // Deactivates Direct Manipulation, call when window show.
-  void Deactivate();
+  // CompositorAnimationObserver implements.
+  // DirectManipulation needs to poll for new events every frame while finger
+  // gesturing on touchpad.
+  void OnAnimationStep(base::TimeTicks timestamp) override;
+  void OnCompositingShuttingDown(ui::Compositor* compositor) override;
 
   // Updates viewport size. Call it when window bounds updated.
   void SetSizeInPixels(const gfx::Size& size_in_pixels);
 
-  // Reset for gesture end.
-  HRESULT Reset(bool need_animtation);
-
-  // Pass the pointer hit test to Direct Manipulation. Return true indicated we
-  // need poll for new events every frame from here.
-  bool OnPointerHitTest(WPARAM w_param, ui::WindowEventTarget* event_target);
-
-  // On each frame poll new Direct Manipulation events. Return true if we still
-  // need poll for new events on next frame, otherwise stop request need begin
-  // frame.
-  bool PollForNextEvent();
+  // Pass the pointer hit test to Direct Manipulation.
+  void OnPointerHitTest(WPARAM w_param);
 
  private:
   friend class content::DirectManipulationBrowserTest;
   friend class DirectManipulationUnitTest;
 
-  DirectManipulationHelper();
+  DirectManipulationHelper(HWND window, ui::Compositor* compositor);
 
   // This function instantiates Direct Manipulation and creates a viewport for
-  // the passed in |window|. Return false if initialize failed.
+  // |window_|. Return false if initialize failed.
   bool Initialize(ui::WindowEventTarget* event_target);
 
   void SetDeviceScaleFactorForTesting(float factor);
 
+  void Destroy();
+
   Microsoft::WRL::ComPtr<IDirectManipulationManager> manager_;
   Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> update_manager_;
   Microsoft::WRL::ComPtr<IDirectManipulationViewport> viewport_;
   Microsoft::WRL::ComPtr<DirectManipulationEventHandler> event_handler_;
   HWND window_;
+  ui::Compositor* compositor_ = nullptr;
   DWORD view_port_handler_cookie_;
-  bool need_poll_events_ = false;
-  gfx::Size viewport_size_in_pixels_;
 
   DISALLOW_COPY_AND_ASSIGN(DirectManipulationHelper);
 };
diff --git a/content/browser/renderer_host/direct_manipulation_win_browsertest.cc b/content/browser/renderer_host/direct_manipulation_win_browsertest.cc
index 6690f4ef..47675942 100644
--- a/content/browser/renderer_host/direct_manipulation_win_browsertest.cc
+++ b/content/browser/renderer_host/direct_manipulation_win_browsertest.cc
@@ -49,35 +49,12 @@
     return rwhva->legacy_render_widget_host_HWND_;
   }
 
-  HWND GetSubWindowHWND() {
-    LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND();
-
-    return lrwhh->hwnd();
-  }
-
   ui::WindowEventTarget* GetWindowEventTarget() {
     LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND();
 
     return lrwhh->GetWindowEventTarget(lrwhh->GetParent());
   }
 
-  void SimulatePointerHitTest() {
-    LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND();
-
-    lrwhh->direct_manipulation_helper_->need_poll_events_ = true;
-    lrwhh->CreateAnimationObserver();
-  }
-
-  void UpdateParent(HWND hwnd) {
-    LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND();
-
-    lrwhh->UpdateParent(hwnd);
-  }
-
-  bool HasCompositorAnimationObserver(LegacyRenderWidgetHostHWND* lrwhh) {
-    return lrwhh->compositor_animation_observer_ != nullptr;
-  }
-
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 
@@ -88,37 +65,6 @@
                          DirectManipulationBrowserTest,
                          testing::Bool());
 
-// Ensure the AnimationObserver destroy when hwnd reparent to other hwnd.
-IN_PROC_BROWSER_TEST_P(DirectManipulationBrowserTest, HWNDReparent) {
-  if (base::win::GetVersion() < base::win::VERSION_WIN10)
-    return;
-
-  NavigateToURL(shell(), GURL(url::kAboutBlankURL));
-
-  LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND();
-  ASSERT_TRUE(lrwhh);
-
-  // The observer should not create before it needed.
-  ASSERT_TRUE(!HasCompositorAnimationObserver(lrwhh));
-
-  // Add AnimationObserver to tab to simulate direct manipulation start.
-  SimulatePointerHitTest();
-  ASSERT_TRUE(HasCompositorAnimationObserver(lrwhh));
-
-  // Create another browser.
-  Shell* shell2 = CreateBrowser();
-  NavigateToURL(shell2, GURL(url::kAboutBlankURL));
-
-  // Move to the tab to browser2.
-  UpdateParent(
-      shell2->window()->GetRootWindow()->GetHost()->GetAcceleratedWidget());
-
-  // The animation observer should be removed.
-  EXPECT_FALSE(HasCompositorAnimationObserver(lrwhh));
-
-  shell2->Close();
-}
-
 // EventLogger is to observe the events sent from WindowEventTarget (the root
 // window).
 class EventLogger : public ui::EventRewriter {
diff --git a/content/browser/renderer_host/direct_manipulation_win_unittest.cc b/content/browser/renderer_host/direct_manipulation_win_unittest.cc
index 3c85312..ce91625 100644
--- a/content/browser/renderer_host/direct_manipulation_win_unittest.cc
+++ b/content/browser/renderer_host/direct_manipulation_win_unittest.cc
@@ -31,6 +31,12 @@
 
   ~MockDirectManipulationViewport() override {}
 
+  bool IsZoomToRectCalled() {
+    bool called = zoom_to_rect_called_;
+    zoom_to_rect_called_ = false;
+    return called;
+  }
+
   HRESULT STDMETHODCALLTYPE Enable() override { return S_OK; }
 
   HRESULT STDMETHODCALLTYPE Disable() override { return S_OK; }
@@ -75,6 +81,7 @@
                                        _In_ const float right,
                                        _In_ const float bottom,
                                        _In_ BOOL animate) override {
+    zoom_to_rect_called_ = true;
     return S_OK;
   }
 
@@ -161,6 +168,8 @@
   HRESULT STDMETHODCALLTYPE Abandon() override { return S_OK; }
 
  private:
+  bool zoom_to_rect_called_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(MockDirectManipulationViewport);
 };
 
@@ -397,13 +406,7 @@
         viewport_.Get(), content_.Get());
   }
 
-  void SetNeedAnimation(bool need_poll_events) {
-    direct_manipulation_helper_->need_poll_events_ = need_poll_events;
-  }
-
-  bool NeedAnimation() {
-    return direct_manipulation_helper_->need_poll_events_;
-  }
+  bool IsZoomToRectCalled() { return viewport_->IsZoomToRectCalled(); }
 
   void SetDeviceScaleFactor(float factor) {
     direct_manipulation_helper_->SetDeviceScaleFactorForTesting(factor);
@@ -721,21 +724,19 @@
 }
 
 TEST_F(DirectManipulationUnitTest,
-       NeedAnimtationShouldBeFalseAfterSecondReset) {
+       ZoomToRectShouldNotBeCalledInEmptyRunningReadySequence) {
   if (!GetDirectManipulationHelper())
     return;
 
-  // Direct Manipulation will set need_poll_events_ true when DM_POINTERTEST
-  // from touchpad.
-  SetNeedAnimation(true);
+  ContentUpdated(1.0f, 5, 0);
 
   // Receive first ready when gesture end.
   ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_RUNNING);
-  EXPECT_TRUE(NeedAnimation());
+  EXPECT_TRUE(IsZoomToRectCalled());
 
   // Receive second ready from ZoomToRect.
   ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_RUNNING);
-  EXPECT_FALSE(NeedAnimation());
+  EXPECT_FALSE(IsZoomToRectCalled());
 }
 
 TEST_F(DirectManipulationUnitTest, HiDPIScroll) {
diff --git a/content/browser/renderer_host/legacy_render_widget_host_win.cc b/content/browser/renderer_host/legacy_render_widget_host_win.cc
index b781869..e32f482 100644
--- a/content/browser/renderer_host/legacy_render_widget_host_win.cc
+++ b/content/browser/renderer_host/legacy_render_widget_host_win.cc
@@ -27,7 +27,6 @@
 #include "ui/base/view_prop.h"
 #include "ui/base/win/internal_constants.h"
 #include "ui/base/win/window_event_target.h"
-#include "ui/compositor/compositor.h"
 #include "ui/display/win/screen_win.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -38,47 +37,6 @@
 // accessibility support.
 const int kIdScreenReaderHoneyPot = 1;
 
-// DirectManipulation needs to poll for new events every frame while finger
-// gesturing on touchpad.
-class CompositorAnimationObserverForDirectManipulation
-    : public ui::CompositorAnimationObserver {
- public:
-  CompositorAnimationObserverForDirectManipulation(
-      LegacyRenderWidgetHostHWND* render_widget_host_hwnd,
-      ui::Compositor* compositor)
-      : render_widget_host_hwnd_(render_widget_host_hwnd),
-        compositor_(compositor) {
-    DCHECK(compositor_);
-    compositor_->AddAnimationObserver(this);
-    DebugLogging("Add AnimationObserverForDirectManipulation.");
-  }
-
-  ~CompositorAnimationObserverForDirectManipulation() override {
-    if (compositor_) {
-      compositor_->RemoveAnimationObserver(this);
-      DebugLogging("Remove AnimationObserverForDirectManipulation.");
-    }
-  }
-
-  // ui::CompositorAnimationObserver
-  void OnAnimationStep(base::TimeTicks timestamp) override {
-    render_widget_host_hwnd_->PollForNextEvent();
-  }
-
-  // ui::CompositorAnimationObserver
-  void OnCompositingShuttingDown(ui::Compositor* compositor) override {
-    DebugLogging("OnCompositingShuttingDown.");
-    compositor->RemoveAnimationObserver(this);
-    compositor_ = nullptr;
-  }
-
- private:
-  LegacyRenderWidgetHostHWND* render_widget_host_hwnd_;
-  ui::Compositor* compositor_;
-
-  DISALLOW_COPY_AND_ASSIGN(CompositorAnimationObserverForDirectManipulation);
-};
-
 // static
 LegacyRenderWidgetHostHWND* LegacyRenderWidgetHostHWND::Create(
     HWND parent) {
@@ -103,8 +61,6 @@
 }
 
 void LegacyRenderWidgetHostHWND::Destroy() {
-  // Stop the AnimationObserver when window close.
-  DestroyAnimationObserver();
   host_ = nullptr;
   if (::IsWindow(hwnd()))
     ::DestroyWindow(hwnd());
@@ -113,10 +69,16 @@
 void LegacyRenderWidgetHostHWND::UpdateParent(HWND parent) {
   if (GetWindowEventTarget(GetParent()))
     GetWindowEventTarget(GetParent())->HandleParentChanged();
-  // Stop the AnimationObserver when window hide. eg. tab switch, move tab to
-  // another window.
-  DestroyAnimationObserver();
+
   ::SetParent(hwnd(), parent);
+
+  // Direct Manipulation is enabled on Windows 10+. The CreateInstance function
+  // returns NULL if Direct Manipulation is not available. Recreate
+  // |direct_manipulation_helper_| when parent changed (compositor and window
+  // event target updated).
+  direct_manipulation_helper_ = DirectManipulationHelper::CreateInstance(
+      hwnd(), host_->GetNativeView()->GetHost()->compositor(),
+      GetWindowEventTarget(GetParent()));
 }
 
 HWND LegacyRenderWidgetHostHWND::GetParent() {
@@ -125,14 +87,10 @@
 
 void LegacyRenderWidgetHostHWND::Show() {
   ::ShowWindow(hwnd(), SW_SHOW);
-  if (direct_manipulation_helper_)
-    direct_manipulation_helper_->Activate();
 }
 
 void LegacyRenderWidgetHostHWND::Hide() {
   ::ShowWindow(hwnd(), SW_HIDE);
-  if (direct_manipulation_helper_)
-    direct_manipulation_helper_->Deactivate();
 }
 
 void LegacyRenderWidgetHostHWND::SetBounds(const gfx::Rect& bounds) {
@@ -191,11 +149,6 @@
                    CHILDID_SELF);
   }
 
-  // Direct Manipulation is enabled on Windows 10+. The CreateInstance function
-  // returns NULL if Direct Manipulation is not available.
-  direct_manipulation_helper_ = DirectManipulationHelper::CreateInstance(
-      hwnd(), GetWindowEventTarget(GetParent()));
-
   // Disable pen flicks (http://crbug.com/506977)
   base::win::DisableFlicks(hwnd());
 
@@ -501,21 +454,6 @@
   return 0;
 }
 
-LRESULT LegacyRenderWidgetHostHWND::OnWindowPosChanged(UINT message,
-                                                       WPARAM w_param,
-                                                       LPARAM l_param) {
-  WINDOWPOS* window_pos = reinterpret_cast<WINDOWPOS*>(l_param);
-  if (direct_manipulation_helper_) {
-    if (window_pos->flags & SWP_SHOWWINDOW) {
-      direct_manipulation_helper_->Activate();
-    } else if (window_pos->flags & SWP_HIDEWINDOW) {
-      direct_manipulation_helper_->Deactivate();
-    }
-  }
-  SetMsgHandled(FALSE);
-  return 0;
-}
-
 LRESULT LegacyRenderWidgetHostHWND::OnDestroy(UINT message,
                                               WPARAM w_param,
                                               LPARAM l_param) {
@@ -534,30 +472,12 @@
     return 0;
 
   DebugLogging("Receive DM_POINTERHITTEST.");
-  // Update window event target for each DM_POINTERHITTEST.
-  if (direct_manipulation_helper_->OnPointerHitTest(
-          w_param, GetWindowEventTarget(GetParent()))) {
-    if (compositor_animation_observer_) {
-      // This is reach if Windows send a DM_POINTERHITTEST before the last
-      // DM_POINTERHITTEST receive READY status. We never see this but still
-      // worth to handle it.
-      DebugLogging("AnimationObserverForDirectManipulation exists.");
-      return 0;
-    }
 
-    CreateAnimationObserver();
-  }
+  direct_manipulation_helper_->OnPointerHitTest(w_param);
 
   return 0;
 }
 
-void LegacyRenderWidgetHostHWND::PollForNextEvent() {
-  DCHECK(direct_manipulation_helper_);
-
-  if (!direct_manipulation_helper_->PollForNextEvent())
-    DestroyAnimationObserver();
-}
-
 gfx::NativeViewAccessible
 LegacyRenderWidgetHostHWND::GetOrCreateWindowRootAccessible() {
   if (!host_)
@@ -589,20 +509,4 @@
   return root->GetNativeViewAccessible();
 }
 
-void LegacyRenderWidgetHostHWND::CreateAnimationObserver() {
-  DCHECK(!compositor_animation_observer_);
-  DCHECK(host_);
-  DCHECK(host_->GetNativeView()->GetHost());
-  DCHECK(host_->GetNativeView()->GetHost()->compositor());
-
-  compositor_animation_observer_ =
-      std::make_unique<CompositorAnimationObserverForDirectManipulation>(
-          this, host_->GetNativeView()->GetHost()->compositor());
-}
-
-void LegacyRenderWidgetHostHWND::DestroyAnimationObserver() {
-  DebugLogging("DestroyAnimationObserver.");
-  compositor_animation_observer_.reset();
-}
-
 }  // namespace content
diff --git a/content/browser/renderer_host/legacy_render_widget_host_win.h b/content/browser/renderer_host/legacy_render_widget_host_win.h
index 950515b4..6c06a240 100644
--- a/content/browser/renderer_host/legacy_render_widget_host_win.h
+++ b/content/browser/renderer_host/legacy_render_widget_host_win.h
@@ -14,7 +14,6 @@
 #include "base/macros.h"
 #include "base/win/atl.h"
 #include "content/common/content_export.h"
-#include "ui/compositor/compositor_animation_observer.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/native_widget_types.h"
 
@@ -96,7 +95,6 @@
                           OnMouseRange)
     MESSAGE_HANDLER_EX(WM_NCCALCSIZE, OnNCCalcSize)
     MESSAGE_HANDLER_EX(WM_SIZE, OnSize)
-    MESSAGE_HANDLER_EX(WM_WINDOWPOSCHANGED, OnWindowPosChanged)
     MESSAGE_HANDLER_EX(WM_DESTROY, OnDestroy)
     MESSAGE_HANDLER_EX(DM_POINTERHITTEST, OnPointerHitTest)
   END_MSG_MAP()
@@ -123,10 +121,6 @@
     host_ = host;
   }
 
-  // DirectManipulation needs to poll for new events every frame while finger
-  // gesturing on touchpad.
-  void PollForNextEvent();
-
   // Return the root accessible object for either MSAA or UI Automation.
   gfx::NativeViewAccessible GetOrCreateWindowRootAccessible();
 
@@ -163,15 +157,10 @@
   LRESULT OnSetCursor(UINT message, WPARAM w_param, LPARAM l_param);
   LRESULT OnNCCalcSize(UINT message, WPARAM w_param, LPARAM l_param);
   LRESULT OnSize(UINT message, WPARAM w_param, LPARAM l_param);
-  LRESULT OnWindowPosChanged(UINT message, WPARAM w_param, LPARAM l_param);
   LRESULT OnDestroy(UINT message, WPARAM w_param, LPARAM l_param);
 
   LRESULT OnPointerHitTest(UINT message, WPARAM w_param, LPARAM l_param);
 
-  void CreateAnimationObserver();
-
-  void DestroyAnimationObserver();
-
   Microsoft::WRL::ComPtr<IAccessible> window_accessible_;
 
   // Set to true if we turned on mouse tracking.
@@ -190,9 +179,6 @@
   // in Chrome on Windows 10.
   std::unique_ptr<DirectManipulationHelper> direct_manipulation_helper_;
 
-  std::unique_ptr<ui::CompositorAnimationObserver>
-      compositor_animation_observer_;
-
   DISALLOW_COPY_AND_ASSIGN(LegacyRenderWidgetHostHWND);
 };
 
diff --git a/content/common/accessibility_messages.h b/content/common/accessibility_messages.h
index 9344a5e..2ed10364 100644
--- a/content/common/accessibility_messages.h
+++ b/content/common/accessibility_messages.h
@@ -76,6 +76,7 @@
   IPC_STRUCT_TRAITS_MEMBER(loaded)
   IPC_STRUCT_TRAITS_MEMBER(loading_progress)
   IPC_STRUCT_TRAITS_MEMBER(focus_id)
+  IPC_STRUCT_TRAITS_MEMBER(sel_is_backward)
   IPC_STRUCT_TRAITS_MEMBER(sel_anchor_object_id)
   IPC_STRUCT_TRAITS_MEMBER(sel_anchor_offset)
   IPC_STRUCT_TRAITS_MEMBER(sel_anchor_affinity)
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index d74bbd0..9512126 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -100,6 +100,8 @@
     "effective_connection_type_helper.h",
     "fetchers/associated_resource_fetcher_impl.cc",
     "fetchers/associated_resource_fetcher_impl.h",
+    "fetchers/manifest_fetcher.cc",
+    "fetchers/manifest_fetcher.h",
     "fetchers/multi_resolution_image_resource_fetcher.cc",
     "fetchers/multi_resolution_image_resource_fetcher.h",
     "fetchers/resource_fetcher_impl.cc",
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc
index 1c0e46c..f26597e 100644
--- a/content/renderer/accessibility/blink_ax_tree_source.cc
+++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include <algorithm>
 #include <set>
 
 #include "base/memory/ptr_util.h"
@@ -377,12 +378,14 @@
   if (!focus().IsNull())
     tree_data->focus_id = focus().AxID();
 
+  bool is_selection_backward = false;
   WebAXObject anchor_object, focus_object;
   int anchor_offset, focus_offset;
   ax::mojom::TextAffinity anchor_affinity, focus_affinity;
   if (base::FeatureList::IsEnabled(features::kNewAccessibilitySelection)) {
-    root().Selection(anchor_object, anchor_offset, anchor_affinity,
-                     focus_object, focus_offset, focus_affinity);
+    root().Selection(is_selection_backward, anchor_object, anchor_offset,
+                     anchor_affinity, focus_object, focus_offset,
+                     focus_affinity);
   } else {
     root().SelectionDeprecated(anchor_object, anchor_offset, anchor_affinity,
                                focus_object, focus_offset, focus_affinity);
@@ -391,6 +394,7 @@
       focus_offset >= 0) {
     int32_t anchor_id = anchor_object.AxID();
     int32_t focus_id = focus_object.AxID();
+    tree_data->sel_is_backward = is_selection_backward;
     tree_data->sel_anchor_object_id = anchor_id;
     tree_data->sel_anchor_offset = anchor_offset;
     tree_data->sel_focus_object_id = focus_id;
diff --git a/content/renderer/fetchers/manifest_fetcher.cc b/content/renderer/fetchers/manifest_fetcher.cc
new file mode 100644
index 0000000..8c45f2e
--- /dev/null
+++ b/content/renderer/fetchers/manifest_fetcher.cc
@@ -0,0 +1,59 @@
+// 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 "content/renderer/fetchers/manifest_fetcher.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "content/public/renderer/associated_resource_fetcher.h"
+#include "services/network/public/mojom/request_context_frame_type.mojom.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/public/web/web_associated_url_loader_options.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+
+namespace content {
+
+ManifestFetcher::ManifestFetcher(const GURL& url)
+    : completed_(false) {
+  fetcher_.reset(AssociatedResourceFetcher::Create(url));
+}
+
+ManifestFetcher::~ManifestFetcher() {
+  if (!completed_)
+    Cancel();
+}
+
+void ManifestFetcher::Start(blink::WebLocalFrame* frame,
+                            bool use_credentials,
+                            const Callback& callback) {
+  callback_ = callback;
+
+  blink::WebAssociatedURLLoaderOptions options;
+  fetcher_->SetLoaderOptions(options);
+
+  // See https://w3c.github.io/manifest/. Use "include" when use_credentials is
+  // true, and "omit" otherwise.
+  fetcher_->Start(
+      frame, blink::mojom::RequestContextType::MANIFEST,
+      network::mojom::FetchRequestMode::kCors,
+      use_credentials ? network::mojom::FetchCredentialsMode::kInclude
+                      : network::mojom::FetchCredentialsMode::kOmit,
+      base::Bind(&ManifestFetcher::OnLoadComplete, base::Unretained(this)));
+}
+
+void ManifestFetcher::Cancel() {
+  DCHECK(!completed_);
+  fetcher_->Cancel();
+}
+
+void ManifestFetcher::OnLoadComplete(const blink::WebURLResponse& response,
+                                     const std::string& data) {
+  DCHECK(!completed_);
+  completed_ = true;
+
+  Callback callback = callback_;
+  std::move(callback).Run(response, data);
+}
+
+}  // namespace content
diff --git a/content/renderer/fetchers/manifest_fetcher.h b/content/renderer/fetchers/manifest_fetcher.h
new file mode 100644
index 0000000..ef48c184
--- /dev/null
+++ b/content/renderer/fetchers/manifest_fetcher.h
@@ -0,0 +1,59 @@
+// 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_RENDERER_FETCHERS_MANIFEST_FETCHER_H_
+#define CONTENT_RENDERER_FETCHERS_MANIFEST_FETCHER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "content/common/content_export.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+
+class GURL;
+
+namespace blink {
+class WebLocalFrame;
+}
+
+namespace content {
+
+class AssociatedResourceFetcher;
+
+// Helper class to download a Web Manifest. When an instance is created, the
+// caller need to call Start() and wait for the passed callback to be executed.
+// If the fetch fails, the callback will be called with two empty objects.
+class CONTENT_EXPORT ManifestFetcher {
+ public:
+  // This will be called asynchronously after the URL has been fetched,
+  // successfully or not.  If there is a failure, response and data will both be
+  // empty.  |response| and |data| are both valid until the URLFetcher instance
+  // is destroyed.
+  typedef base::Callback<void(const blink::WebURLResponse& response,
+                              const std::string& data)> Callback;
+
+  explicit ManifestFetcher(const GURL& url);
+  virtual ~ManifestFetcher();
+
+  void Start(blink::WebLocalFrame* frame,
+             bool use_credentials,
+             const Callback& callback);
+  void Cancel();
+
+ private:
+  void OnLoadComplete(const blink::WebURLResponse& response,
+                      const std::string& data);
+
+  bool completed_;
+  Callback callback_;
+  std::unique_ptr<AssociatedResourceFetcher> fetcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(ManifestFetcher);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_FETCHERS_MANIFEST_FETCHER_H_
diff --git a/content/renderer/manifest/manifest_manager.cc b/content/renderer/manifest/manifest_manager.cc
index 4cefec39..77b5333b 100644
--- a/content/renderer/manifest/manifest_manager.cc
+++ b/content/renderer/manifest/manifest_manager.cc
@@ -10,6 +10,7 @@
 #include "base/no_destructor.h"
 #include "base/strings/nullable_string16.h"
 #include "content/public/renderer/render_frame.h"
+#include "content/renderer/fetchers/manifest_fetcher.h"
 #include "content/renderer/manifest/manifest_uma_util.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
@@ -17,7 +18,6 @@
 #include "third_party/blink/public/web/web_console_message.h"
 #include "third_party/blink/public/web/web_document.h"
 #include "third_party/blink/public/web/web_local_frame.h"
-#include "third_party/blink/public/web/web_manifest_fetcher.h"
 #include "third_party/blink/public/web/web_manifest_parser.h"
 
 namespace content {
@@ -114,8 +114,7 @@
     return;
   }
 
-  blink::WebDocument document = render_frame()->GetWebFrame()->GetDocument();
-  manifest_url_ = document.ManifestURL();
+  manifest_url_ = render_frame()->GetWebFrame()->GetDocument().ManifestURL();
 
   if (manifest_url_.is_empty()) {
     ManifestUmaUtil::FetchFailed(ManifestUmaUtil::FETCH_EMPTY_URL);
@@ -123,11 +122,13 @@
     return;
   }
 
-  fetcher_.reset(new blink::WebManifestFetcher(manifest_url_));
-
-  fetcher_->Start(&document, document.ManifestUseCredentials(),
-                  base::BindOnce(&ManifestManager::OnManifestFetchComplete,
-                                 base::Unretained(this), document.Url()));
+  fetcher_.reset(new ManifestFetcher(manifest_url_));
+  fetcher_->Start(
+      render_frame()->GetWebFrame(),
+      render_frame()->GetWebFrame()->GetDocument().ManifestUseCredentials(),
+      base::Bind(&ManifestManager::OnManifestFetchComplete,
+                 base::Unretained(this),
+                 render_frame()->GetWebFrame()->GetDocument().Url()));
 }
 
 static const std::string& GetMessagePrefix() {
@@ -138,9 +139,9 @@
 void ManifestManager::OnManifestFetchComplete(
     const GURL& document_url,
     const blink::WebURLResponse& response,
-    const blink::WebString& data) {
+    const std::string& data) {
   fetcher_.reset();
-  if (response.IsNull() && data.IsEmpty()) {
+  if (response.IsNull() && data.empty()) {
     manifest_debug_info_ = nullptr;
     ManifestUmaUtil::FetchFailed(ManifestUmaUtil::FETCH_UNSPECIFIED_REASON);
     ResolveCallbacks(ResolveStateFailure);
@@ -149,15 +150,14 @@
 
   ManifestUmaUtil::FetchSucceeded();
   GURL response_url = response.CurrentRequestUrl();
-  std::string data_string = data.Utf8();
-  base::StringPiece data_piece(data_string);
+  base::StringPiece data_piece(data);
 
   blink::WebVector<blink::ManifestError> errors;
   bool result = blink::WebManifestParser::ParseManifest(
       data_piece, response_url, document_url, &manifest_, &errors);
 
   manifest_debug_info_ = blink::mojom::ManifestDebugInfo::New();
-  manifest_debug_info_->raw_manifest = data_string;
+  manifest_debug_info_->raw_manifest = data;
 
   for (const auto& error : errors) {
     blink::WebConsoleMessage message;
diff --git a/content/renderer/manifest/manifest_manager.h b/content/renderer/manifest/manifest_manager.h
index a1920c9..5836e41 100644
--- a/content/renderer/manifest/manifest_manager.h
+++ b/content/renderer/manifest/manifest_manager.h
@@ -20,7 +20,6 @@
 class GURL;
 
 namespace blink {
-class WebManifestFetcher;
 class WebURLResponse;
 }
 
@@ -72,10 +71,10 @@
   void FetchManifest();
   void OnManifestFetchComplete(const GURL& document_url,
                                const blink::WebURLResponse& response,
-                               const blink::WebString& data);
+                               const std::string& data);
   void ResolveCallbacks(ResolveState state);
 
-  std::unique_ptr<blink::WebManifestFetcher> fetcher_;
+  std::unique_ptr<ManifestFetcher> fetcher_;
 
   // Whether the RenderFrame may have an associated Manifest. If true, the frame
   // may have a manifest, if false, it can't have one. This boolean is true when
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index fec6ca58..3027321 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -1018,7 +1018,7 @@
           kGpuStreamIdDefault, kGpuStreamPriorityDefault,
           gpu::kNullSurfaceHandle, GURL(top_document_web_url),
           automatic_flushes, support_locking, support_grcontext,
-          gpu::SharedMemoryLimits(), attributes,
+          gpu::SharedMemoryLimits::ForWebGPUContext(), attributes,
           ws::command_buffer_metrics::ContextType::WEBGPU));
   return std::make_unique<WebGraphicsContext3DProviderImpl>(
       std::move(provider));
diff --git a/content/renderer/skia_benchmarking_extension.cc b/content/renderer/skia_benchmarking_extension.cc
index e125a2d..af81a67 100644
--- a/content/renderer/skia_benchmarking_extension.cc
+++ b/content/renderer/skia_benchmarking_extension.cc
@@ -285,8 +285,9 @@
       v8::Array::New(isolate, benchmarking_canvas.CommandCount());
   for (size_t i = 0; i < benchmarking_canvas.CommandCount(); ++i) {
     op_times
-        ->Set(context, i,
-              v8::Number::New(isolate, benchmarking_canvas.GetTime(i)))
+        ->CreateDataProperty(
+            context, i,
+            v8::Number::New(isolate, benchmarking_canvas.GetTime(i)))
         .Check();
   }
 
diff --git a/content/shell/test_runner/web_ax_object_proxy.cc b/content/shell/test_runner/web_ax_object_proxy.cc
index 5298659f..0e03fabc 100644
--- a/content/shell/test_runner/web_ax_object_proxy.cc
+++ b/content/shell/test_runner/web_ax_object_proxy.cc
@@ -656,6 +656,8 @@
       .SetProperty("stepValue", &WebAXObjectProxy::StepValue)
       .SetProperty("valueDescription", &WebAXObjectProxy::ValueDescription)
       .SetProperty("childrenCount", &WebAXObjectProxy::ChildrenCount)
+      .SetProperty("selectionIsBackward",
+                   &WebAXObjectProxy::SelectionIsBackward)
       .SetProperty("selectionAnchorObject",
                    &WebAXObjectProxy::SelectionAnchorObject)
       .SetProperty("selectionAnchorOffset",
@@ -946,17 +948,35 @@
   return count;
 }
 
-v8::Local<v8::Value> WebAXObjectProxy::SelectionAnchorObject() {
+bool WebAXObjectProxy::SelectionIsBackward() {
   accessibility_object_.UpdateLayoutAndCheckValidity();
 
+  bool is_selection_backward = false;
   blink::WebAXObject anchorObject;
   int anchorOffset = -1;
   ax::mojom::TextAffinity anchorAffinity;
   blink::WebAXObject focusObject;
   int focusOffset = -1;
   ax::mojom::TextAffinity focusAffinity;
-  accessibility_object_.Selection(anchorObject, anchorOffset, anchorAffinity,
-                                  focusObject, focusOffset, focusAffinity);
+  accessibility_object_.Selection(is_selection_backward, anchorObject,
+                                  anchorOffset, anchorAffinity, focusObject,
+                                  focusOffset, focusAffinity);
+  return is_selection_backward;
+}
+
+v8::Local<v8::Value> WebAXObjectProxy::SelectionAnchorObject() {
+  accessibility_object_.UpdateLayoutAndCheckValidity();
+
+  bool is_selection_backward = false;
+  blink::WebAXObject anchorObject;
+  int anchorOffset = -1;
+  ax::mojom::TextAffinity anchorAffinity;
+  blink::WebAXObject focusObject;
+  int focusOffset = -1;
+  ax::mojom::TextAffinity focusAffinity;
+  accessibility_object_.Selection(is_selection_backward, anchorObject,
+                                  anchorOffset, anchorAffinity, focusObject,
+                                  focusOffset, focusAffinity);
   if (anchorObject.IsNull())
     return v8::Null(blink::MainThreadIsolate());
 
@@ -966,14 +986,16 @@
 int WebAXObjectProxy::SelectionAnchorOffset() {
   accessibility_object_.UpdateLayoutAndCheckValidity();
 
+  bool is_selection_backward = false;
   blink::WebAXObject anchorObject;
   int anchorOffset = -1;
   ax::mojom::TextAffinity anchorAffinity;
   blink::WebAXObject focusObject;
   int focusOffset = -1;
   ax::mojom::TextAffinity focusAffinity;
-  accessibility_object_.Selection(anchorObject, anchorOffset, anchorAffinity,
-                                  focusObject, focusOffset, focusAffinity);
+  accessibility_object_.Selection(is_selection_backward, anchorObject,
+                                  anchorOffset, anchorAffinity, focusObject,
+                                  focusOffset, focusAffinity);
   if (anchorOffset < 0)
     return -1;
 
@@ -983,14 +1005,16 @@
 std::string WebAXObjectProxy::SelectionAnchorAffinity() {
   accessibility_object_.UpdateLayoutAndCheckValidity();
 
+  bool is_selection_backward = false;
   blink::WebAXObject anchorObject;
   int anchorOffset = -1;
   ax::mojom::TextAffinity anchorAffinity;
   blink::WebAXObject focusObject;
   int focusOffset = -1;
   ax::mojom::TextAffinity focusAffinity;
-  accessibility_object_.Selection(anchorObject, anchorOffset, anchorAffinity,
-                                  focusObject, focusOffset, focusAffinity);
+  accessibility_object_.Selection(is_selection_backward, anchorObject,
+                                  anchorOffset, anchorAffinity, focusObject,
+                                  focusOffset, focusAffinity);
   return anchorAffinity == ax::mojom::TextAffinity::kUpstream ? "upstream"
                                                               : "downstream";
 }
@@ -998,14 +1022,16 @@
 v8::Local<v8::Value> WebAXObjectProxy::SelectionFocusObject() {
   accessibility_object_.UpdateLayoutAndCheckValidity();
 
+  bool is_selection_backward = false;
   blink::WebAXObject anchorObject;
   int anchorOffset = -1;
   ax::mojom::TextAffinity anchorAffinity;
   blink::WebAXObject focusObject;
   int focusOffset = -1;
   ax::mojom::TextAffinity focusAffinity;
-  accessibility_object_.Selection(anchorObject, anchorOffset, anchorAffinity,
-                                  focusObject, focusOffset, focusAffinity);
+  accessibility_object_.Selection(is_selection_backward, anchorObject,
+                                  anchorOffset, anchorAffinity, focusObject,
+                                  focusOffset, focusAffinity);
   if (focusObject.IsNull())
     return v8::Null(blink::MainThreadIsolate());
 
@@ -1015,14 +1041,16 @@
 int WebAXObjectProxy::SelectionFocusOffset() {
   accessibility_object_.UpdateLayoutAndCheckValidity();
 
+  bool is_selection_backward = false;
   blink::WebAXObject anchorObject;
   int anchorOffset = -1;
   ax::mojom::TextAffinity anchorAffinity;
   blink::WebAXObject focusObject;
   int focusOffset = -1;
   ax::mojom::TextAffinity focusAffinity;
-  accessibility_object_.Selection(anchorObject, anchorOffset, anchorAffinity,
-                                  focusObject, focusOffset, focusAffinity);
+  accessibility_object_.Selection(is_selection_backward, anchorObject,
+                                  anchorOffset, anchorAffinity, focusObject,
+                                  focusOffset, focusAffinity);
   if (focusOffset < 0)
     return -1;
 
@@ -1032,14 +1060,16 @@
 std::string WebAXObjectProxy::SelectionFocusAffinity() {
   accessibility_object_.UpdateLayoutAndCheckValidity();
 
+  bool is_selection_backward = false;
   blink::WebAXObject anchorObject;
   int anchorOffset = -1;
   ax::mojom::TextAffinity anchorAffinity;
   blink::WebAXObject focusObject;
   int focusOffset = -1;
   ax::mojom::TextAffinity focusAffinity;
-  accessibility_object_.Selection(anchorObject, anchorOffset, anchorAffinity,
-                                  focusObject, focusOffset, focusAffinity);
+  accessibility_object_.Selection(is_selection_backward, anchorObject,
+                                  anchorOffset, anchorAffinity, focusObject,
+                                  focusOffset, focusAffinity);
   return focusAffinity == ax::mojom::TextAffinity::kUpstream ? "upstream"
                                                              : "downstream";
 }
diff --git a/content/shell/test_runner/web_ax_object_proxy.h b/content/shell/test_runner/web_ax_object_proxy.h
index da30a01b..3348c58 100644
--- a/content/shell/test_runner/web_ax_object_proxy.h
+++ b/content/shell/test_runner/web_ax_object_proxy.h
@@ -76,6 +76,7 @@
 
   // The following selection functions return global information about the
   // current selection and can be called on any object in the tree.
+  bool SelectionIsBackward();
   v8::Local<v8::Value> SelectionAnchorObject();
   int SelectionAnchorOffset();
   std::string SelectionAnchorAffinity();
diff --git a/content/test/gpu/gather_power_measurement_results.py b/content/test/gpu/gather_power_measurement_results.py
index 44d7b1a..519fc68 100755
--- a/content/test/gpu/gather_power_measurement_results.py
+++ b/content/test/gpu/gather_power_measurement_results.py
@@ -118,6 +118,7 @@
   lines = data.splitlines()
   pattern = re.compile('^\[(\d+)/(\d+)\]$')
   results = None
+  bot_candidates = []
   for line in lines:
     if results is not None:
       my_results = results
@@ -139,16 +140,26 @@
     elif line.startswith('Chrome Env: '):
       chrome_env = ast.literal_eval(line[len('Chrome Env: '):])
       if 'COMPUTERNAME' in chrome_env:
-        entry['bot'] = chrome_env['COMPUTERNAME']
+        bot_candidates.append(chrome_env['COMPUTERNAME'])
     elif line.startswith('INFO:root:Chrome Env: '):
       chrome_env = ast.literal_eval(line[len('INFO:root:Chrome Env: '):])
       if 'COMPUTERNAME' in chrome_env:
-        entry['bot'] = chrome_env['COMPUTERNAME']
+        bot_candidates.append(chrome_env['COMPUTERNAME'])
+    elif line.startswith('Env: '):
+      chrome_env = ast.literal_eval(line[len('Env: '):])
+      if 'COMPUTERNAME' in chrome_env:
+        bot_candidates.append(chrome_env['COMPUTERNAME'])
     elif line.startswith(' COMPUTERNAME: '):
-      entry['bot'] = line.strip()[len('COMPUTERNAME: '):]
+      bot_candidates.append(line.strip()[len('COMPUTERNAME: '):])
     elif line.startswith('Results: '):
       assert results is None
       results = ast.literal_eval(line[len('Results: '):])
+  for name in bot_candidates:
+    if name.startswith('BUILD'):
+      entry['bot'] = name
+      break
+  else:
+    logging.warn('[BUILD %d] Fail to locate the bot name' % number)
 
 
 def CollectBuildData(build, data_entry):
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index 48bc227..af810c9d 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -120,8 +120,6 @@
         ['win', 'no_passthrough'], bug=3033) # angle bug ID
     self.Fail('conformance2/glsl3/tricky-loop-conditions.html',
         ['win'], bug=1465) # anglebug.com/1465
-    self.Fail('conformance/textures/misc/texture-active-bind.html',
-        ['win'], bug=931006)
 
     # Win / NVidia
     self.Flaky('deqp/functional/gles3/fbomultisample*',
@@ -1392,18 +1390,10 @@
     self.Fail('conformance2/textures/video/' +
         'tex-2d-rgba8ui-rgba_integer-unsigned_byte.html',
         ['android', 'qualcomm'], bug=906735)
-    self.Fail('conformance2/textures/misc/tex-new-formats.html',
-        ['android', 'qualcomm'], bug=906740)
-    self.Fail('conformance2/textures/misc/copy-texture-image-luma-format.html',
-        ['android', 'qualcomm'], bug=906740)
     # This test is failing on Android Pixel 2 and 3 (Qualcomm)
     # Seems to be an OpenGL ES bug.
     self.Fail('conformance2/rendering/vertex-id.html',
         ['android', 'qualcomm'], bug=945903)
-    self.Fail('deqp/functional/gles3/textureformat/unsized_2d_array.html',
-        ['android', 'qualcomm'], bug=906740)
-    self.Fail('deqp/functional/gles3/textureformat/unsized_3d.html',
-        ['android', 'qualcomm'], bug=906740)
     self.Fail('deqp/functional/gles3/shaderderivate_dfdy.html',
         ['android', 'qualcomm'], bug=906745)
     self.Flaky('deqp/functional/gles3/multisample.html',
diff --git a/content/test/gpu/power_measurement_results/win10_intel_hd_630/build_3633_4131.csv b/content/test/gpu/power_measurement_results/win10_intel_hd_630/build_3633_4131.csv
index 206a203..cb370d0 100644
--- a/content/test/gpu/power_measurement_results/win10_intel_hd_630/build_3633_4131.csv
+++ b/content/test/gpu/power_measurement_results/win10_intel_hd_630/build_3633_4131.csv
@@ -127,86 +127,86 @@
 0.5752608695652168,0.028234113712374536,3.7860000000000027,0.7194749163879598,0.718672240802676,0.11918729096989966,7.898334448160533,0.1195953177257526,7.776668896321074,0.7800802675585287,0.7235418060200668,0.12331438127090302,8.028535117056862,0.24157525083612028,8.092204013377927,BUILD125-A9,4004

 0.5728862876254179,0.0378327759197324,2.5182274247491643,0.7275400000000001,0.7163812709030098,0.12215050167224081,5.453923076923076,0.12363666666666655,5.559556666666673,0.784923076923076,0.7299866220735789,0.13145150501672243,5.719056856187285,0.25047157190635455,5.775404682274247,BUILD133-A9,4003

 0.5932073578595313,0.024598662207357833,3.6061003344481612,0.7247826086956517,0.718889632107023,0.140438127090301,6.623324414715718,0.1439163879598662,6.3651705685618705,0.7805752508361201,0.7344280936454851,0.152361204013378,6.899003344481609,0.26318060200668913,6.9050869565217425,BUILD147-A9,4002

-0.5737725752508361,0.030150501672240797,2.872792642140466,0.7187224080267561,0.7177424749163881,0.12533444816053507,5.542765886287626,0.12189297658862878,5.572204013377922,0.7852709030100331,0.7358394648829435,0.1318595317725753,5.684491638795985,0.24972575250836118,5.705953177257523,SWARM761-C4,4001

-0.5700200668896318,0.02394983277591971,3.273270903010033,0.7300200668896324,0.7192006688963207,0.12675919732441476,6.993692307692311,0.12455518394648829,6.917822742474914,0.7867892976588625,0.7245083612040136,0.1371973244147158,7.093695652173919,0.27090301003344497,7.147177257525083,SWARM761-C4,4000

-0.5741375838926172,0.02351342281879194,2.5692483221476525,0.7196287625418062,0.7349464882943149,0.09894648829431434,5.5296521739130435,0.09775919732441471,5.465822742474917,0.7810133779264207,0.7954882154882156,0.10468686868686869,7.647148148148149,0.20208361204013373,5.6527458193979925,SWARM761-C4,3999

-0.5772742474916389,0.008816053511705687,2.798759197324414,0.7176454849498326,0.7274782608695646,0.13828093645484943,6.051581939799331,0.13235451505016724,6.0693745819398,0.779183946488294,0.726501672240803,0.14813712374581933,6.35122073578595,0.2564949832775919,6.287622073578598,SWARM761-C4,3998

-0.5769966329966328,0.03242424242424239,2.5547239057239044,0.7277090301003343,0.7176655518394647,0.11656187290969901,5.189702341137124,0.1165819397993311,5.202237458193981,0.7802909698996652,0.7237658862876254,0.12438127090301014,5.381438127090299,0.23398996655518392,5.33338795986622,SWARM761-C4,3997

-0.5797959866220737,0.025040133779264173,3.6298862876254168,0.7176655518394647,0.7213244147157186,0.12470903010033434,6.865411371237459,0.1207056856187292,6.773598662207364,0.7931270903010028,0.7267859531772576,0.13173913043478255,6.850327759197326,0.24390301003344445,7.365020066889637,SWARM761-C4,3996

-0.5751170568561875,0.0279163879598662,3.01229431438127,0.7268327759197325,0.7188595317725756,0.10694983277591977,6.321357859531768,0.10729431438127096,6.272548494983277,0.7941371237458188,0.7284127516778522,0.11568791946308728,6.7030100671140955,0.23475919732441475,6.549926421404683,SWARM761-C4,3995

-0.5719966555183951,0.023816053511705647,3.0481839464882947,0.7228993288590606,0.7187725752508362,0.12845484949832778,6.2951103678929785,0.1312818791946309,6.463949664429528,0.7804749163879597,0.725464882943144,0.13783946488294324,6.206605351170567,0.2533545150501672,6.480859531772572,SWARM761-C4,3994

-0.5878461538461536,0.025354515050167194,3.5771036789297654,0.7266989966555187,0.725848993288591,0.12475838926174491,6.731359060402689,0.12955518394648824,6.260966555183948,0.7775150501672236,0.7168060200668896,0.13292642140468228,7.808695652173912,0.2540702341137124,7.797739130434791,SWARM761-C4,3993

-0.5830233333333332,0.00933,2.383490000000001,0.7336555183946492,0.7235652173913049,0.1292541806020068,5.132230769230772,0.12777926421404687,5.02438127090301,0.8023879598662204,0.7293244147157191,0.13685284280936452,5.2357558528428045,0.2505016722408026,5.428852842809364,SWARM761-C4,3992

-0.5830936454849504,0.029294314381270857,4.162518394648829,0.715334448160535,0.7247324414715725,0.11882943143812716,7.751080267558524,0.12002006688963217,7.8378193979933055,0.7805016722408025,0.7217190635451505,0.13004347826086962,7.8579832775919725,0.24649498327759212,7.9921939799331065,SWARM761-C4,3991

-0.571341137123746,0.026197324414715697,2.394046822742474,0.7206923076923076,0.7298628762541802,0.13220066889632112,5.481896321070235,0.13425083612040137,5.246896321070233,0.7806588628762541,0.7261409395973155,0.14293288590604028,5.48185570469799,0.26045150501672243,5.430408026755849,SWARM761-C4,3990

-0.5731906354515054,0.025468227424749135,2.962234113712376,0.7243745819397991,0.7190602006688964,0.11862876254180602,6.106240802675584,0.12156521739130433,6.137625418060202,0.7798260869565221,0.7316120401337787,0.1265953177257526,6.023331103678928,0.23920735785953162,6.218662207357857,SWARM761-C4,3989

-0.5778996655518394,0.0085685618729097,2.8353545150501684,0.7179030100334444,0.7175953177257526,0.11557859531772578,5.916528428093644,0.11364548494983279,5.802494983277595,0.7897056856187293,0.7224782608695652,0.12314715719063547,5.788006688963207,0.2642073578595318,6.330133779264214,SWARM761-C4,3988

-0.569367892976589,0.026207357859531755,3.0752909698996644,0.7168361204013375,0.7145418060200662,0.1273846153846154,7.008060200668897,0.13000668896321071,6.847876254180603,0.7772408026755855,0.7265117056856191,0.13559866220735786,7.034103678929761,0.2513277591973243,7.161434782608697,SWARM761-C4,3987

-0.5704194630872486,0.0317181208053691,2.5595369127516783,0.7200969899665558,0.7234949832775925,0.14043478260869569,5.3492040133779275,0.14318394648829433,5.255555183946487,0.8063344481605352,0.7339030100334446,0.14436454849498329,5.2371471571906385,0.27155518394648825,5.572137123745825,SWARM761-C4,3986

-0.5750635451505021,0.025729096989966536,2.656103678929765,0.7238394648829427,0.7205819397993309,0.12108361204013375,5.535635451505015,0.12225752508361201,5.445695652173916,0.7956199999999991,0.737836120401338,0.1299030100334448,5.338969899665548,0.23949666666666686,5.515420000000001,SWARM761-C4,3985

-0.5726677852348995,0.029503355704697976,3.0893758389261743,0.7187625418060208,0.7190301003344484,0.12803678929765894,6.9882775919732465,0.1282107023411371,6.9719832775919715,0.7779464882943141,0.7248657718120795,0.1331442953020134,7.283278523489931,0.24979933110367888,7.119351170568563,SWARM761-C4,3984

-0.5838233333333331,0.009076666666666667,5.471726666666671,0.7201137123745815,0.7287056856187292,0.11536789297658868,9.008464882943144,0.11412040133779276,9.12337123745819,0.7902675585284276,0.7263143812709028,0.12333110367892972,8.831668896321075,0.24242809364548487,9.020381270903012,SWARM761-C4,3983

-0.5823478260869562,0.025063545150501635,3.427000000000001,0.7304766666666661,0.7374448160535126,0.1571906354515051,6.492956521739126,0.13785999999999993,6.582673333333332,0.7986989966555178,0.7323478260869568,0.14121739130434788,6.1969698996655485,0.2635953177257524,6.329742474916388,SWARM761-C4,3982

-0.5521739130434787,0.009682274247491638,4.952260869565218,0.6794481605351173,0.673304347826087,0.1347692307692308,8.050598662207355,0.13612709030100337,7.898254180602011,0.743665551839465,0.683612040133779,0.14267558528428093,8.338658862876246,0.2841638795986622,8.337100334448156,SWARM761-C4,3981

-0.5759464882943147,0.027535117056856143,3.0257458193979914,0.7240100334448165,0.722227424749164,0.13526086956521752,6.157772575250841,0.1373745819397993,6.260173913043477,0.7798963210702337,0.7268494983277592,0.1557959866220736,6.213709030100337,0.2704916387959868,6.265080267558525,SWARM761-C4,3980

-0.5710066889632107,0.023173913043478222,2.6294782608695675,0.7163210702341137,0.7188959731543625,0.11605369127516785,5.4151778523489975,0.11643478260869565,5.282551839464882,0.7851036789297657,0.737852842809364,0.12473913043478262,5.235588628762537,0.23394648829431425,5.447013377926418,SWARM761-C4,3978

-0.5751538461538465,0.03138127090301001,2.7357558528428116,0.7180301003344485,0.717953177257525,0.137876254180602,5.9149832775919755,0.1413444816053511,5.899234113712378,0.7942140468227425,0.7343779264214042,0.1448060200668897,6.324090301003342,0.28602675585284293,6.248016722408029,SWARM761-C4,3977

-0.572558528428094,0.020548494983277557,3.2146053511705697,0.721993311036789,0.749443333333333,0.15047999999999995,7.005676666666665,0.1341872909698997,6.434103678929768,0.7835819397993307,0.721170568561873,0.1378428093645486,6.390190635451501,0.25918394648829435,6.62716722408027,SWARM761-C4,3976

-0.5702140468227427,0.009093645484949835,2.8254849498327754,0.7222374581939804,0.7250100334448158,0.1266254180602007,6.013411371237458,0.12878260869565214,5.855755852842811,0.7795986622073574,0.7307458193979932,0.13245819397993297,5.703347826086954,0.2538428093645485,5.830668896321071,SWARM761-C4,3975

-0.5771140939597315,0.03104026845637579,3.5493322147651,0.7223578595317727,0.7154147157190637,0.13121070234113716,6.4363076923076905,0.1325150501672241,6.828829431438129,0.7906822742474914,0.7296521739130437,0.14254849498327762,6.5379765886287595,0.2692040133779266,6.644321070234113,SWARM761-C4,3974

-0.6290201342281877,0.026822147651006673,3.901197986577183,0.7515752508361206,0.7170033444816056,0.10493645484949837,5.370040133779265,0.10287625418060209,7.60757190635451,0.7894046822742471,0.734672240802676,0.11079598662207349,5.679284280936457,0.21772909698996665,5.540842809364551,SWARM761-C4,3973

-0.5706655518394653,0.02932107023411369,3.0259665551839467,0.7402040133779271,0.7193745819397993,0.1267658862876254,6.64846488294314,0.13069230769230772,6.6243444816053465,0.780013377926421,0.7235083612040129,0.13413712374581943,6.647290969899667,0.2605585284280937,6.622826086956524,SWARM761-C4,3972

-0.5787826086956526,0.02671571906354511,2.944177257525083,0.721702341137124,0.7284682274247489,0.13489632107023414,6.077110367892977,0.1343745819397993,5.958143812709028,0.7805384615384607,0.7253311036789292,0.14179598662207357,5.862247491638797,0.2510401337792643,6.023280936454848,SWARM761-C4,3971

-0.5749664429530201,0.028077181208053664,2.682365771812082,0.7204347826086962,0.718274247491639,0.12512040133779273,5.4335484949832775,0.12389632107023411,5.546160535117058,0.7896421404682272,0.7255953177257525,0.1343946488294313,5.502384615384617,0.24891638795986618,5.568404682274251,SWARM761-C4,3970

-0.6355622895622897,0.03125252525252522,5.38030976430977,0.7253511705685615,0.7200606060606062,0.14185185185185178,6.282370370370367,0.14837458193979944,6.049933110367893,0.7777926421404682,0.7219030100334449,0.1461538461538461,6.157785953177257,0.2774147157190638,6.049508361204013,SWARM761-C4,3969

-0.577201342281879,0.03044630872483218,3.225969798657719,0.7200836120401335,0.7182742474916388,0.1312240802675585,6.153157190635448,0.12962207357859534,6.179137123745823,0.7876755852842807,0.7365083612040136,0.1362274247491638,6.122193979933114,0.2837759197324414,6.238096989966556,SWARM761-C4,3968

-0.5746755852842814,0.03218060200668893,2.9621538461538464,0.7185852842809366,0.7279632107023416,0.14631772575250832,6.487347826086962,0.14790301003344472,6.501882943143808,0.782294314381271,0.7251705685618725,0.15442140468227405,6.759882943143812,0.28450167224080275,6.735247491638796,SWARM761-C4,3967

-0.576217391304348,0.028444816053511686,2.5558963210702332,0.7170234113712376,0.7235919732441473,0.12115050167224087,5.602127090301007,0.12305685618729094,5.256190635451506,0.7828026755852845,0.7312341137123752,0.13125083612040128,5.518545150501667,0.2390367892976589,5.741123745819395,SWARM761-C4,3966

-0.5774228187919461,0.030708053691275137,2.697322147651008,0.7198394648829428,0.7237859531772576,0.09628762541806019,5.616030100334449,0.09859531772575254,5.623675585284281,0.7806321070234109,0.7251739130434784,0.10263879598662208,5.843872909698996,0.21496989966555188,5.957207357859533,SWARM761-C4,3965

-0.5759865771812078,0.036758389261744955,2.9319395973154356,0.7191003344481606,0.7192474916387955,0.14573578595317732,6.150274247491638,0.14988628762541822,6.08908695652174,0.779183946488294,0.7245484949832773,0.15834113712374584,6.401093645484949,0.28313377926421424,6.34434448160535,SWARM761-C4,3964

-0.5715785953177267,0.02948829431438124,2.569963210702343,0.7215150501672238,0.7117692307692307,0.14139464882943142,5.419297658862877,0.14605685618729103,5.411150501672242,0.7735250836120403,0.7209063545150497,0.15194983277591967,5.808812709030099,0.30766220735785954,5.701397993311034,SWARM761-C4,3963

-0.5768053691275166,0.026983221476510045,2.8690637583892604,0.7199498327759198,0.7162107023411376,0.10800334448160533,6.07056856187291,0.10929096989966552,5.998632107023413,0.7792274247491636,0.7225986622073576,0.1172006688963211,6.222327759197326,0.23226086956521733,6.21909364548495,SWARM761-C4,3962

-0.5704515050167226,0.004779264214046822,2.338668896321069,0.7290033444816054,0.7318394648829433,0.14307692307692305,5.60170568561873,0.14644481605351176,5.401070234113713,0.7798093645484946,0.7227257525083608,0.15390635451505028,5.593735785953175,0.2700602006688964,5.70205685618729,SWARM761-C4,3961

-0.5752742474916388,0.02985953177257523,3.652354515050168,0.722943143812709,0.719023411371238,0.11531103678929765,7.291615384615382,0.12005351170568579,7.3373678929765935,0.7779665551839458,0.7331040268456375,0.12381879194630867,8.060932885906047,0.24406688963210682,7.599769230769228,SWARM761-C4,3960

-0.5758628762541809,0.027698996655518334,3.0467725752508397,0.7240936454849499,0.7245685618729095,0.13715050167224088,5.992073578595318,0.13655852842809355,6.053441471571905,0.7777525083612038,0.7208260869565218,0.1453177257525084,6.1641438127090336,0.29341471571906375,6.183474916387964,SWARM761-C4,3959

-0.5716555183946486,0.013963210702341133,2.839137123745821,0.7176789297658863,0.7265752508361205,0.11275250836120411,6.166715719063547,0.11633110367892972,6.139591973244149,0.7850969899665547,0.7252408026755854,0.12110702341137125,6.688812709030095,0.23491638795986602,6.810224080267557,SWARM761-C4,3958

-0.5757550335570469,0.02671140939597311,3.0432315436241586,0.7180769230769226,0.7198561872909698,0.1518060200668896,6.77678929765886,0.15486956521739126,6.729003344481602,0.7791137123745819,0.7228428093645481,0.16300668896321063,6.807421404682276,0.294916387959866,6.971638795986624,SWARM761-C4,3957

-0.5803478260869562,0.027842809364548457,2.9640668896321087,0.7304347826086953,0.7179197324414713,0.10790969899665559,5.2156755852842815,0.0943913043478261,5.37356856187291,0.7793010033444817,0.7231204013377925,0.0979799331103679,5.293692307692307,0.19817056856187312,5.409314381270905,SWARM761-C4,3956

-0.5694766666666676,0.008683333333333331,2.6936533333333332,0.7262508361204014,0.7232842809364546,0.11707692307692308,6.631260869565214,0.11854515050167219,6.155856187290972,0.7816454849498329,0.7238026755852843,0.11863879598662207,6.392317725752511,0.23702341137123734,6.520341137123745,SWARM761-C4,3955

-0.574548494983278,0.022100334448160508,2.9308862876254222,0.718775919732442,0.7291204013377932,0.11472909698996649,6.1612207357859585,0.11296321070234122,6.402655518394644,0.7811571906354514,0.7315518394648833,0.12202006688963218,6.352224080267561,0.23767892976588612,6.365571906354518,SWARM761-C4,3954

-0.5709966555183953,0.02598996655518391,2.7516488294314385,0.7156387959866222,0.7175886287625423,0.1410903010033445,5.928197324414717,0.14296321070234097,5.96597658862876,0.7782307692307686,0.7218333333333334,0.14581666666666654,6.166476666666667,0.2758428093645488,6.127250836120403,SWARM761-C4,3953

-0.5881610738255031,0.04199664429530199,4.4503892617449665,0.7186387959866223,0.7201270903010033,0.16627759197324407,7.1302541806020034,0.16565217391304357,7.281110367892972,0.7953545150501673,0.7299866220735796,0.17521739130434777,7.093471571906351,0.34571237458193943,7.410267558528427,SWARM761-C4,3952

-0.5749597315436243,0.02822818791946305,3.3186006711409375,0.7248127090301002,0.7279464882943137,0.12283277591973253,6.584060200668893,0.12557190635451507,6.747956521739128,0.7836387959866216,0.7240869565217387,0.12625083612040133,6.698882943143808,0.24532441471571917,6.740371237458191,SWARM761-C4,3950

-0.575371237458194,0.03710033444816051,2.878438127090303,0.7132107023411373,0.7228829431438122,0.13410702341137115,6.200508361204016,0.13815384615384624,6.081260869565215,0.774755852842809,0.7291610738255043,0.15064765100671137,6.369453020134225,0.3058829431438127,6.344110367892976,SWARM761-C4,3949

-0.5724228187919461,0.027265100671140914,2.9698020134228207,0.714826666666666,0.7204481605351177,0.13995317725752507,6.2426488294314355,0.13393999999999995,6.064716666666666,0.7910301003344489,0.7317751677852352,0.1510973154362415,6.3687248322147685,0.2915852842809366,6.334889632107021,SWARM761-C4,3948

-0.5734566666666668,0.015013333333333332,3.440013333333335,0.7182240802675586,0.7190468227424744,0.12867892976588632,6.937060200668899,0.13136789297658866,7.148541806020069,0.7881003344481601,0.7240334448160537,0.13621739130434793,7.038762541806021,0.2504648829431438,7.169210702341139,SWARM761-C4,3947

-0.57447491638796,0.026424749163879574,2.9051471571906378,0.7233846153846155,0.716030100334448,0.13744481605351178,6.250474916387961,0.14111705685618733,6.210290969899667,0.7789698996655517,0.7261174496644296,0.14960738255033562,6.679946308724833,0.27532775919732455,6.440294314381269,SWARM761-C4,3946

-0.5732909698996652,0.01372240802675585,2.580137123745821,0.7191605351170569,0.718361204013378,0.1116789297658863,5.136545150501671,0.11453511705685619,5.16003678929766,0.7866889632107019,0.7243478260869568,0.12128762541806022,5.217137123745819,0.22709030100334435,5.278638795986625,SWARM761-C4,3945

-0.5691778523489935,0.031157718120805335,2.4849563758389266,0.7168193979933111,0.7227859531772577,0.126571906354515,5.277234113712377,0.1268795986622074,5.2109297658862905,0.7897290969899662,0.7332809364548489,0.13152173913043488,5.341391304347826,0.24585953177257525,5.706842809364547,SWARM761-C4,3944

-0.6153712374581942,0.01428428093645485,3.719795986622075,0.7489531772575254,0.7196521739130437,0.12077926421404682,5.451635451505016,0.11850501672240815,6.606157190635451,0.7777926421404683,0.7345685618729093,0.12581605351170566,5.552277591973241,0.23914715719063528,5.621130434782607,SWARM761-C4,3943

-0.5787324414715715,0.008414715719063545,2.583739130434783,0.7267391304347823,0.7169397993311034,0.11436789297658864,5.122732441471573,0.11625752508361215,5.303264214046824,0.7878695652173908,0.7249464882943146,0.12842140468227403,5.194023411371235,0.230458193979933,5.329648829431438,SWARM761-C4,3942

-0.5808193979933111,0.03350836120401335,2.6704481605351154,0.7188327759197319,0.7249598662207358,0.11968561872909704,5.431969899665549,0.11832775919732447,5.109163879598663,0.7884448160535117,0.7237090301003339,0.12432441471571908,5.360618729096989,0.25609030100334457,5.36935117056856,SWARM761-C4,3941

-0.574521739130435,0.029555183946488234,2.848046822742476,0.7199531772575254,0.7295819397993318,0.11516053511705686,6.743143812709031,0.11270903010033445,6.481354515050172,0.7785886287625418,0.724839464882943,0.12243478260869561,6.550826086956522,0.23760869565217357,6.614779264214045,SWARM761-C4,3940

-0.5739464882943142,0.027578595317725725,2.6171036789297673,0.7193311036789303,0.7194548494983283,0.11300000000000007,5.107367892976584,0.11443143812709039,5.11719063545151,0.7838428093645485,0.7306733333333334,0.11910333333333337,5.496940000000003,0.2266755852842809,5.329535117056859,SWARM761-C4,3939

-0.5754581939799334,0.020842809364548482,2.549575250836122,0.7221237458193975,0.7345986622073574,0.09451505016722404,5.479702341137122,0.09562876254180605,5.267518394648832,0.7792374581939796,0.7249799331103677,0.10226086956521745,5.141418060200667,0.20433110367892968,5.222692307692307,SWARM761-C4,3938

-0.5750100000000005,0.009996666666666668,3.086056666666669,0.7236086956521741,0.7253712374581944,0.12707357859531773,5.929913043478258,0.13067558528428105,5.950270903010037,0.7950735785953177,0.7301705685618731,0.13850167224080265,5.941481605351171,0.24743812709030089,6.098946488294315,SWARM761-C4,3937

-0.5740602006688966,0.023240802675585245,2.733501672240804,0.7198595317725752,0.7197157190635455,0.12111705685618739,5.5181070234113685,0.12225083612040143,5.550618729096993,0.7797725752508357,0.7396488294314386,0.12805016722408027,5.505110367892976,0.23715050167224058,5.576153846153849,SWARM761-C4,3936

-0.5748724832214769,0.036087248322147616,3.095946308724834,0.7178394648829431,0.7167424749163883,0.13453846153846152,6.8643745819398,0.1353110367892976,6.833725752508358,0.777538461538461,0.721401337792642,0.14866220735785948,7.136227424749166,0.2787759197324416,6.961277591973247,SWARM761-C4,3935

-0.5765317725752507,0.03670903010033443,2.8997926421404703,0.7162709030100335,0.7175117056856187,0.14148160535117055,6.134498327759198,0.13748160535117063,6.1754046822742445,0.7783277591973243,0.7310969899665549,0.15195317725752516,6.402923076923078,0.28137458193979964,6.353698996655516,SWARM761-C4,3934

-0.584655518394649,0.009100334448160536,4.376494983277592,0.7250334448160537,0.7227692307692313,0.13601003344481605,7.695916387959864,0.13640802675585276,7.609023411371235,0.7923678929765879,0.7411471571906355,0.14424414715719072,7.950511705685625,0.25483277591973236,8.0006889632107,SWARM761-C4,3933

-0.5807348993288584,0.025318791946308684,2.6897751677852364,0.7190668896321072,0.7178762541806021,0.11560869565217398,5.270545150501672,0.1094548494983278,5.168267558528428,0.7902651006711412,0.7260100334448166,0.12104347826086971,5.370755852842809,0.2378187919463085,5.578224832214766,SWARM761-C4,3932

-0.5774113712374584,0.042050167224080254,2.6277424749163876,0.7204414715719061,0.7121270903010033,0.1452642140468227,5.637217391304347,0.14780602006688964,5.491254180602009,0.7730836120401332,0.7263076923076921,0.16376588628762548,5.916140468227428,0.3380836120401338,5.758020066889626,SWARM761-C4,3931

-0.5781375838926172,0.03122483221476507,2.9609664429530187,0.7217458193979939,0.7205284280936451,0.11927759197324406,5.839842809364545,0.11865217391304352,5.873210702341136,0.7955652173913045,0.7262976588628756,0.12223745819397994,5.831240802675589,0.24547826086956512,6.092137123745817,SWARM761-C4,3930

-0.5750869565217391,0.02844481605351168,2.482357859531773,0.7334715719063547,0.7194280936454851,0.13139799331103674,5.259966555183941,0.1366755852842809,5.262207357859528,0.7797959866220733,0.7266889632107029,0.14006354515050176,5.503187290969905,0.26532441471571927,5.513321070234116,SWARM761-C4,3929

-0.5746789297658864,0.021575250836120368,2.3915752508361208,0.721842809364548,0.7203745819397994,0.08385618729096996,5.044956521739134,0.0875117056856188,5.06289966555184,0.7904949832775919,0.7281371237458192,0.09017056856187294,5.341331103678936,0.226257525083612,5.38454180602007,SWARM761-C4,3928

-0.5736174496644303,0.030187919463087215,3.6837281879194617,0.7215484949832781,0.6901304347826083,0.1064816053511706,6.131588628762536,0.10820735785953177,7.442615384615385,0.7841337792642142,0.7486856187290972,0.11374247491638793,8.119117056856192,0.24284949832775918,7.840364548494984,SWARM761-C4,3927

-0.5731677852348995,0.033291946308724805,2.522322147651006,0.7194749163879592,0.7276521739130436,0.10267558528428103,5.116133779264213,0.1035551839464883,5.132458193979931,0.7810301003344481,0.7270234113712379,0.10844147157190645,5.295374581939799,0.21746153846153846,5.40732441471572,SWARM761-C4,3926

-0.5767449664429534,0.030604026845637552,2.5929328859060394,0.7274381270903013,0.7207826086956522,0.08611705685618734,5.52629431438127,0.08887959866220735,5.531130434782605,0.7790535117056855,0.7371170568561869,0.09586287625418066,5.705351170568563,0.20651839464882946,5.890705685618732,SWARM761-C4,3925

-0.5721538461538462,0.025632107023411333,2.8410434782608704,0.735889632107024,0.7223846153846152,0.12678595317725747,6.143401337792643,0.13158528428093647,5.874698996655517,0.8451107382550331,0.7266354515050167,0.1354782608695651,6.20066220735786,0.25717449664429526,8.501090604026855,SWARM761-C4,3924

-0.5713645484949832,0.014377926421404673,3.018919732441472,0.7315585284280933,0.719809364548495,0.11922408026755849,6.397147157190638,0.11393645484949835,6.3150267558528395,0.7963221476510065,0.7268026755852846,0.1261103678929765,6.716200668896324,0.273553691275168,7.242073825503357,SWARM761-C4,3923

-0.5729362416107382,0.029453020134228163,2.9319127516778507,0.7225551839464884,0.7256555183946486,0.11894648829431437,5.6302274247491635,0.11848829431438124,5.788408026755855,0.7810969899665551,0.7290501672240801,0.1291571906354516,5.626351170568564,0.25159531772575244,5.850003344481604,SWARM761-C4,3922

-0.5758428093645483,0.03633444816053507,2.631418060200669,0.7201806020066885,0.7199464882943144,0.1220033444816054,5.266183946488291,0.12759866220735794,5.243414715719068,0.7790434782608694,0.7255785953177253,0.1292307692307692,5.5810635451505,0.25682608695652165,5.589692307692307,SWARM761-C4,3921

-0.5707684563758393,0.03206711409395971,3.0611812080536915,0.7184648829431434,0.7158795986622074,0.13064882943143824,5.957739130434781,0.13187625418060198,5.9663745819397995,0.8540402684563759,0.7217290969899668,0.1427090301003344,6.001749163879603,0.29916442953020167,8.81292281879195,SWARM761-C4,3920

+0.5737725752508361,0.030150501672240797,2.872792642140466,0.7187224080267561,0.7177424749163881,0.12533444816053507,5.542765886287626,0.12189297658862878,5.572204013377922,0.7852709030100331,0.7358394648829435,0.1318595317725753,5.684491638795985,0.24972575250836118,5.705953177257523,BUILD170-A9,4001

+0.5700200668896318,0.02394983277591971,3.273270903010033,0.7300200668896324,0.7192006688963207,0.12675919732441476,6.993692307692311,0.12455518394648829,6.917822742474914,0.7867892976588625,0.7245083612040136,0.1371973244147158,7.093695652173919,0.27090301003344497,7.147177257525083,BUILD130-A9,4000

+0.5741375838926172,0.02351342281879194,2.5692483221476525,0.7196287625418062,0.7349464882943149,0.09894648829431434,5.5296521739130435,0.09775919732441471,5.465822742474917,0.7810133779264207,0.7954882154882156,0.10468686868686869,7.647148148148149,0.20208361204013373,5.6527458193979925,BUILD182-A9,3999

+0.5772742474916389,0.008816053511705687,2.798759197324414,0.7176454849498326,0.7274782608695646,0.13828093645484943,6.051581939799331,0.13235451505016724,6.0693745819398,0.779183946488294,0.726501672240803,0.14813712374581933,6.35122073578595,0.2564949832775919,6.287622073578598,BUILD168-A9,3998

+0.5769966329966328,0.03242424242424239,2.5547239057239044,0.7277090301003343,0.7176655518394647,0.11656187290969901,5.189702341137124,0.1165819397993311,5.202237458193981,0.7802909698996652,0.7237658862876254,0.12438127090301014,5.381438127090299,0.23398996655518392,5.33338795986622,BUILD138-A9,3997

+0.5797959866220737,0.025040133779264173,3.6298862876254168,0.7176655518394647,0.7213244147157186,0.12470903010033434,6.865411371237459,0.1207056856187292,6.773598662207364,0.7931270903010028,0.7267859531772576,0.13173913043478255,6.850327759197326,0.24390301003344445,7.365020066889637,BUILD177-A9,3996

+0.5751170568561875,0.0279163879598662,3.01229431438127,0.7268327759197325,0.7188595317725756,0.10694983277591977,6.321357859531768,0.10729431438127096,6.272548494983277,0.7941371237458188,0.7284127516778522,0.11568791946308728,6.7030100671140955,0.23475919732441475,6.549926421404683,BUILD175-A9,3995

+0.5719966555183951,0.023816053511705647,3.0481839464882947,0.7228993288590606,0.7187725752508362,0.12845484949832778,6.2951103678929785,0.1312818791946309,6.463949664429528,0.7804749163879597,0.725464882943144,0.13783946488294324,6.206605351170567,0.2533545150501672,6.480859531772572,BUILD168-A9,3994

+0.5878461538461536,0.025354515050167194,3.5771036789297654,0.7266989966555187,0.725848993288591,0.12475838926174491,6.731359060402689,0.12955518394648824,6.260966555183948,0.7775150501672236,0.7168060200668896,0.13292642140468228,7.808695652173912,0.2540702341137124,7.797739130434791,BUILD21-B1,3993

+0.5830233333333332,0.00933,2.383490000000001,0.7336555183946492,0.7235652173913049,0.1292541806020068,5.132230769230772,0.12777926421404687,5.02438127090301,0.8023879598662204,0.7293244147157191,0.13685284280936452,5.2357558528428045,0.2505016722408026,5.428852842809364,BUILD161-A9,3992

+0.5830936454849504,0.029294314381270857,4.162518394648829,0.715334448160535,0.7247324414715725,0.11882943143812716,7.751080267558524,0.12002006688963217,7.8378193979933055,0.7805016722408025,0.7217190635451505,0.13004347826086962,7.8579832775919725,0.24649498327759212,7.9921939799331065,BUILD125-A9,3991

+0.571341137123746,0.026197324414715697,2.394046822742474,0.7206923076923076,0.7298628762541802,0.13220066889632112,5.481896321070235,0.13425083612040137,5.246896321070233,0.7806588628762541,0.7261409395973155,0.14293288590604028,5.48185570469799,0.26045150501672243,5.430408026755849,BUILD162-A9,3990

+0.5731906354515054,0.025468227424749135,2.962234113712376,0.7243745819397991,0.7190602006688964,0.11862876254180602,6.106240802675584,0.12156521739130433,6.137625418060202,0.7798260869565221,0.7316120401337787,0.1265953177257526,6.023331103678928,0.23920735785953162,6.218662207357857,BUILD126-A9,3989

+0.5778996655518394,0.0085685618729097,2.8353545150501684,0.7179030100334444,0.7175953177257526,0.11557859531772578,5.916528428093644,0.11364548494983279,5.802494983277595,0.7897056856187293,0.7224782608695652,0.12314715719063547,5.788006688963207,0.2642073578595318,6.330133779264214,BUILD118-A9,3988

+0.569367892976589,0.026207357859531755,3.0752909698996644,0.7168361204013375,0.7145418060200662,0.1273846153846154,7.008060200668897,0.13000668896321071,6.847876254180603,0.7772408026755855,0.7265117056856191,0.13559866220735786,7.034103678929761,0.2513277591973243,7.161434782608697,BUILD137-A9,3987

+0.5704194630872486,0.0317181208053691,2.5595369127516783,0.7200969899665558,0.7234949832775925,0.14043478260869569,5.3492040133779275,0.14318394648829433,5.255555183946487,0.8063344481605352,0.7339030100334446,0.14436454849498329,5.2371471571906385,0.27155518394648825,5.572137123745825,BUILD162-A9,3986

+0.5750635451505021,0.025729096989966536,2.656103678929765,0.7238394648829427,0.7205819397993309,0.12108361204013375,5.535635451505015,0.12225752508361201,5.445695652173916,0.7956199999999991,0.737836120401338,0.1299030100334448,5.338969899665548,0.23949666666666686,5.515420000000001,BUILD143-A9,3985

+0.5726677852348995,0.029503355704697976,3.0893758389261743,0.7187625418060208,0.7190301003344484,0.12803678929765894,6.9882775919732465,0.1282107023411371,6.9719832775919715,0.7779464882943141,0.7248657718120795,0.1331442953020134,7.283278523489931,0.24979933110367888,7.119351170568563,BUILD137-A9,3984

+0.5838233333333331,0.009076666666666667,5.471726666666671,0.7201137123745815,0.7287056856187292,0.11536789297658868,9.008464882943144,0.11412040133779276,9.12337123745819,0.7902675585284276,0.7263143812709028,0.12333110367892972,8.831668896321075,0.24242809364548487,9.020381270903012,BUILD125-A9,3983

+0.5823478260869562,0.025063545150501635,3.427000000000001,0.7304766666666661,0.7374448160535126,0.1571906354515051,6.492956521739126,0.13785999999999993,6.582673333333332,0.7986989966555178,0.7323478260869568,0.14121739130434788,6.1969698996655485,0.2635953177257524,6.329742474916388,BUILD120-A9,3982

+0.5521739130434787,0.009682274247491638,4.952260869565218,0.6794481605351173,0.673304347826087,0.1347692307692308,8.050598662207355,0.13612709030100337,7.898254180602011,0.743665551839465,0.683612040133779,0.14267558528428093,8.338658862876246,0.2841638795986622,8.337100334448156,BUILD149-A9,3981

+0.5759464882943147,0.027535117056856143,3.0257458193979914,0.7240100334448165,0.722227424749164,0.13526086956521752,6.157772575250841,0.1373745819397993,6.260173913043477,0.7798963210702337,0.7268494983277592,0.1557959866220736,6.213709030100337,0.2704916387959868,6.265080267558525,BUILD120-A9,3980

+0.5710066889632107,0.023173913043478222,2.6294782608695675,0.7163210702341137,0.7188959731543625,0.11605369127516785,5.4151778523489975,0.11643478260869565,5.282551839464882,0.7851036789297657,0.737852842809364,0.12473913043478262,5.235588628762537,0.23394648829431425,5.447013377926418,BUILD138-A9,3978

+0.5751538461538465,0.03138127090301001,2.7357558528428116,0.7180301003344485,0.717953177257525,0.137876254180602,5.9149832775919755,0.1413444816053511,5.899234113712378,0.7942140468227425,0.7343779264214042,0.1448060200668897,6.324090301003342,0.28602675585284293,6.248016722408029,BUILD166-A9,3977

+0.572558528428094,0.020548494983277557,3.2146053511705697,0.721993311036789,0.749443333333333,0.15047999999999995,7.005676666666665,0.1341872909698997,6.434103678929768,0.7835819397993307,0.721170568561873,0.1378428093645486,6.390190635451501,0.25918394648829435,6.62716722408027,BUILD120-A9,3976

+0.5702140468227427,0.009093645484949835,2.8254849498327754,0.7222374581939804,0.7250100334448158,0.1266254180602007,6.013411371237458,0.12878260869565214,5.855755852842811,0.7795986622073574,0.7307458193979932,0.13245819397993297,5.703347826086954,0.2538428093645485,5.830668896321071,BUILD21-B1,3975

+0.5771140939597315,0.03104026845637579,3.5493322147651,0.7223578595317727,0.7154147157190637,0.13121070234113716,6.4363076923076905,0.1325150501672241,6.828829431438129,0.7906822742474914,0.7296521739130437,0.14254849498327762,6.5379765886287595,0.2692040133779266,6.644321070234113,BUILD180-A9,3974

+0.6290201342281877,0.026822147651006673,3.901197986577183,0.7515752508361206,0.7170033444816056,0.10493645484949837,5.370040133779265,0.10287625418060209,7.60757190635451,0.7894046822742471,0.734672240802676,0.11079598662207349,5.679284280936457,0.21772909698996665,5.540842809364551,BUILD145-A9,3973

+0.5706655518394653,0.02932107023411369,3.0259665551839467,0.7402040133779271,0.7193745819397993,0.1267658862876254,6.64846488294314,0.13069230769230772,6.6243444816053465,0.780013377926421,0.7235083612040129,0.13413712374581943,6.647290969899667,0.2605585284280937,6.622826086956524,BUILD174-A9,3972

+0.5787826086956526,0.02671571906354511,2.944177257525083,0.721702341137124,0.7284682274247489,0.13489632107023414,6.077110367892977,0.1343745819397993,5.958143812709028,0.7805384615384607,0.7253311036789292,0.14179598662207357,5.862247491638797,0.2510401337792643,6.023280936454848,BUILD146-A9,3971

+0.5749664429530201,0.028077181208053664,2.682365771812082,0.7204347826086962,0.718274247491639,0.12512040133779273,5.4335484949832775,0.12389632107023411,5.546160535117058,0.7896421404682272,0.7255953177257525,0.1343946488294313,5.502384615384617,0.24891638795986618,5.568404682274251,BUILD133-A9,3970

+0.6355622895622897,0.03125252525252522,5.38030976430977,0.7253511705685615,0.7200606060606062,0.14185185185185178,6.282370370370367,0.14837458193979944,6.049933110367893,0.7777926421404682,0.7219030100334449,0.1461538461538461,6.157785953177257,0.2774147157190638,6.049508361204013,BUILD166-A9,3969

+0.577201342281879,0.03044630872483218,3.225969798657719,0.7200836120401335,0.7182742474916388,0.1312240802675585,6.153157190635448,0.12962207357859534,6.179137123745823,0.7876755852842807,0.7365083612040136,0.1362274247491638,6.122193979933114,0.2837759197324414,6.238096989966556,BUILD120-A9,3968

+0.5746755852842814,0.03218060200668893,2.9621538461538464,0.7185852842809366,0.7279632107023416,0.14631772575250832,6.487347826086962,0.14790301003344472,6.501882943143808,0.782294314381271,0.7251705685618725,0.15442140468227405,6.759882943143812,0.28450167224080275,6.735247491638796,BUILD158-A9,3967

+0.576217391304348,0.028444816053511686,2.5558963210702332,0.7170234113712376,0.7235919732441473,0.12115050167224087,5.602127090301007,0.12305685618729094,5.256190635451506,0.7828026755852845,0.7312341137123752,0.13125083612040128,5.518545150501667,0.2390367892976589,5.741123745819395,BUILD143-A9,3966

+0.5774228187919461,0.030708053691275137,2.697322147651008,0.7198394648829428,0.7237859531772576,0.09628762541806019,5.616030100334449,0.09859531772575254,5.623675585284281,0.7806321070234109,0.7251739130434784,0.10263879598662208,5.843872909698996,0.21496989966555188,5.957207357859533,BUILD129-A9,3965

+0.5759865771812078,0.036758389261744955,2.9319395973154356,0.7191003344481606,0.7192474916387955,0.14573578595317732,6.150274247491638,0.14988628762541822,6.08908695652174,0.779183946488294,0.7245484949832773,0.15834113712374584,6.401093645484949,0.28313377926421424,6.34434448160535,BUILD179-A9,3964

+0.5715785953177267,0.02948829431438124,2.569963210702343,0.7215150501672238,0.7117692307692307,0.14139464882943142,5.419297658862877,0.14605685618729103,5.411150501672242,0.7735250836120403,0.7209063545150497,0.15194983277591967,5.808812709030099,0.30766220735785954,5.701397993311034,BUILD148-A9,3963

+0.5768053691275166,0.026983221476510045,2.8690637583892604,0.7199498327759198,0.7162107023411376,0.10800334448160533,6.07056856187291,0.10929096989966552,5.998632107023413,0.7792274247491636,0.7225986622073576,0.1172006688963211,6.222327759197326,0.23226086956521733,6.21909364548495,BUILD175-A9,3962

+0.5704515050167226,0.004779264214046822,2.338668896321069,0.7290033444816054,0.7318394648829433,0.14307692307692305,5.60170568561873,0.14644481605351176,5.401070234113713,0.7798093645484946,0.7227257525083608,0.15390635451505028,5.593735785953175,0.2700602006688964,5.70205685618729,BUILD147-A9,3961

+0.5752742474916388,0.02985953177257523,3.652354515050168,0.722943143812709,0.719023411371238,0.11531103678929765,7.291615384615382,0.12005351170568579,7.3373678929765935,0.7779665551839458,0.7331040268456375,0.12381879194630867,8.060932885906047,0.24406688963210682,7.599769230769228,BUILD125-A9,3960

+0.5758628762541809,0.027698996655518334,3.0467725752508397,0.7240936454849499,0.7245685618729095,0.13715050167224088,5.992073578595318,0.13655852842809355,6.053441471571905,0.7777525083612038,0.7208260869565218,0.1453177257525084,6.1641438127090336,0.29341471571906375,6.183474916387964,BUILD139-A9,3959

+0.5716555183946486,0.013963210702341133,2.839137123745821,0.7176789297658863,0.7265752508361205,0.11275250836120411,6.166715719063547,0.11633110367892972,6.139591973244149,0.7850969899665547,0.7252408026755854,0.12110702341137125,6.688812709030095,0.23491638795986602,6.810224080267557,BUILD144-A9,3958

+0.5757550335570469,0.02671140939597311,3.0432315436241586,0.7180769230769226,0.7198561872909698,0.1518060200668896,6.77678929765886,0.15486956521739126,6.729003344481602,0.7791137123745819,0.7228428093645481,0.16300668896321063,6.807421404682276,0.294916387959866,6.971638795986624,BUILD158-A9,3957

+0.5803478260869562,0.027842809364548457,2.9640668896321087,0.7304347826086953,0.7179197324414713,0.10790969899665559,5.2156755852842815,0.0943913043478261,5.37356856187291,0.7793010033444817,0.7231204013377925,0.0979799331103679,5.293692307692307,0.19817056856187312,5.409314381270905,BUILD153-A9,3956

+0.5694766666666676,0.008683333333333331,2.6936533333333332,0.7262508361204014,0.7232842809364546,0.11707692307692308,6.631260869565214,0.11854515050167219,6.155856187290972,0.7816454849498329,0.7238026755852843,0.11863879598662207,6.392317725752511,0.23702341137123734,6.520341137123745,BUILD144-A9,3955

+0.574548494983278,0.022100334448160508,2.9308862876254222,0.718775919732442,0.7291204013377932,0.11472909698996649,6.1612207357859585,0.11296321070234122,6.402655518394644,0.7811571906354514,0.7315518394648833,0.12202006688963218,6.352224080267561,0.23767892976588612,6.365571906354518,BUILD144-A9,3954

+0.5709966555183953,0.02598996655518391,2.7516488294314385,0.7156387959866222,0.7175886287625423,0.1410903010033445,5.928197324414717,0.14296321070234097,5.96597658862876,0.7782307692307686,0.7218333333333334,0.14581666666666654,6.166476666666667,0.2758428093645488,6.127250836120403,BUILD166-A9,3953

+0.5881610738255031,0.04199664429530199,4.4503892617449665,0.7186387959866223,0.7201270903010033,0.16627759197324407,7.1302541806020034,0.16565217391304357,7.281110367892972,0.7953545150501673,0.7299866220735796,0.17521739130434777,7.093471571906351,0.34571237458193943,7.410267558528427,BUILD154-A9,3952

+0.5749597315436243,0.02822818791946305,3.3186006711409375,0.7248127090301002,0.7279464882943137,0.12283277591973253,6.584060200668893,0.12557190635451507,6.747956521739128,0.7836387959866216,0.7240869565217387,0.12625083612040133,6.698882943143808,0.24532441471571917,6.740371237458191,BUILD177-A9,3950

+0.575371237458194,0.03710033444816051,2.878438127090303,0.7132107023411373,0.7228829431438122,0.13410702341137115,6.200508361204016,0.13815384615384624,6.081260869565215,0.774755852842809,0.7291610738255043,0.15064765100671137,6.369453020134225,0.3058829431438127,6.344110367892976,BUILD139-A9,3949

+0.5724228187919461,0.027265100671140914,2.9698020134228207,0.714826666666666,0.7204481605351177,0.13995317725752507,6.2426488294314355,0.13393999999999995,6.064716666666666,0.7910301003344489,0.7317751677852352,0.1510973154362415,6.3687248322147685,0.2915852842809366,6.334889632107021,BUILD120-A9,3948

+0.5734566666666668,0.015013333333333332,3.440013333333335,0.7182240802675586,0.7190468227424744,0.12867892976588632,6.937060200668899,0.13136789297658866,7.148541806020069,0.7881003344481601,0.7240334448160537,0.13621739130434793,7.038762541806021,0.2504648829431438,7.169210702341139,BUILD130-A9,3947

+0.57447491638796,0.026424749163879574,2.9051471571906378,0.7233846153846155,0.716030100334448,0.13744481605351178,6.250474916387961,0.14111705685618733,6.210290969899667,0.7789698996655517,0.7261174496644296,0.14960738255033562,6.679946308724833,0.27532775919732455,6.440294314381269,BUILD142-A9,3946

+0.5732909698996652,0.01372240802675585,2.580137123745821,0.7191605351170569,0.718361204013378,0.1116789297658863,5.136545150501671,0.11453511705685619,5.16003678929766,0.7866889632107019,0.7243478260869568,0.12128762541806022,5.217137123745819,0.22709030100334435,5.278638795986625,BUILD172-A9,3945

+0.5691778523489935,0.031157718120805335,2.4849563758389266,0.7168193979933111,0.7227859531772577,0.126571906354515,5.277234113712377,0.1268795986622074,5.2109297658862905,0.7897290969899662,0.7332809364548489,0.13152173913043488,5.341391304347826,0.24585953177257525,5.706842809364547,BUILD176-A9,3944

+0.6153712374581942,0.01428428093645485,3.719795986622075,0.7489531772575254,0.7196521739130437,0.12077926421404682,5.451635451505016,0.11850501672240815,6.606157190635451,0.7777926421404683,0.7345685618729093,0.12581605351170566,5.552277591973241,0.23914715719063528,5.621130434782607,BUILD143-A9,3943

+0.5787324414715715,0.008414715719063545,2.583739130434783,0.7267391304347823,0.7169397993311034,0.11436789297658864,5.122732441471573,0.11625752508361215,5.303264214046824,0.7878695652173908,0.7249464882943146,0.12842140468227403,5.194023411371235,0.230458193979933,5.329648829431438,BUILD172-A9,3942

+0.5808193979933111,0.03350836120401335,2.6704481605351154,0.7188327759197319,0.7249598662207358,0.11968561872909704,5.431969899665549,0.11832775919732447,5.109163879598663,0.7884448160535117,0.7237090301003339,0.12432441471571908,5.360618729096989,0.25609030100334457,5.36935117056856,BUILD172-A9,3941

+0.574521739130435,0.029555183946488234,2.848046822742476,0.7199531772575254,0.7295819397993318,0.11516053511705686,6.743143812709031,0.11270903010033445,6.481354515050172,0.7785886287625418,0.724839464882943,0.12243478260869561,6.550826086956522,0.23760869565217357,6.614779264214045,BUILD144-A9,3940

+0.5739464882943142,0.027578595317725725,2.6171036789297673,0.7193311036789303,0.7194548494983283,0.11300000000000007,5.107367892976584,0.11443143812709039,5.11719063545151,0.7838428093645485,0.7306733333333334,0.11910333333333337,5.496940000000003,0.2266755852842809,5.329535117056859,BUILD172-A9,3939

+0.5754581939799334,0.020842809364548482,2.549575250836122,0.7221237458193975,0.7345986622073574,0.09451505016722404,5.479702341137122,0.09562876254180605,5.267518394648832,0.7792374581939796,0.7249799331103677,0.10226086956521745,5.141418060200667,0.20433110367892968,5.222692307692307,BUILD127-A9,3938

+0.5750100000000005,0.009996666666666668,3.086056666666669,0.7236086956521741,0.7253712374581944,0.12707357859531773,5.929913043478258,0.13067558528428105,5.950270903010037,0.7950735785953177,0.7301705685618731,0.13850167224080265,5.941481605351171,0.24743812709030089,6.098946488294315,BUILD131-A9,3937

+0.5740602006688966,0.023240802675585245,2.733501672240804,0.7198595317725752,0.7197157190635455,0.12111705685618739,5.5181070234113685,0.12225083612040143,5.550618729096993,0.7797725752508357,0.7396488294314386,0.12805016722408027,5.505110367892976,0.23715050167224058,5.576153846153849,BUILD140-A9,3936

+0.5748724832214769,0.036087248322147616,3.095946308724834,0.7178394648829431,0.7167424749163883,0.13453846153846152,6.8643745819398,0.1353110367892976,6.833725752508358,0.777538461538461,0.721401337792642,0.14866220735785948,7.136227424749166,0.2787759197324416,6.961277591973247,BUILD149-A9,3935

+0.5765317725752507,0.03670903010033443,2.8997926421404703,0.7162709030100335,0.7175117056856187,0.14148160535117055,6.134498327759198,0.13748160535117063,6.1754046822742445,0.7783277591973243,0.7310969899665549,0.15195317725752516,6.402923076923078,0.28137458193979964,6.353698996655516,BUILD142-A9,3934

+0.584655518394649,0.009100334448160536,4.376494983277592,0.7250334448160537,0.7227692307692313,0.13601003344481605,7.695916387959864,0.13640802675585276,7.609023411371235,0.7923678929765879,0.7411471571906355,0.14424414715719072,7.950511705685625,0.25483277591973236,8.0006889632107,BUILD141-A9,3933

+0.5807348993288584,0.025318791946308684,2.6897751677852364,0.7190668896321072,0.7178762541806021,0.11560869565217398,5.270545150501672,0.1094548494983278,5.168267558528428,0.7902651006711412,0.7260100334448166,0.12104347826086971,5.370755852842809,0.2378187919463085,5.578224832214766,BUILD136-A9,3932

+0.5774113712374584,0.042050167224080254,2.6277424749163876,0.7204414715719061,0.7121270903010033,0.1452642140468227,5.637217391304347,0.14780602006688964,5.491254180602009,0.7730836120401332,0.7263076923076921,0.16376588628762548,5.916140468227428,0.3380836120401338,5.758020066889626,BUILD148-A9,3931

+0.5781375838926172,0.03122483221476507,2.9609664429530187,0.7217458193979939,0.7205284280936451,0.11927759197324406,5.839842809364545,0.11865217391304352,5.873210702341136,0.7955652173913045,0.7262976588628756,0.12223745819397994,5.831240802675589,0.24547826086956512,6.092137123745817,BUILD126-A9,3930

+0.5750869565217391,0.02844481605351168,2.482357859531773,0.7334715719063547,0.7194280936454851,0.13139799331103674,5.259966555183941,0.1366755852842809,5.262207357859528,0.7797959866220733,0.7266889632107029,0.14006354515050176,5.503187290969905,0.26532441471571927,5.513321070234116,BUILD147-A9,3929

+0.5746789297658864,0.021575250836120368,2.3915752508361208,0.721842809364548,0.7203745819397994,0.08385618729096996,5.044956521739134,0.0875117056856188,5.06289966555184,0.7904949832775919,0.7281371237458192,0.09017056856187294,5.341331103678936,0.226257525083612,5.38454180602007,BUILD127-A9,3928

+0.5736174496644303,0.030187919463087215,3.6837281879194617,0.7215484949832781,0.6901304347826083,0.1064816053511706,6.131588628762536,0.10820735785953177,7.442615384615385,0.7841337792642142,0.7486856187290972,0.11374247491638793,8.119117056856192,0.24284949832775918,7.840364548494984,BUILD125-A9,3927

+0.5731677852348995,0.033291946308724805,2.522322147651006,0.7194749163879592,0.7276521739130436,0.10267558528428103,5.116133779264213,0.1035551839464883,5.132458193979931,0.7810301003344481,0.7270234113712379,0.10844147157190645,5.295374581939799,0.21746153846153846,5.40732441471572,BUILD136-A9,3926

+0.5767449664429534,0.030604026845637552,2.5929328859060394,0.7274381270903013,0.7207826086956522,0.08611705685618734,5.52629431438127,0.08887959866220735,5.531130434782605,0.7790535117056855,0.7371170568561869,0.09586287625418066,5.705351170568563,0.20651839464882946,5.890705685618732,BUILD129-A9,3925

+0.5721538461538462,0.025632107023411333,2.8410434782608704,0.735889632107024,0.7223846153846152,0.12678595317725747,6.143401337792643,0.13158528428093647,5.874698996655517,0.8451107382550331,0.7266354515050167,0.1354782608695651,6.20066220735786,0.25717449664429526,8.501090604026855,BUILD155-A9,3924

+0.5713645484949832,0.014377926421404673,3.018919732441472,0.7315585284280933,0.719809364548495,0.11922408026755849,6.397147157190638,0.11393645484949835,6.3150267558528395,0.7963221476510065,0.7268026755852846,0.1261103678929765,6.716200668896324,0.273553691275168,7.242073825503357,BUILD165-A9,3923

+0.5729362416107382,0.029453020134228163,2.9319127516778507,0.7225551839464884,0.7256555183946486,0.11894648829431437,5.6302274247491635,0.11848829431438124,5.788408026755855,0.7810969899665551,0.7290501672240801,0.1291571906354516,5.626351170568564,0.25159531772575244,5.850003344481604,BUILD21-B1,3922

+0.5758428093645483,0.03633444816053507,2.631418060200669,0.7201806020066885,0.7199464882943144,0.1220033444816054,5.266183946488291,0.12759866220735794,5.243414715719068,0.7790434782608694,0.7255785953177253,0.1292307692307692,5.5810635451505,0.25682608695652165,5.589692307692307,BUILD156-A9,3921

+0.5707684563758393,0.03206711409395971,3.0611812080536915,0.7184648829431434,0.7158795986622074,0.13064882943143824,5.957739130434781,0.13187625418060198,5.9663745819397995,0.8540402684563759,0.7217290969899668,0.1427090301003344,6.001749163879603,0.29916442953020167,8.81292281879195,BUILD139-A9,3920

 0.5759531772575247,0.032140468227424744,2.6997692307692307,0.7194247491638792,0.7245183946488295,0.11278929765886289,5.318976588628757,0.11635451505016726,5.386404682274252,0.796943143812709,0.7304147157190638,0.11959531772575256,5.459959866220738,0.24364882943143812,5.456491638795983,BUILD143-A9,3919

 0.5784697986577184,0.03631543624161071,3.0281879194630883,0.7228595317725749,0.723033444816054,0.11472240802675583,5.741521739130437,0.1158929765886288,5.746695652173914,0.7946521739130435,0.7354682274247485,0.12361872909698998,5.833240802675584,0.24684615384615405,6.010591973244147,BUILD131-A9,3918

 0.5838093645484947,0.014602006688963203,2.539782608695651,0.720483221476509,0.7291906354515049,0.13324080267558527,5.108107023411368,0.13560738255033564,5.063906040268457,0.7817491638795987,0.7290568561872908,0.15345484949832774,5.292016722408028,0.26696655518394674,5.474331103678929,BUILD162-A9,3917

diff --git a/content/test/gpu/power_measurement_results/win10_intel_hd_630/build_3633_4131.json b/content/test/gpu/power_measurement_results/win10_intel_hd_630/build_3633_4131.json
index 806dbca9..b064f52 100644
--- a/content/test/gpu/power_measurement_results/win10_intel_hd_630/build_3633_4131.json
+++ b/content/test/gpu/power_measurement_results/win10_intel_hd_630/build_3633_4131.json
@@ -6613,7 +6613,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD170-A9",
       "number": 4001,
       "tests": [
         {
@@ -6664,7 +6664,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD130-A9",
       "number": 4000,
       "tests": [
         {
@@ -6715,7 +6715,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD182-A9",
       "number": 3999,
       "tests": [
         {
@@ -6766,7 +6766,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD168-A9",
       "number": 3998,
       "tests": [
         {
@@ -6817,7 +6817,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD138-A9",
       "number": 3997,
       "tests": [
         {
@@ -6868,7 +6868,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD177-A9",
       "number": 3996,
       "tests": [
         {
@@ -6919,7 +6919,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD175-A9",
       "number": 3995,
       "tests": [
         {
@@ -6970,7 +6970,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD168-A9",
       "number": 3994,
       "tests": [
         {
@@ -7021,7 +7021,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD21-B1",
       "number": 3993,
       "tests": [
         {
@@ -7072,7 +7072,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD161-A9",
       "number": 3992,
       "tests": [
         {
@@ -7123,7 +7123,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD125-A9",
       "number": 3991,
       "tests": [
         {
@@ -7174,7 +7174,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD162-A9",
       "number": 3990,
       "tests": [
         {
@@ -7225,7 +7225,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD126-A9",
       "number": 3989,
       "tests": [
         {
@@ -7276,7 +7276,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD118-A9",
       "number": 3988,
       "tests": [
         {
@@ -7327,7 +7327,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD137-A9",
       "number": 3987,
       "tests": [
         {
@@ -7378,7 +7378,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD162-A9",
       "number": 3986,
       "tests": [
         {
@@ -7429,7 +7429,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD143-A9",
       "number": 3985,
       "tests": [
         {
@@ -7480,7 +7480,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD137-A9",
       "number": 3984,
       "tests": [
         {
@@ -7531,7 +7531,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD125-A9",
       "number": 3983,
       "tests": [
         {
@@ -7582,7 +7582,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD120-A9",
       "number": 3982,
       "tests": [
         {
@@ -7633,7 +7633,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD149-A9",
       "number": 3981,
       "tests": [
         {
@@ -7684,7 +7684,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD120-A9",
       "number": 3980,
       "tests": [
         {
@@ -7735,7 +7735,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD144-A9",
       "number": 3979,
       "tests": [
         {
@@ -7777,7 +7777,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD138-A9",
       "number": 3978,
       "tests": [
         {
@@ -7828,7 +7828,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD166-A9",
       "number": 3977,
       "tests": [
         {
@@ -7879,7 +7879,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD120-A9",
       "number": 3976,
       "tests": [
         {
@@ -7930,7 +7930,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD21-B1",
       "number": 3975,
       "tests": [
         {
@@ -7981,7 +7981,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD180-A9",
       "number": 3974,
       "tests": [
         {
@@ -8032,7 +8032,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD145-A9",
       "number": 3973,
       "tests": [
         {
@@ -8083,7 +8083,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD174-A9",
       "number": 3972,
       "tests": [
         {
@@ -8134,7 +8134,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD146-A9",
       "number": 3971,
       "tests": [
         {
@@ -8185,7 +8185,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD133-A9",
       "number": 3970,
       "tests": [
         {
@@ -8236,7 +8236,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD166-A9",
       "number": 3969,
       "tests": [
         {
@@ -8287,7 +8287,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD120-A9",
       "number": 3968,
       "tests": [
         {
@@ -8338,7 +8338,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD158-A9",
       "number": 3967,
       "tests": [
         {
@@ -8389,7 +8389,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD143-A9",
       "number": 3966,
       "tests": [
         {
@@ -8440,7 +8440,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD129-A9",
       "number": 3965,
       "tests": [
         {
@@ -8491,7 +8491,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD179-A9",
       "number": 3964,
       "tests": [
         {
@@ -8542,7 +8542,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD148-A9",
       "number": 3963,
       "tests": [
         {
@@ -8593,7 +8593,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD175-A9",
       "number": 3962,
       "tests": [
         {
@@ -8644,7 +8644,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD147-A9",
       "number": 3961,
       "tests": [
         {
@@ -8695,7 +8695,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD125-A9",
       "number": 3960,
       "tests": [
         {
@@ -8746,7 +8746,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD139-A9",
       "number": 3959,
       "tests": [
         {
@@ -8797,7 +8797,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD144-A9",
       "number": 3958,
       "tests": [
         {
@@ -8848,7 +8848,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD158-A9",
       "number": 3957,
       "tests": [
         {
@@ -8899,7 +8899,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD153-A9",
       "number": 3956,
       "tests": [
         {
@@ -8950,7 +8950,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD144-A9",
       "number": 3955,
       "tests": [
         {
@@ -9001,7 +9001,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD144-A9",
       "number": 3954,
       "tests": [
         {
@@ -9052,7 +9052,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD166-A9",
       "number": 3953,
       "tests": [
         {
@@ -9103,7 +9103,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD154-A9",
       "number": 3952,
       "tests": [
         {
@@ -9154,7 +9154,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD177-A9",
       "number": 3950,
       "tests": [
         {
@@ -9205,7 +9205,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD139-A9",
       "number": 3949,
       "tests": [
         {
@@ -9256,7 +9256,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD120-A9",
       "number": 3948,
       "tests": [
         {
@@ -9307,7 +9307,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD130-A9",
       "number": 3947,
       "tests": [
         {
@@ -9358,7 +9358,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD142-A9",
       "number": 3946,
       "tests": [
         {
@@ -9409,7 +9409,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD172-A9",
       "number": 3945,
       "tests": [
         {
@@ -9460,7 +9460,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD176-A9",
       "number": 3944,
       "tests": [
         {
@@ -9511,7 +9511,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD143-A9",
       "number": 3943,
       "tests": [
         {
@@ -9562,7 +9562,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD172-A9",
       "number": 3942,
       "tests": [
         {
@@ -9613,7 +9613,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD172-A9",
       "number": 3941,
       "tests": [
         {
@@ -9664,7 +9664,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD144-A9",
       "number": 3940,
       "tests": [
         {
@@ -9715,7 +9715,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD172-A9",
       "number": 3939,
       "tests": [
         {
@@ -9766,7 +9766,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD127-A9",
       "number": 3938,
       "tests": [
         {
@@ -9817,7 +9817,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD131-A9",
       "number": 3937,
       "tests": [
         {
@@ -9868,7 +9868,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD140-A9",
       "number": 3936,
       "tests": [
         {
@@ -9919,7 +9919,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD149-A9",
       "number": 3935,
       "tests": [
         {
@@ -9970,7 +9970,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD142-A9",
       "number": 3934,
       "tests": [
         {
@@ -10021,7 +10021,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD141-A9",
       "number": 3933,
       "tests": [
         {
@@ -10072,7 +10072,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD136-A9",
       "number": 3932,
       "tests": [
         {
@@ -10123,7 +10123,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD148-A9",
       "number": 3931,
       "tests": [
         {
@@ -10174,7 +10174,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD126-A9",
       "number": 3930,
       "tests": [
         {
@@ -10225,7 +10225,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD147-A9",
       "number": 3929,
       "tests": [
         {
@@ -10276,7 +10276,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD127-A9",
       "number": 3928,
       "tests": [
         {
@@ -10327,7 +10327,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD125-A9",
       "number": 3927,
       "tests": [
         {
@@ -10378,7 +10378,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD136-A9",
       "number": 3926,
       "tests": [
         {
@@ -10429,7 +10429,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD129-A9",
       "number": 3925,
       "tests": [
         {
@@ -10480,7 +10480,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD155-A9",
       "number": 3924,
       "tests": [
         {
@@ -10531,7 +10531,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD165-A9",
       "number": 3923,
       "tests": [
         {
@@ -10582,7 +10582,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD21-B1",
       "number": 3922,
       "tests": [
         {
@@ -10633,7 +10633,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD156-A9",
       "number": 3921,
       "tests": [
         {
@@ -10684,7 +10684,7 @@
       ]
     },
     {
-      "bot": "SWARM761-C4",
+      "bot": "BUILD139-A9",
       "number": 3920,
       "tests": [
         {
diff --git a/device/vr/buildflags/buildflags.gni b/device/vr/buildflags/buildflags.gni
index faedebf..ef0161b0 100644
--- a/device/vr/buildflags/buildflags.gni
+++ b/device/vr/buildflags/buildflags.gni
@@ -53,4 +53,7 @@
 
   # Whether to create AR module as an asynchronous DFM.
   async_ar = false
+
+  # Whether to create VR module as an asynchronous DFM.
+  async_vr = false
 }
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index 13598c2..1d5ae26 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -852,6 +852,20 @@
   // The callback to call when we get a response from all event handlers.
   net::CompletionOnceCallback callback;
 
+  // The callback to invoke for onBeforeSendHeaders. If
+  // |before_send_headers_callback.is_null()| is false, |callback| must be NULL.
+  // Only valid for OnBeforeSendHeaders.
+  BeforeSendHeadersCallback before_send_headers_callback;
+
+  // The callback to invoke for auth. If |auth_callback.is_null()| is false,
+  // |callback| must be NULL.
+  // Only valid for OnAuthRequired.
+  net::NetworkDelegate::AuthCallback auth_callback;
+
+  // If non-empty, this contains the auth credentials that may be filled in.
+  // Only valid for OnAuthRequired.
+  net::AuthCredentials* auth_credentials = nullptr;
+
   // If non-empty, this contains the new URL that the request will redirect to.
   // Only valid for OnBeforeRequest and OnHeadersReceived.
   GURL* new_url = nullptr;
@@ -868,15 +882,6 @@
   // OnHeadersReceived.
   scoped_refptr<net::HttpResponseHeaders>* override_response_headers = nullptr;
 
-  // If non-empty, this contains the auth credentials that may be filled in.
-  // Only valid for OnAuthRequired.
-  net::AuthCredentials* auth_credentials = nullptr;
-
-  // The callback to invoke for auth. If |auth_callback.is_null()| is false,
-  // |callback| must be NULL.
-  // Only valid for OnAuthRequired.
-  net::NetworkDelegate::AuthCallback auth_callback;
-
   // Time the request was paused. Used for logging purposes.
   base::Time blocking_time;
 
@@ -1092,7 +1097,7 @@
     void* browser_context,
     const InfoMap* extension_info_map,
     const WebRequestInfo* request,
-    net::CompletionOnceCallback callback,
+    BeforeSendHeadersCallback callback,
     net::HttpRequestHeaders* headers) {
   if (ShouldHideEvent(browser_context, extension_info_map, *request))
     return net::OK;
@@ -1130,7 +1135,7 @@
   blocked_request.event = kOnBeforeSendHeaders;
   blocked_request.is_incognito |= IsIncognitoBrowserContext(browser_context);
   blocked_request.request = request;
-  blocked_request.callback = std::move(callback);
+  blocked_request.before_send_headers_callback = std::move(callback);
   blocked_request.request_headers = headers;
 
   if (blocked_request.num_handlers_blocking == 0) {
@@ -2215,6 +2220,9 @@
   bool request_headers_modified = false;
   bool response_headers_modified = false;
   bool credentials_set = false;
+  // The set of request headers which were removed or set to new values.
+  std::set<std::string> request_headers_removed;
+  std::set<std::string> request_headers_set;
 
   deltas.sort(&helpers::InDecreasingExtensionInstallationTimeOrder);
 
@@ -2229,11 +2237,12 @@
         request->url, blocked_request.response_deltas, blocked_request.new_url,
         &ignored_actions, request->logger.get());
   } else if (blocked_request.event == kOnBeforeSendHeaders) {
-    CHECK(!blocked_request.callback.is_null());
+    CHECK(!blocked_request.before_send_headers_callback.is_null());
     helpers::MergeOnBeforeSendHeadersResponses(
         request->url, blocked_request.response_deltas,
         blocked_request.request_headers, &ignored_actions,
-        request->logger.get(), &request_headers_modified);
+        request->logger.get(), &request_headers_removed, &request_headers_set,
+        &request_headers_modified);
   } else if (blocked_request.event == kOnHeadersReceived) {
     CHECK(!blocked_request.callback.is_null());
     helpers::MergeOnHeadersReceivedResponses(
@@ -2293,6 +2302,13 @@
     blocked_requests_.erase(request->id);
     if (call_callback)
       std::move(callback).Run(rv);
+  } else if (!blocked_request.before_send_headers_callback.is_null()) {
+    auto callback = std::move(blocked_request.before_send_headers_callback);
+    // Ensure that request is removed before callback because the callback
+    // might trigger the next event.
+    blocked_requests_.erase(request->id);
+    if (call_callback)
+      std::move(callback).Run(request_headers_removed, request_headers_set, rv);
   } else if (!blocked_request.auth_callback.is_null()) {
     net::NetworkDelegate::AuthRequiredResponse response;
     if (canceled)
diff --git a/extensions/browser/api/web_request/web_request_api.h b/extensions/browser/api/web_request/web_request_api.h
index e0322509..bd9ea87 100644
--- a/extensions/browser/api/web_request/web_request_api.h
+++ b/extensions/browser/api/web_request/web_request_api.h
@@ -359,6 +359,11 @@
                       GURL* new_url,
                       bool* should_collapse_initiator);
 
+  using BeforeSendHeadersCallback =
+      base::OnceCallback<void(const std::set<std::string>& removed_headers,
+                              const std::set<std::string>& set_headers,
+                              int error_code)>;
+
   // Dispatches the onBeforeSendHeaders event. This is fired for HTTP(s)
   // requests only, and allows modification of the outgoing request headers.
   // Returns net::ERR_IO_PENDING if an extension is intercepting the request, OK
@@ -366,7 +371,7 @@
   int OnBeforeSendHeaders(void* browser_context,
                           const extensions::InfoMap* extension_info_map,
                           const WebRequestInfo* request,
-                          net::CompletionOnceCallback callback,
+                          BeforeSendHeadersCallback callback,
                           net::HttpRequestHeaders* headers);
 
   // Dispatches the onSendHeaders event. This is fired for HTTP(s) requests
diff --git a/extensions/browser/api/web_request/web_request_api_helpers.cc b/extensions/browser/api/web_request/web_request_api_helpers.cc
index 362b80fe..03f2d1c 100644
--- a/extensions/browser/api/web_request/web_request_api_helpers.cc
+++ b/extensions/browser/api/web_request/web_request_api_helpers.cc
@@ -709,15 +709,14 @@
     net::HttpRequestHeaders* request_headers,
     IgnoredActions* ignored_actions,
     extensions::WebRequestInfo::Logger* logger,
+    std::set<std::string>* removed_headers,
+    std::set<std::string>* set_headers,
     bool* request_headers_modified) {
   DCHECK(request_headers_modified);
+  DCHECK(removed_headers->empty());
+  DCHECK(set_headers->empty());
   *request_headers_modified = false;
 
-  // Here we collect which headers we have removed or set to new values
-  // so far due to extensions of higher precedence.
-  std::set<std::string> removed_headers;
-  std::set<std::string> set_headers;
-
   // We assume here that the deltas are sorted in decreasing extension
   // precedence (i.e. decreasing extension installation time).
   for (const auto& delta : deltas) {
@@ -739,14 +738,14 @@
         const std::string& value = modification.value();
 
         // We must not delete anything that has been modified before.
-        if (removed_headers.find(key) != removed_headers.end() &&
+        if (removed_headers->find(key) != removed_headers->end() &&
             !extension_conflicts) {
           extension_conflicts = true;
         }
 
         // We must not modify anything that has been set to a *different*
         // value before.
-        if (set_headers.find(key) != set_headers.end() &&
+        if (set_headers->find(key) != set_headers->end() &&
             !extension_conflicts) {
           std::string current_value;
           if (!request_headers->GetHeader(key, &current_value) ||
@@ -763,7 +762,7 @@
       for (auto key = delta.deleted_request_headers.begin();
            key != delta.deleted_request_headers.end() && !extension_conflicts;
            ++key) {
-        if (set_headers.find(*key) != set_headers.end()) {
+        if (set_headers->find(*key) != set_headers->end()) {
           std::string current_value;
           request_headers->GetHeader(*key, &current_value);
           extension_conflicts = true;
@@ -780,14 +779,14 @@
         net::HttpRequestHeaders::Iterator modification(
             delta.modified_request_headers);
         while (modification.GetNext())
-          set_headers.insert(modification.name());
+          set_headers->insert(modification.name());
       }
 
       // Perform all deletions and record which keys were deleted.
       {
         for (const auto& header : delta.deleted_request_headers) {
           request_headers->RemoveHeader(header);
-          removed_headers.insert(header);
+          removed_headers->insert(header);
         }
       }
       logger->LogEvent(net::NetLogEventType::CHROME_EXTENSION_MODIFIED_HEADERS,
@@ -815,7 +814,7 @@
           {"referer", WebRequestSpecialRequestHeaderModification::kReferer},
       };
   int special_headers_removed = 0;
-  for (const auto& header : removed_headers) {
+  for (const auto& header : *removed_headers) {
     auto it = kHeaderMap.find(base::ToLowerASCII(header));
     if (it != kHeaderMap.end()) {
       special_headers_removed++;
@@ -831,7 +830,7 @@
   }
 
   int special_headers_changed = 0;
-  for (const auto& header : set_headers) {
+  for (const auto& header : *set_headers) {
     auto it = kHeaderMap.find(base::ToLowerASCII(header));
     if (it != kHeaderMap.end()) {
       special_headers_changed++;
diff --git a/extensions/browser/api/web_request/web_request_api_helpers.h b/extensions/browser/api/web_request/web_request_api_helpers.h
index 24e3c911..0698fc3a 100644
--- a/extensions/browser/api/web_request/web_request_api_helpers.h
+++ b/extensions/browser/api/web_request/web_request_api_helpers.h
@@ -332,6 +332,8 @@
     net::HttpRequestHeaders* request_headers,
     IgnoredActions* ignored_actions,
     extensions::WebRequestInfo::Logger* logger,
+    std::set<std::string>* removed_headers,
+    std::set<std::string>* set_headers,
     bool* request_headers_modified);
 // Modifies the "Set-Cookie" headers in |override_response_headers| according to
 // |deltas.response_cookie_modifications|. If |override_response_headers| is
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
index 7df76569..d14153d0 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
@@ -26,6 +26,11 @@
 
 namespace extensions {
 
+WebRequestProxyingURLLoaderFactory::InProgressRequest::FollowRedirectParams::
+    FollowRedirectParams() = default;
+WebRequestProxyingURLLoaderFactory::InProgressRequest::FollowRedirectParams::
+    ~FollowRedirectParams() = default;
+
 WebRequestProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
     WebRequestProxyingURLLoaderFactory* factory,
     uint64_t request_id,
@@ -80,7 +85,12 @@
 }
 
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::Restart() {
-  request_completed_ = false;
+  UpdateRequestInfo();
+  RestartInternal();
+}
+
+void WebRequestProxyingURLLoaderFactory::InProgressRequest::
+    UpdateRequestInfo() {
   // Derive a new WebRequestInfo value any time |Restart()| is called, because
   // the details in |request_| may have changed e.g. if we've been redirected.
   // |request_initiator| can be modified on redirects, but we keep the original
@@ -101,6 +111,12 @@
       ExtensionWebRequestEventRouter::GetInstance()
           ->HasExtraHeadersListenerForRequest(
               factory_->browser_context_, factory_->info_map_, &info_.value());
+}
+
+void WebRequestProxyingURLLoaderFactory::InProgressRequest::RestartInternal() {
+  DCHECK_EQ(info_->url, request_.url)
+      << "UpdateRequestInfo must have been called first";
+  request_completed_ = false;
 
   // If the header client will be used, we start the request immediately, and
   // OnBeforeSendHeaders and OnSendHeaders will be handled there. Otherwise,
@@ -162,11 +178,29 @@
     request_.headers.RemoveHeader(header);
   request_.headers.MergeFrom(modified_headers);
 
+  // Call this before checking |current_request_uses_header_client_| as it
+  // calculates it.
+  UpdateRequestInfo();
+
   if (target_loader_.is_bound()) {
-    target_loader_->FollowRedirect(removed_headers, modified_headers, new_url);
+    // If header_client_ is used, then we have to call FollowRedirect now as
+    // that's what triggers the network service calling back to
+    // OnBeforeSendHeaders(). Otherwise, don't call FollowRedirect now. Wait for
+    // the onBeforeSendHeaders callback(s) to run as these may modify request
+    // headers and if so we'll pass these modifications to FollowRedirect.
+    if (current_request_uses_header_client_) {
+      target_loader_->FollowRedirect(removed_headers, modified_headers,
+                                     new_url);
+    } else {
+      auto params = std::make_unique<FollowRedirectParams>();
+      params->removed_headers = removed_headers;
+      params->modified_headers = modified_headers;
+      params->new_url = new_url;
+      pending_follow_redirect_params_ = std::move(params);
+    }
   }
 
-  Restart();
+  RestartInternal();
 }
 
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::
@@ -447,7 +481,8 @@
     DCHECK_EQ(net::OK, result);
   }
 
-  ContinueToSendHeaders(net::OK);
+  ContinueToSendHeaders(std::set<std::string>(), std::set<std::string>(),
+                        net::OK);
 }
 
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::
@@ -490,7 +525,9 @@
 }
 
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::
-    ContinueToSendHeaders(int error_code) {
+    ContinueToSendHeaders(const std::set<std::string>& removed_headers,
+                          const std::set<std::string>& set_headers,
+                          int error_code) {
   if (error_code != net::OK) {
     OnRequestError(network::URLLoaderCompletionStatus(error_code));
     return;
@@ -500,6 +537,29 @@
     DCHECK(on_before_send_headers_callback_);
     std::move(on_before_send_headers_callback_)
         .Run(error_code, request_.headers);
+  } else if (pending_follow_redirect_params_) {
+    pending_follow_redirect_params_->removed_headers.insert(
+        pending_follow_redirect_params_->removed_headers.end(),
+        removed_headers.begin(), removed_headers.end());
+
+    for (auto& set_header : set_headers) {
+      std::string header_value;
+      if (request_.headers.GetHeader(set_header, &header_value)) {
+        pending_follow_redirect_params_->modified_headers.SetHeader(
+            set_header, header_value);
+      } else {
+        NOTREACHED();
+      }
+    }
+
+    if (target_loader_.is_bound()) {
+      target_loader_->FollowRedirect(
+          pending_follow_redirect_params_->removed_headers,
+          pending_follow_redirect_params_->modified_headers,
+          pending_follow_redirect_params_->new_url);
+    }
+
+    pending_follow_redirect_params_.reset();
   }
 
   if (proxied_client_binding_.is_bound())
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
index 2226b1ea..e9108ef 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
@@ -103,8 +103,14 @@
                            OnHeadersReceivedCallback callback) override;
 
    private:
+    // These two methods combined form the implementation of Restart().
+    void UpdateRequestInfo();
+    void RestartInternal();
+
     void ContinueToBeforeSendHeaders(int error_code);
-    void ContinueToSendHeaders(int error_code);
+    void ContinueToSendHeaders(const std::set<std::string>& removed_headers,
+                               const std::set<std::string>& set_headers,
+                               int error_code);
     void ContinueToStartRequest(int error_code);
     void ContinueToHandleOverrideHeaders(int error_code);
     void ContinueToResponseStarted(int error_code);
@@ -168,6 +174,21 @@
     OnHeadersReceivedCallback on_headers_received_callback_;
     mojo::Binding<network::mojom::TrustedHeaderClient> header_client_binding_;
 
+    // If |has_any_extra_headers_listeners_| is set to false and a redirect is
+    // in progress, this stores the parameters to FollowRedirect that came from
+    // the client. That way we can combine it with any other changes that
+    // extensions made to headers in their callbacks.
+    struct FollowRedirectParams {
+      FollowRedirectParams();
+      ~FollowRedirectParams();
+      std::vector<std::string> removed_headers;
+      net::HttpRequestHeaders modified_headers;
+      base::Optional<GURL> new_url;
+
+      DISALLOW_COPY_AND_ASSIGN(FollowRedirectParams);
+    };
+    std::unique_ptr<FollowRedirectParams> pending_follow_redirect_params_;
+
     base::WeakPtrFactory<InProgressRequest> weak_factory_;
 
     DISALLOW_COPY_AND_ASSIGN(InProgressRequest);
diff --git a/extensions/browser/api/web_request/web_request_proxying_websocket.cc b/extensions/browser/api/web_request/web_request_proxying_websocket.cc
index 41baffb..4921b10 100644
--- a/extensions/browser/api/web_request/web_request_proxying_websocket.cc
+++ b/extensions/browser/api/web_request/web_request_proxying_websocket.cc
@@ -379,10 +379,14 @@
     return;
 
   DCHECK_EQ(net::OK, result);
-  OnBeforeSendHeadersComplete(net::OK);
+  OnBeforeSendHeadersComplete(std::set<std::string>(), std::set<std::string>(),
+                              net::OK);
 }
 
-void WebRequestProxyingWebSocket::OnBeforeSendHeadersComplete(int error_code) {
+void WebRequestProxyingWebSocket::OnBeforeSendHeadersComplete(
+    const std::set<std::string>& removed_headers,
+    const std::set<std::string>& set_headers,
+    int error_code) {
   DCHECK(binding_as_header_client_ || !binding_as_client_.is_bound());
   if (error_code != net::OK) {
     OnError(error_code);
diff --git a/extensions/browser/api/web_request/web_request_proxying_websocket.h b/extensions/browser/api/web_request/web_request_proxying_websocket.h
index 1e5d162..e11ae727 100644
--- a/extensions/browser/api/web_request/web_request_proxying_websocket.h
+++ b/extensions/browser/api/web_request/web_request_proxying_websocket.h
@@ -112,7 +112,9 @@
 
  private:
   void OnBeforeRequestComplete(int error_code);
-  void OnBeforeSendHeadersComplete(int error_code);
+  void OnBeforeSendHeadersComplete(const std::set<std::string>& removed_headers,
+                                   const std::set<std::string>& set_headers,
+                                   int error_code);
   void ContinueToStartRequest(int error_code);
   void OnHeadersReceivedComplete(int error_code);
   void ContinueToHeadersReceived();
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index 5cee00c..efdbf85 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -1115,6 +1115,7 @@
   IPC_STRUCT_TRAITS_MEMBER(loaded)
   IPC_STRUCT_TRAITS_MEMBER(loading_progress)
   IPC_STRUCT_TRAITS_MEMBER(focus_id)
+  IPC_STRUCT_TRAITS_MEMBER(sel_is_backward)
   IPC_STRUCT_TRAITS_MEMBER(sel_anchor_object_id)
   IPC_STRUCT_TRAITS_MEMBER(sel_anchor_offset)
   IPC_STRUCT_TRAITS_MEMBER(sel_anchor_affinity)
diff --git a/extensions/shell/browser/shell_network_delegate.cc b/extensions/shell/browser/shell_network_delegate.cc
index b3ffe0f..9d6ca65f 100644
--- a/extensions/shell/browser/shell_network_delegate.cc
+++ b/extensions/shell/browser/shell_network_delegate.cc
@@ -70,13 +70,22 @@
   return result;
 }
 
+namespace {
+void OnHeadersReceivedAdapter(net::CompletionOnceCallback callback,
+                              const std::set<std::string>& removed_headers,
+                              const std::set<std::string>& set_headers,
+                              int error_code) {
+  std::move(callback).Run(error_code);
+}
+}  // namespace
+
 int ShellNetworkDelegate::OnBeforeStartTransaction(
     net::URLRequest* request,
     net::CompletionOnceCallback callback,
     net::HttpRequestHeaders* headers) {
   return ExtensionWebRequestEventRouter::GetInstance()->OnBeforeSendHeaders(
       browser_context_, extension_info_map_.get(), GetWebRequestInfo(request),
-      std::move(callback), headers);
+      base::BindOnce(OnHeadersReceivedAdapter, std::move(callback)), headers);
 }
 
 void ShellNetworkDelegate::OnStartTransaction(
diff --git a/gpu/command_buffer/client/shared_memory_limits.h b/gpu/command_buffer/client/shared_memory_limits.h
index bef14c3b..eb68c2f9 100644
--- a/gpu/command_buffer/client/shared_memory_limits.h
+++ b/gpu/command_buffer/client/shared_memory_limits.h
@@ -66,6 +66,15 @@
     return limits;
   }
 
+  static SharedMemoryLimits ForWebGPUContext() {
+    // Most WebGPU commands are sent via transfer buffer, so we use a smaller
+    // command buffer.
+    SharedMemoryLimits limits;
+    limits.command_buffer_size = 64 * 1024;
+
+    return limits;
+  }
+
 #if defined(OS_ANDROID)
   static SharedMemoryLimits ForDisplayCompositor(const gfx::Size& screen_size) {
     DCHECK(!screen_size.IsEmpty());
diff --git a/gpu/command_buffer/client/webgpu_implementation.cc b/gpu/command_buffer/client/webgpu_implementation.cc
index 8c3615d0..792e036 100644
--- a/gpu/command_buffer/client/webgpu_implementation.cc
+++ b/gpu/command_buffer/client/webgpu_implementation.cc
@@ -4,8 +4,10 @@
 
 #include "gpu/command_buffer/client/webgpu_implementation.h"
 
+#include <algorithm>
 #include <vector>
 
+#include "base/numerics/checked_math.h"
 #include "gpu/command_buffer/client/gpu_control.h"
 #include "gpu/command_buffer/client/shared_memory_limits.h"
 
@@ -42,12 +44,8 @@
     return result;
   }
 
-  // TODO(enga): Keep track of how much command space the application is using
-  // and adjust c2s_buffer_size_ accordingly.
-  c2s_buffer_size_ = limits.start_transfer_buffer_size;
-  DCHECK_GT(c2s_buffer_size_, 0u);
-  DCHECK(
-      base::CheckAdd(c2s_buffer_size_, c2s_buffer_size_).IsValid<uint32_t>());
+  c2s_buffer_default_size_ = limits.start_transfer_buffer_size;
+  DCHECK_GT(c2s_buffer_default_size_, 0u);
 
   return gpu::ContextResult::kSuccess;
 }
@@ -214,36 +212,37 @@
 
 void* WebGPUImplementation::GetCmdSpace(size_t size) {
   // The buffer size must be initialized before any commands are serialized.
-  if (c2s_buffer_size_ == 0u) {
+  if (c2s_buffer_default_size_ == 0u) {
     NOTREACHED();
     return nullptr;
   }
 
-  // TODO(enga): Handle chunking commands if size > c2s_buffer_size_.
-  if (size > c2s_buffer_size_) {
-    NOTREACHED();
-    return nullptr;
-  }
+  base::CheckedNumeric<uint32_t> checked_next_offset(c2s_put_offset_);
+  checked_next_offset += size;
 
-  // This should never be more than 2 * c2s_buffer_size_ which is checked in
-  // WebGPUImplementation::Initialize.
-  DCHECK_LE(c2s_put_offset_, c2s_buffer_size_);
-  DCHECK_LE(size, c2s_buffer_size_);
-  uint32_t next_offset = c2s_put_offset_ + static_cast<uint32_t>(size);
+  uint32_t next_offset;
+  bool next_offset_valid = checked_next_offset.AssignIfValid(&next_offset);
 
   // If the buffer does not have enough space, or if the buffer is not
   // initialized, flush and reset the command stream.
-  if (next_offset > c2s_buffer_.size() || !c2s_buffer_.valid()) {
+  if (!next_offset_valid || next_offset > c2s_buffer_.size() ||
+      !c2s_buffer_.valid()) {
     Flush();
 
-    c2s_buffer_.Reset(c2s_buffer_size_);
+    uint32_t max_allocation = transfer_buffer_->GetMaxSize();
+    // TODO(crbug.com/951558): Handle command chunking or ensure commands aren't
+    // this large.
+    CHECK_LE(size, max_allocation);
+
+    uint32_t allocation_size =
+        std::max(c2s_buffer_default_size_, static_cast<uint32_t>(size));
+    c2s_buffer_.Reset(allocation_size);
     c2s_put_offset_ = 0;
     next_offset = size;
 
-    if (size > c2s_buffer_.size() || !c2s_buffer_.valid()) {
-      // TODO(enga): Handle OOM.
-      return nullptr;
-    }
+    // TODO(crbug.com/951558): Handle OOM.
+    CHECK(c2s_buffer_.valid());
+    CHECK_LE(size, c2s_buffer_.size());
   }
 
   DCHECK(c2s_buffer_.valid());
@@ -256,6 +255,7 @@
 
 bool WebGPUImplementation::Flush() {
   if (c2s_buffer_.valid()) {
+    c2s_buffer_.Shrink(c2s_put_offset_);
     helper_->DawnCommands(c2s_buffer_.shm_id(), c2s_buffer_.offset(),
                           c2s_put_offset_);
     c2s_put_offset_ = 0;
diff --git a/gpu/command_buffer/client/webgpu_implementation.h b/gpu/command_buffer/client/webgpu_implementation.h
index c06d9b2b..3032f99 100644
--- a/gpu/command_buffer/client/webgpu_implementation.h
+++ b/gpu/command_buffer/client/webgpu_implementation.h
@@ -124,7 +124,7 @@
 #endif
   DawnProcTable procs_ = {};
 
-  uint32_t c2s_buffer_size_ = 0;
+  uint32_t c2s_buffer_default_size_ = 0;
   uint32_t c2s_put_offset_ = 0;
   ScopedTransferBufferPtr c2s_buffer_;
 
diff --git a/gpu/command_buffer/common/shared_image_usage.h b/gpu/command_buffer/common/shared_image_usage.h
index 34311eb..fdce596 100644
--- a/gpu/command_buffer/common/shared_image_usage.h
+++ b/gpu/command_buffer/common/shared_image_usage.h
@@ -20,9 +20,10 @@
   SHARED_IMAGE_USAGE_DISPLAY = 1 << 3,
   // Image will be used as a scanout buffer (overlay)
   SHARED_IMAGE_USAGE_SCANOUT = 1 << 4,
-  // Image will be used in OOP rasterization
-  // TODO(backer): Fold back into SHARED_IMAGE_USAGE_RASTER once RasterInterface
-  // can CPU raster (CopySubImage?) to SkImage.
+  // Image will be used in OOP rasterization. This flag is used on top of
+  // SHARED_IMAGE_USAGE_RASTER to indicate that the client will only use
+  // RasterInterface for OOP rasterization. TODO(backer): Eliminate once we can
+  // CPU raster to SkImage via RasterInterface.
   SHARED_IMAGE_USAGE_OOP_RASTERIZATION = 1 << 5,
   // Image will be used for RGB emulation in WebGL on Mac.
   SHARED_IMAGE_USAGE_RGB_EMULATION = 1 << 6,
diff --git a/gpu/command_buffer/service/shared_image_factory.cc b/gpu/command_buffer/service/shared_image_factory.cc
index 86d40301..66d40d6b 100644
--- a/gpu/command_buffer/service/shared_image_factory.cc
+++ b/gpu/command_buffer/service/shared_image_factory.cc
@@ -208,12 +208,13 @@
 SharedImageBackingFactory* SharedImageFactory::GetFactoryByUsage(
     uint32_t usage,
     bool* allow_legacy_mailbox) {
-  // wrapped_sk_image_factory_ is only used for OOPR.
-  constexpr auto kUsageOOPR = SHARED_IMAGE_USAGE_RASTER |
-                              SHARED_IMAGE_USAGE_OOP_RASTERIZATION |
-                              SHARED_IMAGE_USAGE_DISPLAY;
-  bool oopr_only_usage = !(usage & ~kUsageOOPR);
-  bool using_wrapped_sk_image = wrapped_sk_image_factory_ && oopr_only_usage;
+  // wrapped_sk_image_factory_ is only used for OOPR and supports
+  // a limited number of flags (e.g. no SHARED_IMAGE_USAGE_SCANOUT).
+  constexpr auto kWrappedSkImageUsage = SHARED_IMAGE_USAGE_RASTER |
+                                        SHARED_IMAGE_USAGE_OOP_RASTERIZATION |
+                                        SHARED_IMAGE_USAGE_DISPLAY;
+  bool using_wrapped_sk_image =
+      wrapped_sk_image_factory_ && (usage == kWrappedSkImageUsage);
 
   bool vulkan_usage = using_vulkan_ && (usage & SHARED_IMAGE_USAGE_DISPLAY);
   bool gl_usage = usage & SHARED_IMAGE_USAGE_GLES2;
diff --git a/gpu/command_buffer/tests/webgpu_test.h b/gpu/command_buffer/tests/webgpu_test.h
index c51cb5f..acd37899 100644
--- a/gpu/command_buffer/tests/webgpu_test.h
+++ b/gpu/command_buffer/tests/webgpu_test.h
@@ -27,7 +27,8 @@
     Options();
 
     // Shared memory limits
-    SharedMemoryLimits shared_memory_limits = {};
+    SharedMemoryLimits shared_memory_limits =
+        SharedMemoryLimits::ForWebGPUContext();
   };
 
   WebGPUTest();
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index 1b80593..13964b8 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -679,6 +679,12 @@
   }
 }
 
+builder_mixins {
+  name: "builderless"
+  auto_builder_dimension: NO
+  dimensions: "builderless:1"
+}
+
 buckets {
   name: "ci"
   acl_sets: "ci"
@@ -711,6 +717,7 @@
     builders {
       name: "Android arm Builder (dbg)"
       mixins: "android-ci"
+      mixins: "builderless"
       dimensions: "os:Ubuntu-14.04"
       execution_timeout_secs: 14400  # 4h
     }
@@ -718,6 +725,7 @@
     builders {
       name: "Android arm64 Builder (dbg)"
       mixins: "android-ci"
+      mixins: "builderless"
       mixins: "goma-many-jobs-for-ci"
       dimensions: "os:Ubuntu-14.04"
       execution_timeout_secs: 14400  # 4h
@@ -865,6 +873,7 @@
     builders {
       name: "Android x64 Builder (dbg)"
       mixins: "android-ci"
+      mixins: "builderless"
       dimensions: "os:Ubuntu-14.04"
       execution_timeout_secs: 14400  # 4h
     }
@@ -872,6 +881,7 @@
     builders {
       name: "Android x86 Builder (dbg)"
       mixins: "android-ci"
+      mixins: "builderless"
       dimensions: "os:Ubuntu-14.04"
     }
 
@@ -1044,6 +1054,7 @@
     builders {
       name: "chromeos-amd64-generic-asan-rel"
       mixins: "chromeos-ci"
+      mixins: "builderless"
     }
 
     builders {
@@ -1223,6 +1234,7 @@
       name: "linux-blink-heap-concurrent-marking-tsan-rel"
       mixins: "fyi-ci"
       mixins: "linux"
+      mixins: "builderless"
     }
 
     builders {
@@ -1510,62 +1522,64 @@
       name: "GPU FYI Mac dEQP Builder"
       mixins: "mac-gpu-fyi-ci"
     }
+    # Note that the Mac testers are all thin Linux VMs, triggering jobs on the
+    # physical Mac hardware in the Swarming pool, and therefore use the
+    # linux-dawn-ci mixin.
     builders {
       name: "Mac FYI 10.14 Release (AMD)"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
       mixins: "gpu-slow-bot"
     }
     builders {
       name: "Mac FYI 10.14 Release (Intel)"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
       mixins: "gpu-slow-bot"
     }
     builders {
       name: "Mac FYI 10.14 Release (NVIDIA)"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
       mixins: "gpu-slow-bot"
     }
     builders {
       name: "Mac FYI Release (Intel)"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
     }
     builders {
       name: "Mac FYI Debug (Intel)"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
     }
     builders {
       name: "Mac Pro FYI Release (AMD)"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
       mixins: "gpu-slow-bot"
     }
     builders {
       name: "Mac FYI Retina Release (NVIDIA)"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
     }
     builders {
       name: "Mac FYI Retina Debug (NVIDIA)"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
     }
     builders {
-      # TODO(kbr): testing running this on a thin Linux VM.
       name: "Mac FYI Retina Release (AMD)"
       mixins: "linux-gpu-fyi-ci"
     }
     builders {
       name: "Mac FYI Retina Debug (AMD)"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
     }
     builders {
       name: "Mac FYI Experimental Release (Intel)"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
     }
     builders {
       name: "Mac FYI Experimental Retina Release (AMD)"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
     }
     builders {
       name: "Mac FYI Experimental Retina Release (NVIDIA)"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
       # This bot has one machine backing its tests at the moment.
       # If it gets more, this should be switched back to gpu-slow-bot.
       # See crbug.com/853307 for more context.
@@ -1573,16 +1587,16 @@
     }
     builders {
       name: "Mac FYI GPU ASAN Release"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
       execution_timeout_secs: 14400  # 4h
     }
     builders {
       name: "Mac FYI dEQP Release AMD"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
     }
     builders {
       name: "Mac FYI dEQP Release Intel"
-      mixins: "mac-gpu-fyi-ci"
+      mixins: "linux-gpu-fyi-ci"
     }
 
     # iOS bots.
@@ -3660,6 +3674,7 @@
     builders { mixins: "ios-try" name: "ios-simulator-eg" }
     builders { mixins: "ios-try" name: "ios-simulator-xcode-clang" }
     builders { mixins: "ios-try" name: "ios-slimnav" }
+    builders { mixins: "mac-dawn-try" name: "dawn-mac-x64-deps-rel" }
     builders { mixins: "mac-angle-try" name: "mac-angle-rel" }
     builders { mixins: "mac-angle-try" name: "mac_angle_compile_dbg_ng" }
     builders { mixins: "mac-angle-try" name: "mac_angle_dbg_ng" }
diff --git a/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm b/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm
index 0b6ac7f..33a24224 100644
--- a/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm
+++ b/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm
@@ -150,12 +150,17 @@
       kSelectedTabHistogramName, TabUsageRecorder::IN_MEMORY, 1, failureBlock);
 
   // Evict the tab.
-  OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  NSError* openError = OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  GREYAssertNil(openError, openError.localizedDescription);
+
   GREYAssertTrue(chrome_test_util::IsIncognitoMode(),
                  @"Failed to switch to incognito mode");
 
   // Switch back to the normal tabs. Should be on tab one.
-  SwitchToNormalMode();
+
+  NSError* switchError = SwitchToNormalMode();
+  GREYAssertNil(switchError, switchError.localizedDescription);
+
   [ChromeEarlGrey waitForWebViewContainingText:kURL1FirstWord];
 
   histogramTester.ExpectTotalCount(kSelectedTabHistogramName, 2, failureBlock);
@@ -216,11 +221,14 @@
   // Evict the tab. Create a dummy tab so that switching back to normal mode
   // does not trigger a reload immediately.
   [ChromeEarlGrey openNewTab];
-  OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  NSError* openError = OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  GREYAssertNil(openError, openError.localizedDescription);
   [ChromeEarlGrey waitForIncognitoTabCount:1];
 
   // Switch back to the normal tabs. Should be on tab one.
-  SwitchToNormalMode();
+  NSError* switchError = SwitchToNormalMode();
+  GREYAssertNil(switchError, switchError.localizedDescription);
+
   chrome_test_util::SelectTabAtIndexInCurrentMode(0);
   [ChromeEarlGrey waitForWebViewContainingText:kURL1FirstWord];
 
@@ -254,12 +262,17 @@
                  @"Fail to state tabs as cold start tabs");
 
   // Open two incognito tabs with urls, clearing normal tabs from memory.
-  OpenNewIncognitoTabUsingUIAndEvictMainTabs();
-  OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  NSError* firstTabError = OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  GREYAssertNil(firstTabError, firstTabError.localizedDescription);
+  NSError* secondTabError = OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  GREYAssertNil(secondTabError, secondTabError.localizedDescription);
+
   [ChromeEarlGrey waitForIncognitoTabCount:2];
 
   // Switch back to the normal tabs.
-  SwitchToNormalMode();
+  NSError* switchError = SwitchToNormalMode();
+  GREYAssertNil(switchError, switchError.localizedDescription);
+
   [ChromeEarlGrey waitForWebViewContainingText:kURL2FirstWord];
 
   // Select the other one so it also reloads.
@@ -308,13 +321,16 @@
                  @"Fail to simulate tab backgrounding.");
 
   // Open incognito and clear normal tabs from memory.
-  OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  NSError* openError = OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  GREYAssertNil(openError, openError.localizedDescription);
   GREYAssertTrue(chrome_test_util::IsIncognitoMode(),
                  @"Failed to switch to incognito mode");
   histogramTester.ExpectTotalCount(kEvictedTabReloadTime, 0, failureBlock);
 
   // Switch back to the normal tabs.
-  SwitchToNormalMode();
+  NSError* switchError = SwitchToNormalMode();
+  GREYAssertNil(switchError, switchError.localizedDescription);
+
   [ChromeEarlGrey waitForWebViewContainingText:kURL2FirstWord];
 
   const GURL url1 = web::test::HttpServer::MakeUrl(kTestUrl1);
@@ -345,8 +361,12 @@
   [ChromeEarlGrey closeAllTabsInCurrentMode];
   GURL URL = web::test::HttpServer::MakeUrl(kTestUrl1);
   NewMainTabWithURL(URL, kURL1FirstWord);
-  OpenNewIncognitoTabUsingUIAndEvictMainTabs();
-  SwitchToNormalMode();
+  NSError* openError = OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  GREYAssertNil(openError, openError.localizedDescription);
+
+  NSError* switchError = SwitchToNormalMode();
+  GREYAssertNil(switchError, switchError.localizedDescription);
+
   [ChromeEarlGrey waitForWebViewContainingText:kURL1FirstWord];
   [ChromeEarlGrey waitForMainTabCount:1];
 
@@ -378,12 +398,18 @@
   [ChromeEarlGrey openNewTab];
   [ChromeEarlGrey openNewTab];
   chrome_test_util::LoadUrl(slowURL);
-  OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  NSError* openError = OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  GREYAssertNil(openError, openError.localizedDescription);
 
   web::test::SetUpHttpServer(std::make_unique<web::DelayedResponseProvider>(
       std::make_unique<HtmlResponseProvider>(responses), kSlowURLDelay));
 
-  SwitchToNormalMode();
+  NSError* switchError = SwitchToNormalMode();
+  // TODO(crbug.com/951600): We avoid asserting directly unless the test fails,
+  // due to timing issues.
+  if (switchError != nil) {
+    GREYAssert(false, switchError.localizedDescription);
+  }
 
   // Turn off synchronization of GREYAssert to test the pending states.
   [[GREYConfiguration sharedInstance]
@@ -425,11 +451,17 @@
 
   NewMainTabWithURL(slowURL, "Slow");
 
-  OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  NSError* openError = OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  GREYAssertNil(openError, openError.localizedDescription);
   web::test::SetUpHttpServer(std::make_unique<web::DelayedResponseProvider>(
       std::make_unique<HtmlResponseProvider>(responses), kSlowURLDelay));
 
-  SwitchToNormalMode();
+  NSError* switchError = SwitchToNormalMode();
+  // TODO(crbug.com/951600): We avoid asserting directly unless the test fails,
+  // due to timing issues.
+  if (switchError != nil) {
+    GREYAssert(false, switchError.localizedDescription);
+  }
 
   // Letting page load start.
   base::test::ios::SpinRunLoopWithMinDelay(base::TimeDelta::FromSecondsD(0.5));
@@ -470,9 +502,12 @@
   };
 
   NewMainTabWithURL(slowURL, responses[slowURL]);
-  OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  NSError* openError = OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  GREYAssertNil(openError, openError.localizedDescription);
 
-  SwitchToNormalMode();
+  NSError* switchError = SwitchToNormalMode();
+  GREYAssertNil(switchError, switchError.localizedDescription);
+
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SettingsMenuPrivacyButton()];
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
@@ -500,11 +535,17 @@
   [ChromeEarlGrey openNewTab];
   chrome_test_util::LoadUrl(slowURL);
 
-  OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  NSError* openError = OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  GREYAssertNil(openError, openError.localizedDescription);
 
   web::test::SetUpHttpServer(std::make_unique<web::DelayedResponseProvider>(
       std::make_unique<HtmlResponseProvider>(responses), kSlowURLDelay));
-  SwitchToNormalMode();
+  NSError* switchError = SwitchToNormalMode();
+  // TODO(crbug.com/951600): We avoid asserting directly unless the test fails,
+  // due to timing issues.
+  if (switchError != nil) {
+    GREYAssert(false, switchError.localizedDescription);
+  }
 
   // Letting page load start.
   base::test::ios::SpinRunLoopWithMinDelay(base::TimeDelta::FromSecondsD(0.5));
@@ -585,8 +626,12 @@
 
   NSUInteger tabIndex = chrome_test_util::GetMainTabCount() - 1;
   [ChromeEarlGrey openNewTab];
-  OpenNewIncognitoTabUsingUIAndEvictMainTabs();
-  SwitchToNormalMode();
+  NSError* openError = OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  GREYAssertNil(openError, openError.localizedDescription);
+
+  NSError* switchError = SwitchToNormalMode();
+  GREYAssertNil(switchError, switchError.localizedDescription);
+
   chrome_test_util::SelectTabAtIndexInCurrentMode(tabIndex);
   [ChromeEarlGrey waitForWebViewContainingText:"arrived"];
 
@@ -632,8 +677,12 @@
   [ChromeEarlGrey waitForWebViewContainingText:"Whee"];
   NSUInteger tabIndex = chrome_test_util::GetMainTabCount() - 1;
   [ChromeEarlGrey openNewTab];
-  OpenNewIncognitoTabUsingUIAndEvictMainTabs();
-  SwitchToNormalMode();
+  NSError* openError = OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+  GREYAssertNil(openError, openError.localizedDescription);
+
+  NSError* switchError = SwitchToNormalMode();
+  GREYAssertNil(switchError, switchError.localizedDescription);
+
   chrome_test_util::SelectTabAtIndexInCurrentMode(tabIndex);
   [ChromeEarlGrey waitForWebViewContainingText:"Whee"];
 
@@ -665,11 +714,11 @@
       web::test::HttpServer::MakeUrl("http://destination");
   // Make the link that cover the whole page so that long pressing the web view
   // will trigger the link context menu.
-  responses[initialURL] = base::StringPrintf(
-      "<body style='width:auto; height:auto;'><a href='%s' "
-      "id='link'><div style='width:100%%; "
-      "height:100%%;'>link</div></a></body>",
-      destinationURL.spec().c_str());
+  responses[initialURL] =
+      base::StringPrintf("<body style='width:auto; height:auto;'><a href='%s' "
+                         "id='link'><div style='width:100%%; "
+                         "height:100%%;'>link</div></a></body>",
+                         destinationURL.spec().c_str());
   responses[destinationURL] = "Whee!";
   web::test::SetUpHttpServer(std::make_unique<HtmlResponseProvider>(responses));
   chrome_test_util::HistogramTester histogramTester;
diff --git a/ios/chrome/browser/metrics/tab_usage_recorder_test_util.h b/ios/chrome/browser/metrics/tab_usage_recorder_test_util.h
index 3ca044c2..3a5224a 100644
--- a/ios/chrome/browser/metrics/tab_usage_recorder_test_util.h
+++ b/ios/chrome/browser/metrics/tab_usage_recorder_test_util.h
@@ -5,14 +5,16 @@
 #ifndef IOS_CHROME_BROWSER_METRICS_TAB_USAGE_RECORDER_TEST_UTIL_H_
 #define IOS_CHROME_BROWSER_METRICS_TAB_USAGE_RECORDER_TEST_UTIL_H_
 
+@class NSError;
+
 namespace tab_usage_recorder_test_util {
 
 // Opens a new incognito tab using the UI and evicts any main tab model tabs.
-void OpenNewIncognitoTabUsingUIAndEvictMainTabs();
+NSError* OpenNewIncognitoTabUsingUIAndEvictMainTabs();
 
 // Switches to normal mode using the tab switcher and selects the
 // previously-selected normal tab. Assumes current mode is Incognito.
-void SwitchToNormalMode();
+NSError* SwitchToNormalMode();
 
 }  // namespace tab_usage_recorder_test_util
 
diff --git a/ios/chrome/browser/metrics/tab_usage_recorder_test_util.mm b/ios/chrome/browser/metrics/tab_usage_recorder_test_util.mm
index e5a4c5f..9a1e59cc 100644
--- a/ios/chrome/browser/metrics/tab_usage_recorder_test_util.mm
+++ b/ios/chrome/browser/metrics/tab_usage_recorder_test_util.mm
@@ -21,6 +21,7 @@
 #import "ios/chrome/test/app/tab_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
+#import "ios/chrome/test/earl_grey/chrome_error_util.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
@@ -35,7 +36,7 @@
 
 // Shows the tab switcher by tapping the switcher button.  Works on both phone
 // and tablet.
-void ShowTabSwitcher() {
+bool ShowTabSwitcher() {
   id<GREYMatcher> matcher = chrome_test_util::TabGridOpenButton();
   // Perform a tap with a timeout. Occasionally EG doesn't sync up properly to
   // the animations of tab switcher, so it is necessary to poll here.
@@ -50,15 +51,14 @@
                                  }];
 
   // Wait until 2 seconds for the tap.
-  BOOL hasClicked = [tapTabSwitcher waitWithTimeout:2];
-  GREYAssertTrue(hasClicked, @"Tab switcher could not be tapped.");
+  return [tapTabSwitcher waitWithTimeout:2];
 }
 
 }  // namespace
 
 namespace tab_usage_recorder_test_util {
 
-void OpenNewIncognitoTabUsingUIAndEvictMainTabs() {
+NSError* OpenNewIncognitoTabUsingUIAndEvictMainTabs() {
   int nb_incognito_tab = chrome_test_util::GetIncognitoTabCount();
   [ChromeEarlGreyUI openToolsMenu];
   id<GREYMatcher> new_incognito_tab_button_matcher =
@@ -69,18 +69,29 @@
   ConditionBlock condition = ^bool {
     return chrome_test_util::IsIncognitoMode();
   };
-  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(kWaitElementTimeout,
-                                                          condition),
-             @"Waiting switch to incognito mode.");
+
+  bool success = base::test::ios::WaitUntilConditionOrTimeout(
+      kWaitElementTimeout, condition);
+  if (!success) {
+    return chrome_test_util::NSErrorWithLocalizedDescription(
+        @"Waiting switch to incognito mode.");
+  }
+
   chrome_test_util::EvictOtherTabModelTabs();
+  return nil;
 }
 
-void SwitchToNormalMode() {
-  GREYAssertTrue(chrome_test_util::IsIncognitoMode(),
-                 @"Switching to normal mode is only allowed from Incognito.");
+NSError* SwitchToNormalMode() {
+  if (!chrome_test_util::IsIncognitoMode()) {
+    return chrome_test_util::NSErrorWithLocalizedDescription(
+        @"Switching to normal mode is only allowed from Incognito.");
+  }
 
   // Enter the tab grid to switch modes.
-  ShowTabSwitcher();
+  if (!ShowTabSwitcher()) {
+    return chrome_test_util::NSErrorWithLocalizedDescription(
+        @"Tab switcher could not be tapped.");
+  }
 
   // Switch modes and exit the tab grid.
   TabModel* model = chrome_test_util::GetMainController()
@@ -99,13 +110,17 @@
   ConditionBlock condition = ^bool {
     return !chrome_test_util::IsIncognitoMode();
   };
-  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(kWaitElementTimeout,
-                                                          condition),
-             @"Waiting switch to normal mode.");
+
+  if (!base::test::ios::WaitUntilConditionOrTimeout(kWaitElementTimeout,
+                                                    condition)) {
+    return chrome_test_util::NSErrorWithLocalizedDescription(
+        @"Waiting switch to normal mode.");
+  }
 
   [[GREYConfiguration sharedInstance]
           setValue:@(YES)
       forConfigKey:kGREYConfigKeySynchronizationEnabled];
+  return nil;
 }
 
 }  // namespace tab_usage_recorder_test_util
diff --git a/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm b/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm
index ef801d04..5f5e5308 100644
--- a/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm
+++ b/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm
@@ -58,7 +58,7 @@
 // YES if the view should be dismissed after any touch gesture has ended.
 @property(nonatomic, assign) BOOL shouldDismissAfterTouchesEnded;
 // UIButton with title |self.buttonText|, which triggers the Infobar action.
-@property(nonatomic, weak) UIButton* infobarButton;
+@property(nonatomic, strong) UIButton* infobarButton;
 // UILabel displaying |self.titleText|.
 @property(nonatomic, strong) UILabel* titleLabel;
 // UILabel displaying |self.subTitleText|.
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
index ee26ceb..22907ad 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
+++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
@@ -81,6 +81,9 @@
   self.modalViewController.infobarModalDelegate = self;
   self.modalViewController.username =
       self.passwordInfoBarDelegate->GetUserNameText();
+  self.modalViewController.saveButtonText =
+      base::SysUTF16ToNSString(self.passwordInfoBarDelegate->GetButtonLabel(
+          ConfirmInfoBarDelegate::BUTTON_OK));
   self.modalViewController.URL = self.passwordInfoBarDelegate->GetURLHostText();
 }
 
diff --git a/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.h b/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.h
index a524c21..f956098 100644
--- a/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.h
+++ b/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.h
@@ -19,6 +19,8 @@
 @property(nonatomic, copy) NSString* username;
 // The URL being displayed in the InfobarModal.
 @property(nonatomic, copy) NSString* URL;
+// The text used for the save credentials button.
+@property(nonatomic, copy) NSString* saveButtonText;
 
 @end
 
diff --git a/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.mm b/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.mm
index a9cc9de..40aaa9d 100644
--- a/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.mm
+++ b/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.mm
@@ -11,6 +11,7 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_detail_icon_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util_mac.h"
@@ -27,9 +28,18 @@
   ItemTypeURL = kItemTypeEnumZero,
   ItemTypeUsername,
   ItemTypePassword,
-  ItemTypeSavePassword,
+  ItemTypeSaveCredentials,
 };
 
+@interface InfobarPasswordTableViewController ()
+// Item that holds the Username TextField information.
+@property(nonatomic, strong) TableViewTextEditItem* usernameItem;
+// Item that holds the Password TextField information.
+@property(nonatomic, strong) TableViewTextEditItem* passwordItem;
+// Item that holds the SaveCredentials Button information.
+@property(nonatomic, strong) TableViewTextButtonItem* saveCredentialsItem;
+@end
+
 @implementation InfobarPasswordTableViewController
 
 #pragma mark - ViewController Lifecycle
@@ -78,30 +88,30 @@
   [model addItem:URLDetailItem
       toSectionWithIdentifier:SectionIdentifierContent];
 
-  TableViewDetailIconItem* usernameDetailItem =
-      [[TableViewDetailIconItem alloc] initWithType:ItemTypeUsername];
-  usernameDetailItem.text =
+  TableViewTextEditItem* usernameTextEditItem =
+      [[TableViewTextEditItem alloc] initWithType:ItemTypeUsername];
+  usernameTextEditItem.textFieldName =
       l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_USERNAME);
-  usernameDetailItem.detailText = self.username;
-  [model addItem:usernameDetailItem
+  usernameTextEditItem.textFieldValue = self.username;
+  usernameTextEditItem.textFieldEnabled = YES;
+  [model addItem:usernameTextEditItem
       toSectionWithIdentifier:SectionIdentifierContent];
 
-  TableViewDetailIconItem* passwordDetailItem =
-      [[TableViewDetailIconItem alloc] initWithType:ItemTypePassword];
-  passwordDetailItem.text =
+  self.passwordItem =
+      [[TableViewTextEditItem alloc] initWithType:ItemTypePassword];
+  self.passwordItem.textFieldName =
       l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD);
   // TODO(crbug.com/927064): Set the number of dots depending on Password
   // length?
-  passwordDetailItem.detailText = @"•••••••••";
-  [model addItem:passwordDetailItem
+  self.passwordItem.textFieldValue = @"•••••••••";
+  self.passwordItem.textFieldEnabled = YES;
+  [model addItem:self.passwordItem
       toSectionWithIdentifier:SectionIdentifierContent];
 
-  TableViewTextButtonItem* savePasswordButtonItem =
-      [[TableViewTextButtonItem alloc] initWithType:ItemTypeSavePassword];
-  // TODO(crbug.com/927064): Create IDS String for this once we're sure about
-  // the exact text.
-  savePasswordButtonItem.buttonText = @"Save Password";
-  [model addItem:savePasswordButtonItem
+  self.saveCredentialsItem =
+      [[TableViewTextButtonItem alloc] initWithType:ItemTypeSaveCredentials];
+  self.saveCredentialsItem.buttonText = self.saveButtonText;
+  [model addItem:self.saveCredentialsItem
       toSectionWithIdentifier:SectionIdentifierContent];
 }
 
@@ -111,16 +121,30 @@
         cellForRowAtIndexPath:(NSIndexPath*)indexPath {
   UITableViewCell* cell = [super tableView:tableView
                      cellForRowAtIndexPath:indexPath];
-  NSInteger itemTypeSelected =
-      [self.tableViewModel itemTypeForIndexPath:indexPath];
+  ItemType itemType = static_cast<ItemType>(
+      [self.tableViewModel itemTypeForIndexPath:indexPath]);
 
-  if (itemTypeSelected == ItemTypeSavePassword) {
-    TableViewTextButtonCell* tableViewTextButtonCell =
-        base::mac::ObjCCastStrict<TableViewTextButtonCell>(cell);
-    [tableViewTextButtonCell.button
-               addTarget:self.infobarModalDelegate
-                  action:@selector(modalInfobarButtonWasPressed:)
-        forControlEvents:UIControlEventTouchUpInside];
+  switch (itemType) {
+    case ItemTypeSaveCredentials: {
+      TableViewTextButtonCell* tableViewTextButtonCell =
+          base::mac::ObjCCastStrict<TableViewTextButtonCell>(cell);
+      [tableViewTextButtonCell.button
+                 addTarget:self.infobarModalDelegate
+                    action:@selector(modalInfobarButtonWasPressed:)
+          forControlEvents:UIControlEventTouchUpInside];
+      break;
+    }
+    case ItemTypeUsername:
+    case ItemTypePassword: {
+      TableViewTextEditCell* editCell =
+          base::mac::ObjCCast<TableViewTextEditCell>(cell);
+      [editCell.textField addTarget:self
+                             action:@selector(updateSaveCredentialsButtonState)
+                   forControlEvents:UIControlEventEditingChanged];
+      break;
+    }
+    case ItemTypeURL:
+      break;
   }
 
   return cell;
@@ -128,6 +152,15 @@
 
 #pragma mark - Private Methods
 
+- (void)updateSaveCredentialsButtonState {
+  BOOL currentButtonState = [self.saveCredentialsItem isEnabled];
+  BOOL newButtonState = [self.passwordItem.textFieldValue length] ? YES : NO;
+  if (currentButtonState != newButtonState) {
+    self.saveCredentialsItem.enabled = newButtonState;
+    [self reconfigureCellsForItems:@[ self.saveCredentialsItem ]];
+  }
+}
+
 - (void)dismissInfobarModal:(UIButton*)sender {
   [self.infobarModalDelegate dismissInfobarModal:sender completion:nil];
 }
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.h b/ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.h
index 78d81bd52..fca2d28 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.h
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.h
@@ -14,6 +14,7 @@
 
 // Text being displayed above the button.
 @property(nonatomic, readwrite, strong) NSString* text;
+
 // Text for cell button.
 @property(nonatomic, readwrite, strong) NSString* buttonText;
 
@@ -23,6 +24,10 @@
 // Accessibility identifier that will assigned to the button.
 @property(nonatomic, strong) NSString* buttonAccessibilityIdentifier;
 
+// Whether the Item's button should be enabled or not. Button is enabled by
+// default.
+@property(nonatomic, assign, getter=isEnabled) BOOL enabled;
+
 @end
 
 // TableViewTextButtonCell contains a textLabel and a UIbutton
@@ -31,6 +36,7 @@
 
 // Cell text information.
 @property(nonatomic, strong) UILabel* textLabel;
+
 // Action button. Note: Set action method in the TableView datasource method.
 @property(nonatomic, strong) UIButton* button;
 
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.mm
index 8e40c89d..774bdb7 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.mm
@@ -16,6 +16,8 @@
 const CGFloat grayHexColor = 0x6d6d72;
 // Action button blue background color.
 const CGFloat blueHexColor = 0x1A73E8;
+// Alpha value for the disabled action button.
+const CGFloat disabledButtonAlpha = 0.5;
 // Vertical spacing between stackView and cell contentView.
 const CGFloat stackViewVerticalSpacing = 9.0;
 // Horizontal spacing between stackView and cell contentView.
@@ -42,6 +44,7 @@
   self = [super initWithType:type];
   if (self) {
     self.cellClass = [TableViewTextButtonCell class];
+    _enabled = YES;
   }
   return self;
 }
@@ -58,6 +61,11 @@
                                     ? self.buttonBackgroundColor
                                     : UIColorFromRGB(blueHexColor);
   [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
+  cell.button.enabled = self.enabled;
+  if (!self.enabled) {
+    cell.button.backgroundColor = [cell.button.backgroundColor
+        colorWithAlphaComponent:disabledButtonAlpha];
+  }
 }
 
 @end
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index a6666ca..a3718969 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -227,6 +227,8 @@
     "chrome_earl_grey.mm",
     "chrome_earl_grey_ui.h",
     "chrome_earl_grey_ui.mm",
+    "chrome_error_util.h",
+    "chrome_error_util.mm",
     "chrome_matchers.h",
     "chrome_matchers.mm",
     "chrome_matchers_shorthand.h",
diff --git a/ios/chrome/test/earl_grey/chrome_error_util.h b/ios/chrome/test/earl_grey/chrome_error_util.h
new file mode 100644
index 0000000..c3b7258
--- /dev/null
+++ b/ios/chrome/test/earl_grey/chrome_error_util.h
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_TEST_EARL_GREY_CHROME_ERROR_UTIL_H_
+#define IOS_CHROME_TEST_EARL_GREY_CHROME_ERROR_UTIL_H_
+
+@class NSError;
+@class NSString;
+
+namespace chrome_test_util {
+
+// Returns a NSError with generic domain and error code, and the provided string
+// as localizedDescription.
+NSError* NSErrorWithLocalizedDescription(NSString* error_description);
+
+}  // namespace chrome_test_util
+
+#endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_ERROR_UTIL_H_
diff --git a/ios/chrome/test/earl_grey/chrome_error_util.mm b/ios/chrome/test/earl_grey/chrome_error_util.mm
new file mode 100644
index 0000000..1d9207bd
--- /dev/null
+++ b/ios/chrome/test/earl_grey/chrome_error_util.mm
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/test/earl_grey/chrome_error_util.h"
+
+#import <Foundation/Foundation.h>
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace chrome_test_util {
+
+NSError* NSErrorWithLocalizedDescription(NSString* error_description) {
+  NSDictionary* userInfo = @{
+    NSLocalizedDescriptionKey : error_description,
+  };
+
+  return [[NSError alloc] initWithDomain:@"com.google.chrome.errorDomain"
+                                    code:0
+                                userInfo:userInfo];
+}
+
+}  // namespace chrome_test_util
diff --git a/ios/third_party/material_components_ios/README.chromium b/ios/third_party/material_components_ios/README.chromium
index 24df9a2..fb3a9cd 100644
--- a/ios/third_party/material_components_ios/README.chromium
+++ b/ios/third_party/material_components_ios/README.chromium
@@ -1,7 +1,7 @@
 Name: Material Components for iOS
 URL: https://github.com/material-components/material-components-ios
 Version: 0
-Revision: d4fdb374214cfe2010d167338affd3fa9e67263c
+Revision: 82cfc2ceebd3d1bfbaf6b5129d08f2b1f4054508
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/ios/third_party/material_roboto_font_loader_ios/README.chromium b/ios/third_party/material_roboto_font_loader_ios/README.chromium
index 5cec40ca..e9730cc 100644
--- a/ios/third_party/material_roboto_font_loader_ios/README.chromium
+++ b/ios/third_party/material_roboto_font_loader_ios/README.chromium
@@ -1,7 +1,7 @@
 Name: Material Roboto Font Loader iOS
 URL: https://github.com/material-foundation/material-roboto-font-loader-ios
 Version: 0
-Revision: 4aa51e906e5671c71d24e991f1f10d782a58409f
+Revision: bc63eabbbd1e14cee0779b05827e08db2e413553
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index eccf4872..c99a549 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -72,6 +72,7 @@
     ":buildflags",
     ":common",
     ":image_processor",
+    ":video_frame_mapper",
     "//base",
     "//gpu",
     "//media",
diff --git a/media/gpu/linux/platform_video_frame_utils.cc b/media/gpu/linux/platform_video_frame_utils.cc
index 81ac1d8a4..e68df74 100644
--- a/media/gpu/linux/platform_video_frame_utils.cc
+++ b/media/gpu/linux/platform_video_frame_utils.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/files/scoped_file.h"
+#include "build/build_config.h"
 #include "media/base/scopedfd_helper.h"
 #include "media/base/video_frame_layout.h"
 #include "media/gpu/format_utils.h"
@@ -102,18 +103,28 @@
   DCHECK(video_frame);
 
   gfx::GpuMemoryBufferHandle handle;
+#if defined(OS_LINUX)
   handle.type = gfx::NATIVE_PIXMAP;
 
   std::vector<base::ScopedFD> duped_fds =
       DuplicateFDs(video_frame->DmabufFds());
   const size_t num_planes = VideoFrame::NumPlanes(video_frame->format());
-  DCHECK_EQ(num_planes, duped_fds.size());
+  const size_t num_buffers = video_frame->layout().buffer_sizes().size();
+  DCHECK_EQ(video_frame->layout().planes().size(), num_planes);
+
+  // TODO(crbug.com/946880): Handles case that num_planes mismatches num_buffers
   for (size_t i = 0; i < num_planes; ++i) {
     const auto& plane = video_frame->layout().planes()[i];
+    size_t buffer_size = 0;
+    if (i < num_buffers)
+      buffer_size = video_frame->layout().buffer_sizes()[i];
     handle.native_pixmap_handle.planes.emplace_back(
-        plane.stride, plane.offset, i, std::move(duped_fds[i]), plane.modifier);
+        plane.stride, plane.offset, buffer_size, std::move(duped_fds[i]),
+        plane.modifier);
   }
-
+#else
+  NOTREACHED();
+#endif  // defined(OS_LINUX)
   return handle;
 }
 
diff --git a/media/gpu/test/BUILD.gn b/media/gpu/test/BUILD.gn
index 60eb18f..de591d3f 100644
--- a/media/gpu/test/BUILD.gn
+++ b/media/gpu/test/BUILD.gn
@@ -50,7 +50,6 @@
     ":decode_helpers",
     ":helpers",
     "//media/gpu",
-    "//media/gpu:video_frame_mapper",
   ]
 }
 
@@ -64,7 +63,6 @@
     ":helpers",
     ":render_helpers",
     "//media/gpu",
-    "//media/gpu:video_frame_mapper",
     "//ui/gfx/codec:codec",
   ]
 }
@@ -100,7 +98,6 @@
   ]
   deps = [
     "//media/gpu",
-    "//media/gpu:video_frame_mapper",
   ]
 }
 
diff --git a/media/gpu/test/video_player/video.cc b/media/gpu/test/video_player/video.cc
index 91be797..f731f1a2 100644
--- a/media/gpu/test/video_player/video.cc
+++ b/media/gpu/test/video_player/video.cc
@@ -46,21 +46,21 @@
 
   int64_t file_size;
   if (!base::GetFileSize(file_path_, &file_size) || (file_size < 0)) {
-    VLOGF(1) << "Failed to read file size: " << file_path_;
+    LOG(ERROR) << "Failed to read file size: " << file_path_;
     return false;
   }
 
   std::vector<uint8_t> data(file_size);
   if (base::ReadFile(file_path_, reinterpret_cast<char*>(data.data()),
                      base::checked_cast<int>(file_size)) != file_size) {
-    VLOGF(1) << "Failed to read file: " << file_path_;
+    LOG(ERROR) << "Failed to read file: " << file_path_;
     return false;
   }
 
   data_ = std::move(data);
 
   if (!LoadMetadata()) {
-    VLOGF(1) << "Failed to load metadata";
+    LOG(ERROR) << "Failed to load metadata";
     return false;
   }
 
@@ -114,7 +114,7 @@
 
 bool Video::LoadMetadata() {
   if (IsMetadataLoaded()) {
-    VLOGF(1) << "Video metadata is already loaded";
+    LOG(ERROR) << "Video metadata is already loaded";
     return false;
   }
 
@@ -125,14 +125,14 @@
   base::Optional<base::FilePath> resolved_path =
       ResolveFilePath(metadata_file_path_);
   if (!resolved_path) {
-    VLOGF(1) << "Video metadata file not found: " << metadata_file_path_;
+    LOG(ERROR) << "Video metadata file not found: " << metadata_file_path_;
     return false;
   }
   metadata_file_path_ = resolved_path.value();
 
   std::string json_data;
   if (!base::ReadFileToString(metadata_file_path_, &json_data)) {
-    VLOGF(1) << "Failed to read video metadata file: " << metadata_file_path_;
+    LOG(ERROR) << "Failed to read video metadata file: " << metadata_file_path_;
     return false;
   }
 
@@ -140,28 +140,28 @@
   std::unique_ptr<base::Value> metadata(
       reader.ReadToValueDeprecated(json_data));
   if (!metadata) {
-    VLOGF(1) << "Failed to parse video metadata: " << metadata_file_path_
-             << ": " << reader.GetErrorMessage();
+    LOG(ERROR) << "Failed to parse video metadata: " << metadata_file_path_
+               << ": " << reader.GetErrorMessage();
     return false;
   }
 
   const base::Value* profile =
       metadata->FindKeyOfType("profile", base::Value::Type::STRING);
   if (!profile) {
-    VLOGF(1) << "Key \"profile\" is not found in " << metadata_file_path_;
+    LOG(ERROR) << "Key \"profile\" is not found in " << metadata_file_path_;
     return false;
   }
   profile_ = ConvertStringtoProfile(profile->GetString());
   codec_ = ConvertProfileToCodec(profile_);
   if (profile_ == VIDEO_CODEC_PROFILE_UNKNOWN || codec_ == kUnknownVideoCodec) {
-    VLOGF(1) << profile->GetString() << " is not supported";
+    LOG(ERROR) << profile->GetString() << " is not supported";
     return false;
   }
 
   const base::Value* num_frames =
       metadata->FindKeyOfType("num_frames", base::Value::Type::INTEGER);
   if (!num_frames) {
-    VLOGF(1) << "Key \"num_frames\" is not found in " << metadata_file_path_;
+    LOG(ERROR) << "Key \"num_frames\" is not found in " << metadata_file_path_;
     return false;
   }
   num_frames_ = static_cast<uint32_t>(num_frames->GetInt());
@@ -169,7 +169,8 @@
   const base::Value* num_fragments =
       metadata->FindKeyOfType("num_fragments", base::Value::Type::INTEGER);
   if (!num_fragments) {
-    VLOGF(1) << "Key \"num_fragments\" is not found in " << metadata_file_path_;
+    LOG(ERROR) << "Key \"num_fragments\" is not found in "
+               << metadata_file_path_;
     return false;
   }
   num_fragments_ = static_cast<uint32_t>(num_fragments->GetInt());
@@ -177,13 +178,13 @@
   const base::Value* width =
       metadata->FindKeyOfType("width", base::Value::Type::INTEGER);
   if (!width) {
-    VLOGF(1) << "Key \"width\" is not found in " << metadata_file_path_;
+    LOG(ERROR) << "Key \"width\" is not found in " << metadata_file_path_;
     return false;
   }
   const base::Value* height =
       metadata->FindKeyOfType("height", base::Value::Type::INTEGER);
   if (!height) {
-    VLOGF(1) << "Key \"height\" is not found in " << metadata_file_path_;
+    LOG(ERROR) << "Key \"height\" is not found in " << metadata_file_path_;
     return false;
   }
   resolution_ = gfx::Size(static_cast<uint32_t>(width->GetInt()),
@@ -192,7 +193,8 @@
   const base::Value* md5_checksums =
       metadata->FindKeyOfType("md5_checksums", base::Value::Type::LIST);
   if (!md5_checksums) {
-    VLOGF(1) << "Key \"md5_checksums\" is not found in " << metadata_file_path_;
+    LOG(ERROR) << "Key \"md5_checksums\" is not found in "
+               << metadata_file_path_;
     return false;
   }
   for (const base::Value& checksum : md5_checksums->GetList()) {
@@ -202,8 +204,8 @@
   const base::Value* thumbnail_checksums =
       metadata->FindKeyOfType("thumbnail_checksums", base::Value::Type::LIST);
   if (!thumbnail_checksums) {
-    VLOGF(1) << "Key \"thumbnail_checksums\" is not found in "
-             << metadata_file_path_;
+    LOG(ERROR) << "Key \"thumbnail_checksums\" is not found in "
+               << metadata_file_path_;
     return false;
   }
   for (const base::Value& checksum : thumbnail_checksums->GetList()) {
diff --git a/media/gpu/test/video_player/video_decoder_client.cc b/media/gpu/test/video_player/video_decoder_client.cc
index 09808e0..c894f02 100644
--- a/media/gpu/test/video_player/video_decoder_client.cc
+++ b/media/gpu/test/video_player/video_decoder_client.cc
@@ -153,12 +153,19 @@
   WaitingCB waiting_cb =
       base::BindRepeating([](WaitingReason) { NOTIMPLEMENTED(); });
 
-  // TODO(dstaessens@) Currently we always create a VDA-based video decoder,
-  // change to use a factory that can create different types of decoders.
-  decoder_ = base::WrapUnique(
-      new TestVDAVideoDecoder(decoder_client_config_.allocation_mode,
-                              gfx::ColorSpace(), frame_renderer_.get()));
-  decoder_->Initialize(config, false, nullptr, init_cb, output_cb, waiting_cb);
+  if (decoder_client_config_.use_vd) {
+    // TODO(dstaessens@) Create VD-based video decoder.
+    NOTIMPLEMENTED();
+  } else {
+    // The video decoder client expects decoders to use the VD interface. We can
+    // use the TestVDAVideoDecoder wrapper here to test VDA-based video
+    // decoders.
+    decoder_ = base::WrapUnique(
+        new TestVDAVideoDecoder(decoder_client_config_.allocation_mode,
+                                gfx::ColorSpace(), frame_renderer_.get()));
+    decoder_->Initialize(config, false, nullptr, init_cb, output_cb,
+                         waiting_cb);
+  }
 
   DCHECK_LE(decoder_client_config_.max_outstanding_decode_requests,
             static_cast<size_t>(decoder_->GetMaxDecodeRequests()));
diff --git a/media/gpu/test/video_player/video_decoder_client.h b/media/gpu/test/video_player/video_decoder_client.h
index 73756dc6..30bac98 100644
--- a/media/gpu/test/video_player/video_decoder_client.h
+++ b/media/gpu/test/video_player/video_decoder_client.h
@@ -43,6 +43,8 @@
   size_t max_outstanding_decode_requests = 1;
   // How the pictures buffers should be allocated.
   AllocationMode allocation_mode = AllocationMode::kImport;
+  // Use VD-based video decoders instead of VDA-based video decoders.
+  bool use_vd = false;
 };
 
 // The video decoder client is responsible for the communication between the
diff --git a/media/gpu/test/video_player/video_player_test_environment.cc b/media/gpu/test/video_player/video_player_test_environment.cc
index 4dc9586a..750084c 100644
--- a/media/gpu/test/video_player/video_player_test_environment.cc
+++ b/media/gpu/test/video_player/video_player_test_environment.cc
@@ -35,7 +35,8 @@
     const base::FilePath& video_path,
     const base::FilePath& video_metadata_path,
     bool enable_validator,
-    bool output_frames) {
+    bool output_frames,
+    bool use_vd) {
   auto video = std::make_unique<media::test::Video>(
       video_path.empty() ? base::FilePath(kDefaultTestVideoPath) : video_path,
       video_metadata_path);
@@ -45,16 +46,18 @@
   }
 
   return new VideoPlayerTestEnvironment(std::move(video), enable_validator,
-                                        output_frames);
+                                        output_frames, use_vd);
 }
 
 VideoPlayerTestEnvironment::VideoPlayerTestEnvironment(
     std::unique_ptr<media::test::Video> video,
     bool enable_validator,
-    bool output_frames)
+    bool output_frames,
+    bool use_vd)
     : video_(std::move(video)),
       enable_validator_(enable_validator),
-      output_frames_(output_frames) {}
+      output_frames_(output_frames),
+      use_vd_(use_vd) {}
 
 VideoPlayerTestEnvironment::~VideoPlayerTestEnvironment() = default;
 
@@ -115,6 +118,10 @@
   return output_frames_;
 }
 
+bool VideoPlayerTestEnvironment::UseVD() const {
+  return use_vd_;
+}
+
 base::FilePath::StringType VideoPlayerTestEnvironment::GetTestName() const {
   const ::testing::TestInfo* const test_info =
       ::testing::UnitTest::GetInstance()->current_test_info();
diff --git a/media/gpu/test/video_player/video_player_test_environment.h b/media/gpu/test/video_player/video_player_test_environment.h
index 6a5a4dd..e4cd659a 100644
--- a/media/gpu/test/video_player/video_player_test_environment.h
+++ b/media/gpu/test/video_player/video_player_test_environment.h
@@ -30,7 +30,8 @@
       const base::FilePath& video_path,
       const base::FilePath& video_metadata_path,
       bool enable_validator,
-      bool output_frames);
+      bool output_frames,
+      bool use_vd);
   ~VideoPlayerTestEnvironment() override;
 
   // Set up the video decode test environment, only called once.
@@ -44,6 +45,8 @@
   bool IsValidatorEnabled() const;
   // Check whether outputting frames is enabled.
   bool IsFramesOutputEnabled() const;
+  // Check whether we should use VD-based video decoders instead of VDA-based.
+  bool UseVD() const;
 
   // Get the name of the current test.
   base::FilePath::StringType GetTestName() const;
@@ -51,12 +54,14 @@
  private:
   VideoPlayerTestEnvironment(std::unique_ptr<media::test::Video> video,
                              bool enable_validator,
-                             bool output_frames);
+                             bool output_frames,
+                             bool use_vd);
 
   std::unique_ptr<base::test::ScopedTaskEnvironment> task_environment_;
   const std::unique_ptr<media::test::Video> video_;
   const bool enable_validator_;
   const bool output_frames_;
+  const bool use_vd_;
 
   // An exit manager is required to run callbacks on shutdown.
   base::AtExitManager at_exit_manager;
diff --git a/media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.cc b/media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.cc
index 3e31e41c..56279f08 100644
--- a/media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.cc
+++ b/media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.cc
@@ -15,9 +15,9 @@
 #include "media/gpu/vaapi/vaapi_wrapper.h"
 #include "media/video/picture.h"
 
-#if defined(USE_OZONE) || defined(USE_EGL)
-#include "media/gpu/vaapi/vaapi_picture_native_pixmap.h"
-#endif  // defined(USE_OZONE) || defined(USE_EGL)
+#if defined(OS_LINUX)
+#include "media/gpu/linux/platform_video_frame_utils.h"
+#endif
 
 namespace media {
 
@@ -115,11 +115,9 @@
   }
 
   gfx::GpuMemoryBufferHandle gmb_handle;
-#if defined(USE_OZONE) || defined(USE_EGL)
-  gmb_handle =
-      VaapiPictureNativePixmap::CreateGpuMemoryBufferHandleFromVideoFrame(
-          video_frame.get());
-#endif  // defined(USE_OZONE) || defined(USE_EGL)
+#if defined(OS_LINUX)
+  gmb_handle = CreateGpuMemoryBufferHandle(video_frame.get());
+#endif
   if (gmb_handle.is_null()) {
     VLOGF(1) << "Failed to CreateGMBHandleFromVideoFrame.";
     return nullptr;
diff --git a/media/gpu/vaapi/vaapi_picture_native_pixmap.cc b/media/gpu/vaapi/vaapi_picture_native_pixmap.cc
index 82304a0..13ed1e9 100644
--- a/media/gpu/vaapi/vaapi_picture_native_pixmap.cc
+++ b/media/gpu/vaapi/vaapi_picture_native_pixmap.cc
@@ -50,29 +50,4 @@
   return va_surface_->id();
 }
 
-// static
-gfx::GpuMemoryBufferHandle
-VaapiPictureNativePixmap::CreateGpuMemoryBufferHandleFromVideoFrame(
-    const VideoFrame* const video_frame) {
-  DCHECK(video_frame->HasDmaBufs());
-
-  const auto& planes = video_frame->layout().planes();
-  const auto& fds = video_frame->DmabufFds();
-  DCHECK_EQ(fds.size(), planes.size());
-
-  gfx::GpuMemoryBufferHandle handle;
-  handle.type = gfx::NATIVE_PIXMAP;
-  for (size_t i = 0; i < planes.size(); ++i) {
-    int dup_fd = HANDLE_EINTR(dup(fds[i].get()));
-    if (dup_fd == -1) {
-      PLOG(ERROR) << "Failed duplicating dmabuf fd";
-      return gfx::GpuMemoryBufferHandle();
-    }
-
-    handle.native_pixmap_handle.planes.emplace_back(
-        planes[i].stride, planes[i].offset, 0, base::ScopedFD(dup_fd));
-  }
-  return handle;
-}
-
 }  // namespace media
diff --git a/media/gpu/vaapi/vaapi_picture_native_pixmap.h b/media/gpu/vaapi/vaapi_picture_native_pixmap.h
index 8d70ec8a..f506341 100644
--- a/media/gpu/vaapi/vaapi_picture_native_pixmap.h
+++ b/media/gpu/vaapi/vaapi_picture_native_pixmap.h
@@ -24,7 +24,6 @@
 
 namespace media {
 
-class VideoFrame;
 class VaapiWrapper;
 
 // Implementation of VaapiPicture based on NativePixmaps.
@@ -41,9 +40,6 @@
       uint32_t texture_target);
   ~VaapiPictureNativePixmap() override;
 
-  static gfx::GpuMemoryBufferHandle CreateGpuMemoryBufferHandleFromVideoFrame(
-      const VideoFrame* const video_frame);
-
   // VaapiPicture implementation.
   bool DownloadFromSurface(const scoped_refptr<VASurface>& va_surface) override;
   bool AllowOverlay() const override;
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
index f9bbfd6..0a70709 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -41,8 +41,8 @@
 #include "media/gpu/vp8_reference_frame_vector.h"
 #include "media/gpu/vp9_reference_frame_vector.h"
 
-#if defined(OS_POSIX)
-#include "media/gpu/vaapi/vaapi_picture_native_pixmap.h"
+#if defined(OS_LINUX)
+#include "media/gpu/linux/platform_video_frame_utils.h"
 #endif
 
 #define NOTIFY_ERROR(error, msg)                        \
@@ -555,10 +555,8 @@
         vaapi_wrapper_, MakeGLContextCurrentCallback(), BindGLImageCallback(),
         PictureBuffer(kDummyPictureBufferId, frame->coded_size()));
     gfx::GpuMemoryBufferHandle gmb_handle;
-#if defined(OS_POSIX)
-    gmb_handle =
-        VaapiPictureNativePixmap::CreateGpuMemoryBufferHandleFromVideoFrame(
-            frame.get());
+#if defined(OS_LINUX)
+    gmb_handle = CreateGpuMemoryBufferHandle(frame.get());
 #endif
     if (gmb_handle.is_null()) {
       NOTIFY_ERROR(kPlatformFailureError,
diff --git a/media/gpu/video_decode_accelerator_perf_tests.cc b/media/gpu/video_decode_accelerator_perf_tests.cc
index 48245aa..ba41040f 100644
--- a/media/gpu/video_decode_accelerator_perf_tests.cc
+++ b/media/gpu/video_decode_accelerator_perf_tests.cc
@@ -20,6 +20,29 @@
 
 namespace {
 
+// Video decoder perf tests usage message.
+constexpr const char* usage_msg =
+    "usage: video_decode_accelerator_perf_tests\n"
+    "           [-v=<level>] [--vmodule=<config>] [--use_vd] [--gtest_help]\n"
+    "           [--help] [<video path>] [<video metadata path>]\n";
+
+// Video decoder perf tests help message.
+constexpr const char* help_msg =
+    "Run the video decode accelerator performance tests on the video\n"
+    "specified by <video path>. If no <video path> is given the default\n"
+    "\"test-25fps.h264\" video will be used.\n"
+    "\nThe <video metadata path> should specify the location of a json file\n"
+    "containing the video's metadata, such as frame checksums. By default\n"
+    "<video path>.json will be used.\n"
+    "\nThe following arguments are supported:\n"
+    "   -v                  enable verbose mode, e.g. -v=2.\n"
+    "  --vmodule            enable verbose mode for the specified module,\n"
+    "                       e.g. --vmodule=*media/gpu*=2.\n"
+    "  --use_vd             use the new VD-based video decoders, instead of\n"
+    "                       the default VDA-based video decoders.\n"
+    "  --gtest_help         display the gtest help and exit.\n"
+    "  --help               display this help and exit.\n";
+
 media::test::VideoPlayerTestEnvironment* g_env;
 
 // Default output folder used to store performance metrics.
@@ -147,9 +170,13 @@
     auto performance_evaluator = std::make_unique<PerformanceEvaluator>();
     performance_evaluator_ = performance_evaluator.get();
     frame_processors.push_back(std::move(performance_evaluator));
+
+    // Use the new VD-based video decoders if requested.
+    VideoDecoderClientConfig config;
+    config.use_vd = g_env->UseVD();
+
     return VideoPlayer::Create(video, FrameRendererDummy::Create(),
-                               std::move(frame_processors),
-                               VideoDecoderClientConfig());
+                               std::move(frame_processors), config);
   }
 
   PerformanceEvaluator* performance_evaluator_;
@@ -177,26 +204,53 @@
 }  // namespace media
 
 int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
+  // Set the default test data path.
+  media::test::Video::SetTestDataPath(media::GetTestDataPath());
+
+  // Print the help message if requested. This needs to be done before
+  // initializing gtest, to overwrite the default gtest help message.
   base::CommandLine::Init(argc, argv);
+  const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+  LOG_ASSERT(cmd_line);
+  if (cmd_line->HasSwitch("help")) {
+    std::cout << media::test::usage_msg << "\n" << media::test::help_msg;
+    return 0;
+  }
 
   // Check if a video was specified on the command line.
-  const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
   base::CommandLine::StringVector args = cmd_line->GetArgs();
   base::FilePath video_path =
       (args.size() >= 1) ? base::FilePath(args[0]) : base::FilePath();
   base::FilePath video_metadata_path =
       (args.size() >= 2) ? base::FilePath(args[1]) : base::FilePath();
 
-  // Set the default test data path.
-  media::test::Video::SetTestDataPath(media::GetTestDataPath());
+  // Parse command line arguments.
+  bool use_vd = false;
+  base::CommandLine::SwitchMap switches = cmd_line->GetSwitches();
+  for (base::CommandLine::SwitchMap::const_iterator it = switches.begin();
+       it != switches.end(); ++it) {
+    if (it->first.find("gtest_") == 0 ||               // Handled by GoogleTest
+        it->first == "v" || it->first == "vmodule") {  // Handled by Chrome
+      continue;
+    }
+
+    if (it->first == "use_vd") {
+      use_vd = true;
+    } else {
+      std::cout << "unknown option: --" << it->first << "\n"
+                << media::test::usage_msg;
+      return EXIT_FAILURE;
+    }
+  }
+
+  testing::InitGoogleTest(&argc, argv);
 
   // Set up our test environment.
   media::test::VideoPlayerTestEnvironment* test_environment =
       media::test::VideoPlayerTestEnvironment::Create(
-          video_path, video_metadata_path, false, false);
+          video_path, video_metadata_path, false, false, use_vd);
   if (!test_environment)
-    return 0;
+    return EXIT_FAILURE;
 
   media::test::g_env = static_cast<media::test::VideoPlayerTestEnvironment*>(
       testing::AddGlobalTestEnvironment(test_environment));
diff --git a/media/gpu/video_decode_accelerator_tests.cc b/media/gpu/video_decode_accelerator_tests.cc
index 0970a25c..5153294 100644
--- a/media/gpu/video_decode_accelerator_tests.cc
+++ b/media/gpu/video_decode_accelerator_tests.cc
@@ -22,21 +22,29 @@
 // Video decoder tests usage message.
 constexpr const char* usage_msg =
     "usage: video_decode_accelerator_tests\n"
-    "           [--help] [--disable_validator] [--output_frames]\n"
+    "           [-v=<level>] [--vmodule=<config>] [--disable_validator]\n"
+    "           [--output_frames] [--use_vd] [--gtest_help] [--help]\n"
     "           [<video path>] [<video metadata path>]\n";
 
 // Video decoder tests help message.
 constexpr const char* help_msg =
-    "Run the video decode accelerator tests on the specified video. If no\n"
-    "video is specified the default \"test-25fps.h264\" video will be used.\n"
-    "\nThe video metadata path should specify the location of a json file\n"
+    "Run the video decode accelerator tests on the video specified by\n"
+    "<video path>. If no <video path> is given the default\n"
+    "\"test-25fps.h264\" video will be used.\n"
+    "\nThe <video metadata path> should specify the location of a json file\n"
     "containing the video's metadata, such as frame checksums. By default\n"
     "<video path>.json will be used.\n"
     "\nThe following arguments are supported:\n"
+    "   -v                  enable verbose mode, e.g. -v=2.\n"
+    "  --vmodule            enable verbose mode for the specified module,\n"
+    "                       e.g. --vmodule=*media/gpu*=2.\n"
     "  --disable_validator  disable frame validation, useful on old\n"
     "                       platforms that don't support import mode.\n"
     "  --output_frames      write all decoded video frames to the\n"
     "                       \"video_frames\" folder.\n"
+    "  --use_vd             use the new VD-based video decoders, instead of\n"
+    "                       the default VDA-based video decoders.\n"
+    "  --gtest_help         display the gtest help and exit.\n"
     "  --help               display this help and exit.\n";
 
 media::test::VideoPlayerTestEnvironment* g_env;
@@ -46,7 +54,7 @@
  public:
   std::unique_ptr<VideoPlayer> CreateVideoPlayer(
       const Video* video,
-      const VideoDecoderClientConfig& config = VideoDecoderClientConfig(),
+      VideoDecoderClientConfig config = VideoDecoderClientConfig(),
       std::unique_ptr<FrameRenderer> frame_renderer =
           FrameRendererDummy::Create()) {
     LOG_ASSERT(video);
@@ -66,6 +74,9 @@
       frame_processors.push_back(VideoFrameFileWriter::Create(output_folder));
     }
 
+    // Use the new VD-based video decoders if requested.
+    config.use_vd = g_env->UseVD();
+
     return VideoPlayer::Create(video, std::move(frame_renderer),
                                std::move(frame_processors), config);
   }
@@ -266,22 +277,19 @@
 }  // namespace media
 
 int main(int argc, char** argv) {
-  base::CommandLine::Init(argc, argv);
-  const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+  // Set the default test data path.
+  media::test::Video::SetTestDataPath(media::GetTestDataPath());
 
   // Print the help message if requested. This needs to be done before
   // initializing gtest, to overwrite the default gtest help message.
+  base::CommandLine::Init(argc, argv);
+  const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
   LOG_ASSERT(cmd_line);
   if (cmd_line->HasSwitch("help")) {
     std::cout << media::test::usage_msg << "\n" << media::test::help_msg;
     return 0;
   }
 
-  testing::InitGoogleTest(&argc, argv);
-
-  // Set the default test data path.
-  media::test::Video::SetTestDataPath(media::GetTestDataPath());
-
   // Check if a video was specified on the command line.
   base::CommandLine::StringVector args = cmd_line->GetArgs();
   base::FilePath video_path =
@@ -292,6 +300,7 @@
   // Parse command line arguments.
   bool enable_validator = true;
   bool output_frames = false;
+  bool use_vd = false;
   base::CommandLine::SwitchMap switches = cmd_line->GetSwitches();
   for (base::CommandLine::SwitchMap::const_iterator it = switches.begin();
        it != switches.end(); ++it) {
@@ -304,19 +313,24 @@
       enable_validator = false;
     } else if (it->first == "output_frames") {
       output_frames = true;
+    } else if (it->first == "use_vd") {
+      use_vd = true;
     } else {
       std::cout << "unknown option: --" << it->first << "\n"
                 << media::test::usage_msg;
-      return 0;
+      return EXIT_FAILURE;
     }
   }
 
+  testing::InitGoogleTest(&argc, argv);
+
   // Set up our test environment.
   media::test::VideoPlayerTestEnvironment* test_environment =
       media::test::VideoPlayerTestEnvironment::Create(
-          video_path, video_metadata_path, enable_validator, output_frames);
+          video_path, video_metadata_path, enable_validator, output_frames,
+          use_vd);
   if (!test_environment)
-    return 0;
+    return EXIT_FAILURE;
 
   media::test::g_env = static_cast<media::test::VideoPlayerTestEnvironment*>(
       testing::AddGlobalTestEnvironment(test_environment));
diff --git a/services/ws/client_root.cc b/services/ws/client_root.cc
index 7919d47..275fa7b 100644
--- a/services/ws/client_root.cc
+++ b/services/ws/client_root.cc
@@ -8,6 +8,7 @@
 #include "base/callback_forward.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
+#include "base/trace_event/trace_event.h"
 #include "components/viz/common/surfaces/surface_info.h"
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "services/ws/client_change.h"
@@ -331,6 +332,9 @@
 void ClientRoot::NotifyClientOfNewBounds() {
   last_bounds_ = GetBoundsToSend(window_);
   auto id = ProxyWindow::GetMayBeNull(window_)->local_surface_id_allocation();
+  TRACE_EVENT_WITH_FLOW0("ui", "ClientRoot::NotifyClientOfNewBounds",
+                         id->local_surface_id().hash(),
+                         TRACE_EVENT_FLAG_FLOW_OUT);
   window_tree_->window_tree_client_->OnWindowBoundsChanged(
       window_tree_->TransportIdForWindow(window_), last_bounds_,
       ProxyWindow::GetMayBeNull(window_)->local_surface_id_allocation());
diff --git a/services/ws/input_devices/input_device_server.cc b/services/ws/input_devices/input_device_server.cc
index f8b82b2..072be9d 100644
--- a/services/ws/input_devices/input_device_server.cc
+++ b/services/ws/input_devices/input_device_server.cc
@@ -57,6 +57,8 @@
     OnTouchpadDeviceConfigurationChanged();
   if (input_device_types & ui::InputDeviceEventObserver::kTouchscreen)
     OnTouchscreenDeviceConfigurationChanged();
+  if (input_device_types & ui::InputDeviceEventObserver::kUncategorized)
+    OnUncategorizedDeviceConfigurationChanged();
 }
 
 void InputDeviceServer::OnKeyboardDeviceConfigurationChanged() {
@@ -112,6 +114,7 @@
   observer->OnDeviceListsComplete(
       manager_->GetKeyboardDevices(), manager_->GetTouchscreenDevices(),
       manager_->GetMouseDevices(), manager_->GetTouchpadDevices(),
+      manager_->GetUncategorizedDevices(),
       manager_->AreTouchscreenTargetDisplaysValid());
 }
 
@@ -129,4 +132,14 @@
   });
 }
 
+void InputDeviceServer::OnUncategorizedDeviceConfigurationChanged() {
+  if (!manager_->AreDeviceListsComplete())
+    return;
+
+  auto& devices = manager_->GetUncategorizedDevices();
+  observers_.ForAllPtrs([&devices](mojom::InputDeviceObserverMojo* observer) {
+    observer->OnUncategorizedDeviceConfigurationChanged(devices);
+  });
+}
+
 }  // namespace ws
diff --git a/services/ws/input_devices/input_device_server.h b/services/ws/input_devices/input_device_server.h
index 01c4763..5830d727 100644
--- a/services/ws/input_devices/input_device_server.h
+++ b/services/ws/input_devices/input_device_server.h
@@ -51,6 +51,7 @@
   void OnTouchscreenDeviceConfigurationChanged();
   void OnMouseDeviceConfigurationChanged();
   void OnTouchpadDeviceConfigurationChanged();
+  void OnUncategorizedDeviceConfigurationChanged();
 
   mojo::BindingSet<mojom::InputDeviceServer> bindings_;
   mojo::InterfacePtrSet<mojom::InputDeviceObserverMojo> observers_;
diff --git a/services/ws/public/cpp/input_devices/input_device_client.cc b/services/ws/public/cpp/input_devices/input_device_client.cc
index ffcb9e7..a58e7ae4 100644
--- a/services/ws/public/cpp/input_devices/input_device_client.cc
+++ b/services/ws/public/cpp/input_devices/input_device_client.cc
@@ -39,6 +39,11 @@
   return touchpad_devices_;
 }
 
+const std::vector<ui::InputDevice>& InputDeviceClient::GetUncategorizedDevices()
+    const {
+  return uncategorized_devices_;
+}
+
 bool InputDeviceClient::AreDeviceListsComplete() const {
   return device_lists_complete_;
 }
@@ -79,6 +84,12 @@
   NotifyObserversKeyboardDeviceConfigurationChanged();
 }
 
+void InputDeviceClient::OnUncategorizedDeviceConfigurationChanged(
+    const std::vector<ui::InputDevice>& devices) {
+  uncategorized_devices_ = devices;
+  NotifyObserversUncategorizedDeviceConfigurationChanged();
+}
+
 void InputDeviceClient::OnTouchscreenDeviceConfigurationChanged(
     const std::vector<ui::TouchscreenDevice>& devices,
     bool touchscreen_target_display_ids_changed) {
@@ -116,6 +127,7 @@
     const std::vector<ui::TouchscreenDevice>& touchscreen_devices,
     const std::vector<ui::InputDevice>& mouse_devices,
     const std::vector<ui::InputDevice>& touchpad_devices,
+    const std::vector<ui::InputDevice>& uncategorized_devices,
     bool are_touchscreen_target_displays_valid) {
   are_touchscreen_target_displays_valid_ =
       are_touchscreen_target_displays_valid;
@@ -133,6 +145,9 @@
   if (!touchpad_devices.empty())
     OnTouchpadDeviceConfigurationChanged(touchpad_devices);
 
+  if (!uncategorized_devices.empty())
+    OnUncategorizedDeviceConfigurationChanged(uncategorized_devices);
+
   if (!device_lists_complete_) {
     device_lists_complete_ = true;
     NotifyObserversDeviceListsComplete();
@@ -170,4 +185,12 @@
   }
 }
 
+void InputDeviceClient::
+    NotifyObserversUncategorizedDeviceConfigurationChanged() {
+  for (auto& observer : observers_) {
+    observer.OnInputDeviceConfigurationChanged(
+        ui::InputDeviceEventObserver::kUncategorized);
+  }
+}
+
 }  // namespace ws
diff --git a/services/ws/public/cpp/input_devices/input_device_client.h b/services/ws/public/cpp/input_devices/input_device_client.h
index 8b8b8c6..a8864ab 100644
--- a/services/ws/public/cpp/input_devices/input_device_client.h
+++ b/services/ws/public/cpp/input_devices/input_device_client.h
@@ -39,6 +39,7 @@
       const override;
   const std::vector<ui::InputDevice>& GetMouseDevices() const override;
   const std::vector<ui::InputDevice>& GetTouchpadDevices() const override;
+  const std::vector<ui::InputDevice>& GetUncategorizedDevices() const override;
   bool AreDeviceListsComplete() const override;
   bool AreTouchscreensEnabled() const override;
   bool AreTouchscreenTargetDisplaysValid() const override;
@@ -61,11 +62,14 @@
       const std::vector<ui::InputDevice>& devices) override;
   void OnTouchpadDeviceConfigurationChanged(
       const std::vector<ui::InputDevice>& devices) override;
+  void OnUncategorizedDeviceConfigurationChanged(
+      const std::vector<ui::InputDevice>& devices) override;
   void OnDeviceListsComplete(
       const std::vector<ui::InputDevice>& keyboard_devices,
       const std::vector<ui::TouchscreenDevice>& touchscreen_devices,
       const std::vector<ui::InputDevice>& mouse_devices,
       const std::vector<ui::InputDevice>& touchpad_devices,
+      const std::vector<ui::InputDevice>& uncategorized_devices,
       bool are_touchscreen_target_displays_valid) override;
   void OnStylusStateChanged(ui::StylusState state) override;
 
@@ -76,6 +80,7 @@
   void NotifyObserversKeyboardDeviceConfigurationChanged();
   void NotifyObserversTouchscreenDeviceConfigurationChanged();
   void NotifyObserversTouchpadDeviceConfigurationChanged();
+  void NotifyObserversUncategorizedDeviceConfigurationChanged();
 
   mojo::Binding<mojom::InputDeviceObserverMojo> binding_;
 
@@ -87,6 +92,7 @@
   std::vector<ui::TouchscreenDevice> touchscreen_devices_;
   std::vector<ui::InputDevice> mouse_devices_;
   std::vector<ui::InputDevice> touchpad_devices_;
+  std::vector<ui::InputDevice> uncategorized_devices_;
   bool device_lists_complete_ = false;
   bool are_touchscreen_target_displays_valid_ = false;
 
diff --git a/services/ws/public/cpp/input_devices/input_device_client_test_api.cc b/services/ws/public/cpp/input_devices/input_device_client_test_api.cc
index 20f630d..170276f8 100644
--- a/services/ws/public/cpp/input_devices/input_device_client_test_api.cc
+++ b/services/ws/public/cpp/input_devices/input_device_client_test_api.cc
@@ -66,7 +66,7 @@
   if (ui::DeviceDataManager::instance_)
     ui::DeviceDataManager::instance_->OnDeviceListsComplete();
   else
-    GetInputDeviceClient()->OnDeviceListsComplete({}, {}, {}, {}, false);
+    GetInputDeviceClient()->OnDeviceListsComplete({}, {}, {}, {}, {}, false);
 }
 
 void InputDeviceClientTestApi::SetKeyboardDevices(
diff --git a/services/ws/public/mojom/input_devices/input_device_server.mojom b/services/ws/public/mojom/input_devices/input_device_server.mojom
index c06fc1e..4fe97c9 100644
--- a/services/ws/public/mojom/input_devices/input_device_server.mojom
+++ b/services/ws/public/mojom/input_devices/input_device_server.mojom
@@ -25,6 +25,11 @@
   // Is called when the list of touchpads changes.
   OnTouchpadDeviceConfigurationChanged(array<ui.mojom.InputDevice> devices);
 
+  // Is called when the list of uncategorized input devices (besides keyboards,
+  // touchscreens, mice and touchpads) changes.
+  OnUncategorizedDeviceConfigurationChanged(
+      array<ui.mojom.InputDevice> devices);
+
   // Is called once all of the input-device lists are available. This will
   // always be the first call that an observer receives.
   OnDeviceListsComplete(
@@ -32,6 +37,7 @@
       array<ui.mojom.TouchscreenDevice> touchscreen_devices,
       array<ui.mojom.InputDevice> mouse_devices,
       array<ui.mojom.InputDevice> touchpad_devices,
+      array<ui.mojom.InputDevice> uncategorized_devices,
       bool are_touchscreen_target_displays_valid);
 
   // Is called when a stylus is removed or inserted into the device.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 9447c82ed..df12c146 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -5691,21 +5691,6 @@
             ]
         }
     ],
-    "WebContentsOcclusion": [
-        {
-            "platforms": [
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "WindowsWebContentsOcclusion",
-                    "enable_features": [
-                        "WebContentsOcclusion"
-                    ]
-                }
-            ]
-        }
-    ],
     "WebPaymentsPerMethodCanMakePaymentQuota": [
         {
             "platforms": [
@@ -6062,6 +6047,21 @@
             ]
         }
     ],
+    "WindowsWebContentsOcclusion": [
+        {
+            "platforms": [
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "WindowsWebContentsOcclusion",
+                    "enable_features": [
+                        "WebContentsOcclusion"
+                    ]
+                }
+            ]
+        }
+    ],
     "history-manipulation-intervention": [
         {
             "platforms": [
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 0a8edd5..9b623ee 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -461,7 +461,6 @@
     "web/web_language_detection_details.h",
     "web/web_local_frame.h",
     "web/web_local_frame_client.h",
-    "web/web_manifest_fetcher.h",
     "web/web_manifest_parser.h",
     "web/web_meaningful_layout.h",
     "web/web_media_player_action.h",
diff --git a/third_party/blink/public/web/web_ax_object.h b/third_party/blink/public/web/web_ax_object.h
index 9e401922..f56b763e 100644
--- a/third_party/blink/public/web/web_ax_object.h
+++ b/third_party/blink/public/web/web_ax_object.h
@@ -79,6 +79,12 @@
     return *this;
   }
 
+  BLINK_EXPORT bool operator==(const WebAXObject& other) const;
+  BLINK_EXPORT bool operator!=(const WebAXObject& other) const;
+  BLINK_EXPORT bool operator<(const WebAXObject& other) const;
+  BLINK_EXPORT bool operator<=(const WebAXObject& other) const;
+  BLINK_EXPORT bool operator>(const WebAXObject& other) const;
+  BLINK_EXPORT bool operator>=(const WebAXObject& other) const;
   BLINK_EXPORT static WebAXObject FromWebNode(const WebNode&);
   BLINK_EXPORT static WebAXObject FromWebDocument(const WebDocument&);
   BLINK_EXPORT static WebAXObject FromWebDocumentByID(const WebDocument&, int);
@@ -224,7 +230,8 @@
       WebAXObject& focus_object,
       int& focus_offset,
       ax::mojom::TextAffinity& focus_affinity) const;
-  BLINK_EXPORT void Selection(WebAXObject& anchor_object,
+  BLINK_EXPORT void Selection(bool& is_selection_backward,
+                              WebAXObject& anchor_object,
                               int& anchor_offset,
                               ax::mojom::TextAffinity& anchor_affinity,
                               WebAXObject& focus_object,
@@ -369,6 +376,9 @@
                                       SkMatrix44& container_transform,
                                       bool* clips_children = nullptr) const;
 
+  // Exchanges a WebAXObject with another.
+  BLINK_EXPORT void Swap(WebAXObject& other);
+
   // Returns a brief description of the object, suitable for debugging. E.g. its
   // role and name.
   BLINK_EXPORT WebString ToString() const;
diff --git a/third_party/blink/public/web/web_manifest_fetcher.h b/third_party/blink/public/web/web_manifest_fetcher.h
deleted file mode 100644
index 58bf778..0000000
--- a/third_party/blink/public/web/web_manifest_fetcher.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_MANIFEST_FETCHER_H_
-#define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_MANIFEST_FETCHER_H_
-
-#include <memory>
-#include "base/callback.h"
-#include "third_party/blink/public/platform/web_common.h"
-#include "third_party/blink/public/platform/web_private_ptr.h"
-
-namespace blink {
-
-class ManifestFetcher;
-class WebDocument;
-class WebURL;
-class WebURLResponse;
-class WebString;
-
-class WebManifestFetcher {
- public:
-  // This will be called asynchronously after the URL has been fetched,
-  // successfully or not.  If there is a failure, response and data will both be
-  // empty.  |response| and |data| are both valid until the ManifestFetcher
-  // instance is destroyed.
-  using Callback =
-      base::OnceCallback<void(const WebURLResponse&, const WebString&)>;
-
-  BLINK_EXPORT explicit WebManifestFetcher(const WebURL& url);
-  BLINK_EXPORT ~WebManifestFetcher() { Reset(); }
-
-  BLINK_EXPORT void Reset();
-
-  BLINK_EXPORT void Start(WebDocument* web_document,
-                          bool use_credentials,
-                          Callback callback);
-  BLINK_EXPORT void Cancel();
-
- private:
-  bool IsNull() const { return private_.IsNull(); }
-
-  WebPrivatePtr<ManifestFetcher> private_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_MANIFEST_FETCHER_H_
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index de95335..4a543fa 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -5173,6 +5173,10 @@
       # Manifest content.
       optional string data
 
+  experimental command getInstallabilityErrors
+    returns
+      array of string errors
+
   # Returns all browser cookies. Depending on the backend support, will return detailed cookie
   # information in the `cookies` field.
   experimental deprecated command getCookies
@@ -5705,7 +5709,6 @@
       # Base64-encoded data
       binary data
 
-
 domain Performance
 
   # Run-time execution metric.
diff --git a/third_party/blink/renderer/core/inspector/inspect_tool_highlight.html b/third_party/blink/renderer/core/inspector/inspect_tool_highlight.html
index bfb0011a..03a17962 100644
--- a/third_party/blink/renderer/core/inspector/inspect_tool_highlight.html
+++ b/third_party/blink/renderer/core/inspector/inspect_tool_highlight.html
@@ -594,6 +594,18 @@
         boxY = canvasHeight - arrowHalfWidth - titleHeight;
     }
 
+    // If tooltip intersects with the bounds, hide it.
+    if (boxX < bounds.maxX && boxX + titleWidth > bounds.minX &&
+        boxY < bounds.maxY && boxY + titleHeight > bounds.minY) {
+      tooltipContent.style.display = 'none';
+      return;
+    }
+
+    if (boxX + titleWidth >= bounds.minX && boxX + titleWidth <= bounds.maxX && boxY + titleHeight >= bounds.minY && boxY + titleHeight <= bounds.maxY) {
+      tooltipContent.style.display = 'none';
+      return;
+    }
+
     tooltipContent.style.top = boxY + "px";
     tooltipContent.style.left = boxX + "px";
     tooltipContent.style.setProperty('--arrow-visibility', arrowHidden ? 'hidden' : 'visible');
diff --git a/third_party/blink/renderer/core/inspector/inspector_protocol_config.json b/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
index 0003c4d..7b29fd67 100644
--- a/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
+++ b/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
@@ -74,7 +74,7 @@
             {
                 "domain": "Page",
                 "exclude": ["getNavigationHistory", "navigateToHistoryEntry", "resetNavigationHistory", "captureScreenshot", "screencastFrameAck", "handleJavaScriptDialog", "setColorPickerEnabled",
-                            "getAppManifest", "setControlNavigations", "processNavigation", "printToPDF", "bringToFront", "setDownloadBehavior", "navigate", "crash", "close", "setWebLifecycleState", "captureSnapshot"],
+                            "getAppManifest", "setControlNavigations", "processNavigation", "printToPDF", "bringToFront", "setDownloadBehavior", "navigate", "crash", "close", "setWebLifecycleState", "captureSnapshot", "getInstallabilityErrors"],
                 "async": ["getResourceContent", "searchInResource"],
                 "exclude_events": ["screencastFrame", "screencastVisibilityChanged", "colorPicked", "interstitialShown", "interstitialHidden", "javascriptDialogOpening", "javascriptDialogClosed", "navigationRequested"]
             },
diff --git a/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.cc b/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.cc
index d4198ae..0b5ff29 100644
--- a/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.cc
+++ b/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.cc
@@ -73,6 +73,17 @@
   }
 }
 
+void NGPhysicalOffsetRect::ExpandEdgesToPixelBoundaries() {
+  int left = FloorToInt(offset.left);
+  int top = FloorToInt(offset.top);
+  int max_right = (offset.left + size.width).Ceil();
+  int max_bottom = (offset.top + size.height).Ceil();
+  offset.left = LayoutUnit(left);
+  offset.top = LayoutUnit(top);
+  size.width = LayoutUnit(max_right - left);
+  size.height = LayoutUnit(max_bottom - top);
+}
+
 NGPhysicalOffsetRect::NGPhysicalOffsetRect(const LayoutRect& source)
     : NGPhysicalOffsetRect({source.X(), source.Y()},
                            {source.Width(), source.Height()}) {}
diff --git a/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h b/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h
index 82754b6..2ef735b 100644
--- a/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h
+++ b/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h
@@ -43,6 +43,7 @@
   void UniteEvenIfEmpty(const NGPhysicalOffsetRect&);
 
   void Expand(const NGPhysicalBoxStrut&);
+  void ExpandEdgesToPixelBoundaries();
 
   // Conversions from/to existing code. New code prefers type safety for
   // logical/physical distinctions.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
index 4ce2a71f..6db5844 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
@@ -267,8 +267,6 @@
     ink_overflow.Expand(text_shadow_logical_outsets);
   }
 
-  ink_overflow.ExpandEdgesToPixelBoundaries();
-
   // Uniting the frame rect ensures that non-ink spaces such side bearings, or
   // even space characters, are included in the visual rect for decorations.
   NGPhysicalOffsetRect local_ink_overflow = ConvertToLocal(ink_overflow);
@@ -278,6 +276,7 @@
     return;
   }
   local_ink_overflow.Unite(local_rect);
+  local_ink_overflow.ExpandEdgesToPixelBoundaries();
   EnsureRareData()->self_ink_overflow_ = local_ink_overflow;
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h
index 6c9aa94..c3f7669 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h
@@ -144,6 +144,9 @@
                          scoped_refptr<const ShapeResultView> shape_result);
 
   struct RareData {
+    USING_FAST_MALLOC(RareData);
+
+   public:
     NGPhysicalOffsetRect self_ink_overflow_;
     scoped_refptr<const ComputedStyle> style_;  // Used only for ellipsis.
   };
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
index 35f9638..49832c5 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
@@ -348,6 +348,9 @@
 
   // The ink overflow storage for when |InkOverflowOwnerBox()| is nullptr.
   struct NGInkOverflowModel {
+    USING_FAST_MALLOC(NGInkOverflowModel);
+
+   public:
     NGInkOverflowModel(const NGPhysicalOffsetRect& self_ink_overflow,
                        const NGPhysicalOffsetRect& contents_ink_overflow);
 
diff --git a/third_party/blink/renderer/devtools/front_end/application_test_runner/CacheStorageTestRunner.js b/third_party/blink/renderer/devtools/front_end/application_test_runner/CacheStorageTestRunner.js
index 14a0a8ad..6f8f6b97 100644
--- a/third_party/blink/renderer/devtools/front_end/application_test_runner/CacheStorageTestRunner.js
+++ b/third_party/blink/renderer/devtools/front_end/application_test_runner/CacheStorageTestRunner.js
@@ -26,7 +26,6 @@
       TestRunner.addResult(' '.repeat(8) + entries.join(', '));
     }
   }
-
   UI.panels.resources._sidebar.cacheStorageListTreeElement.expand();
 
   if (!pathFilter)
diff --git a/third_party/blink/renderer/devtools/front_end/inline_editor/bezierEditor.css b/third_party/blink/renderer/devtools/front_end/inline_editor/bezierEditor.css
index f1ecc31..6da7ab04 100644
--- a/third_party/blink/renderer/devtools/front_end/inline_editor/bezierEditor.css
+++ b/third_party/blink/renderer/devtools/front_end/inline_editor/bezierEditor.css
@@ -180,8 +180,7 @@
     margin-top: 16px;
 }
 
-svg.bezier-preset-modify:active,
-.-theme-selection-color {
+svg.bezier-preset-modify:active {
     transform: scale(1.1);
     background-color: var(--selection-bg-color);
 }
diff --git a/third_party/blink/renderer/devtools/front_end/resources/AppManifestView.js b/third_party/blink/renderer/devtools/front_end/resources/AppManifestView.js
index d61a88a2..baef908 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/AppManifestView.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/AppManifestView.js
@@ -93,15 +93,17 @@
    */
   async _updateManifest(immediately) {
     const {url, data, errors} = await this._resourceTreeModel.fetchAppManifest();
-    this._throttler.schedule(() => this._renderManifest(url, data, errors), immediately);
+    const installabilityErrors = await this._resourceTreeModel.getInstallabilityErrors();
+    this._throttler.schedule(() => this._renderManifest(url, data, errors, installabilityErrors), immediately);
   }
 
   /**
    * @param {string} url
    * @param {?string} data
    * @param {!Array<!Protocol.Page.AppManifestError>} errors
+   * @param {!Array<string>} installabilityErrors
    */
-  async _renderManifest(url, data, errors) {
+  async _renderManifest(url, data, errors, installabilityErrors) {
     if (!data && !errors.length) {
       this._emptyView.showWidget();
       this._reportView.hideWidget();
@@ -121,31 +123,20 @@
     if (!data)
       return;
 
-    const installabilityErrors = [];
-
     if (data.charCodeAt(0) === 0xFEFF)
       data = data.slice(1);  // Trim the BOM as per https://tools.ietf.org/html/rfc7159#section-8.1.
 
     const parsedManifest = JSON.parse(data);
     this._nameField.textContent = stringProperty('name');
     this._shortNameField.textContent = stringProperty('short_name');
-    if (!this._nameField.textContent && !this._shortNameField.textContent)
-      installabilityErrors.push(ls`Either 'name' or 'short_name' is required`);
 
     this._startURLField.removeChildren();
     const startURL = stringProperty('start_url');
     if (startURL) {
       const completeURL = /** @type {string} */ (Common.ParsedURL.completeURL(url, startURL));
       this._startURLField.appendChild(Components.Linkifier.linkifyURL(completeURL, {text: startURL}));
-      if (!this._serviceWorkerManager.hasRegistrationForURLs([completeURL, this._target.inspectedURL()]))
-        installabilityErrors.push(ls`Service worker is not registered or does not control the Start URL`);
-      else if (!await this._swHasFetchHandler())
-        installabilityErrors.push(ls`Service worker does not have the 'fetch' handler`);
-    } else {
-      installabilityErrors.push(ls`'start_url' needs to be a valid URL`);
     }
 
-
     this._themeColorSwatch.classList.toggle('hidden', !stringProperty('theme_color'));
     const themeColor = Common.Color.parse(stringProperty('theme_color') || 'white') || Common.Color.parse('white');
     this._themeColorSwatch.setColor(/** @type {!Common.Color} */ (themeColor));
@@ -157,33 +148,17 @@
     this._orientationField.textContent = stringProperty('orientation');
     const displayType = stringProperty('display');
     this._displayField.textContent = displayType;
-    if (!['minimal-ui', 'standalone', 'fullscreen'].includes(displayType))
-      installabilityErrors.push(ls`'display' property must be set to 'standalone', 'fullscreen' or 'minimal-ui'`);
 
     const icons = parsedManifest['icons'] || [];
-    let hasInstallableIcon = false;
     this._iconsSection.clearContent();
 
     for (const icon of icons) {
-      if (!icon.sizes)
-        hasInstallableIcon = true;  // any
       const title = (icon['sizes'] || '') + '\n' + (icon['type'] || '');
-      try {
-        const widthHeight = icon['sizes'].split('x');
-        if (parseInt(widthHeight[0], 10) >= 144 && parseInt(widthHeight[1], 10) >= 144)
-          hasInstallableIcon = true;
-      } catch (e) {
-      }
-
       const field = this._iconsSection.appendField(title);
       const image = await this._loadImage(Common.ParsedURL.completeURL(url, icon['src']));
       if (image)
         field.appendChild(image);
-      else
-        installabilityErrors.push(ls`Some of the icons could not be loaded`);
     }
-    if (!hasInstallableIcon)
-      installabilityErrors.push(ls`An icon at least 144px x 144px large is required`);
 
     this._installabilitySection.clearContent();
     this._installabilitySection.element.classList.toggle('hidden', !installabilityErrors.length);
@@ -203,32 +178,6 @@
   }
 
   /**
-   * @return {!Promise<boolean>}
-   */
-  async _swHasFetchHandler() {
-    for (const target of SDK.targetManager.targets()) {
-      if (target.type() !== SDK.Target.Type.Worker)
-        continue;
-      if (!target.parentTarget() || target.parentTarget().type() !== SDK.Target.Type.ServiceWorker)
-        continue;
-
-      const ec = target.model(SDK.RuntimeModel).defaultExecutionContext();
-      const result = await ec.evaluate(
-          {
-            expression: `'fetch' in getEventListeners(self)`,
-            includeCommandLineAPI: true,
-            silent: true,
-            returnByValue: true
-          },
-          false, false);
-      if (!result.object || !result.object.value)
-        continue;
-      return true;
-    }
-    return false;
-  }
-
-  /**
    * @param {?string} url
    * @return {!Promise<?Image>}
    */
diff --git a/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js b/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js
index 10123401..de8b0eb 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js
@@ -29,7 +29,6 @@
  */
 /**
  * @implements {SDK.TargetManager.Observer}
- * @implements {SDK.SDKModelObserver<!Resources.DOMStorageModel>}
  * @unrestricted
  */
 Resources.ApplicationPanelSidebar = class extends UI.VBox {
@@ -196,18 +195,22 @@
       this._addCookieDocument(frame);
     this._databaseModel.enable();
 
-    const indexedDBModel = this._target.model(Resources.IndexedDBModel);
-    if (indexedDBModel)
-      indexedDBModel.enable();
-
     const cacheStorageModel = this._target.model(SDK.ServiceWorkerCacheModel);
     if (cacheStorageModel)
       cacheStorageModel.enable();
     const resourceTreeModel = this._target.model(SDK.ResourceTreeModel);
     if (resourceTreeModel)
       this._populateApplicationCacheTree(resourceTreeModel);
-    SDK.targetManager.observeModels(Resources.DOMStorageModel, this);
+    SDK.targetManager.observeModels(Resources.DOMStorageModel, /** @type {!SDK.SDKModelObserver} */ ({
+                                      modelAdded: model => this._domStorageModelAdded(model),
+                                      modelRemoved: model => this._domStorageModelRemoved(model)
+                                    }));
     this.indexedDBListTreeElement._initialize();
+    SDK.targetManager.observeModels(
+        Resources.IndexedDBModel, /** @type {!SDK.SDKModelObserver} */ ({
+          modelAdded: model => model.enable(),
+          modelRemoved: model => this.indexedDBListTreeElement.removeIndexedDBForModel(model)
+        }));
     const serviceWorkerCacheModel = this._target.model(SDK.ServiceWorkerCacheModel);
     this.cacheStorageListTreeElement._initialize(serviceWorkerCacheModel);
     const backgroundServiceModel = this._target.model(Resources.BackgroundServiceModel);
@@ -218,25 +221,22 @@
   }
 
   /**
-   * @override
-   * @param {!Resources.DOMStorageModel} domStorageModel
+   * @param {!Resources.DOMStorageModel} model
    */
-  modelAdded(domStorageModel) {
-    domStorageModel.enable();
-    domStorageModel.storages().forEach(this._addDOMStorage.bind(this));
-    domStorageModel.addEventListener(Resources.DOMStorageModel.Events.DOMStorageAdded, this._domStorageAdded, this);
-    domStorageModel.addEventListener(Resources.DOMStorageModel.Events.DOMStorageRemoved, this._domStorageRemoved, this);
+  _domStorageModelAdded(model) {
+    model.enable();
+    model.storages().forEach(this._addDOMStorage.bind(this));
+    model.addEventListener(Resources.DOMStorageModel.Events.DOMStorageAdded, this._domStorageAdded, this);
+    model.addEventListener(Resources.DOMStorageModel.Events.DOMStorageRemoved, this._domStorageRemoved, this);
   }
 
   /**
-   * @override
-   * @param {!Resources.DOMStorageModel} domStorageModel
+   * @param {!Resources.DOMStorageModel} model
    */
-  modelRemoved(domStorageModel) {
-    domStorageModel.storages().forEach(this._removeDOMStorage.bind(this));
-    domStorageModel.removeEventListener(Resources.DOMStorageModel.Events.DOMStorageAdded, this._domStorageAdded, this);
-    domStorageModel.removeEventListener(
-        Resources.DOMStorageModel.Events.DOMStorageRemoved, this._domStorageRemoved, this);
+  _domStorageModelRemoved(model) {
+    model.storages().forEach(this._removeDOMStorage.bind(this));
+    model.removeEventListener(Resources.DOMStorageModel.Events.DOMStorageAdded, this._domStorageAdded, this);
+    model.removeEventListener(Resources.DOMStorageModel.Events.DOMStorageRemoved, this._domStorageRemoved, this);
   }
 
   _resetWithFrames() {
@@ -287,8 +287,11 @@
     this.cookieListTreeElement.removeChildren();
   }
 
+  /**
+   * @param {!Common.Event} event
+   */
   _frameNavigated(event) {
-    const frame = event.data;
+    const frame = /** @type {!SDK.ResourceTreeFrame} */ (event.data);
 
     if (frame.isTopFrame())
       this._reset();
@@ -1138,6 +1141,15 @@
   }
 
   /**
+   * @param {!Resources.IndexedDBModel} model
+   */
+  removeIndexedDBForModel(model) {
+    const idbDatabaseTreeElements = this._idbDatabaseTreeElements.filter(element => element._model === model);
+    for (const idbDatabaseTreeElement of idbDatabaseTreeElements)
+      this._removeIDBDatabaseTreeElement(idbDatabaseTreeElement);
+  }
+
+  /**
    * @override
    */
   onattach() {
@@ -1186,7 +1198,13 @@
     const idbDatabaseTreeElement = this._idbDatabaseTreeElement(model, databaseId);
     if (!idbDatabaseTreeElement)
       return;
+    this._removeIDBDatabaseTreeElement(idbDatabaseTreeElement);
+  }
 
+  /**
+   * @param {!Resources.IDBDatabaseTreeElement} idbDatabaseTreeElement
+   */
+  _removeIDBDatabaseTreeElement(idbDatabaseTreeElement) {
     idbDatabaseTreeElement.clear();
     this.removeChild(idbDatabaseTreeElement);
     this._idbDatabaseTreeElements.remove(idbDatabaseTreeElement);
@@ -1205,6 +1223,11 @@
     if (!idbDatabaseTreeElement)
       return;
     idbDatabaseTreeElement.update(database, entriesUpdated);
+    this._indexedDBLoadedForTest();
+  }
+
+  _indexedDBLoadedForTest() {
+    // For sniffing in tests.
   }
 
   /**
@@ -1222,23 +1245,12 @@
   }
 
   /**
-   * @param {!Resources.IndexedDBModel.DatabaseId} databaseId
    * @param {!Resources.IndexedDBModel} model
+   * @param {!Resources.IndexedDBModel.DatabaseId} databaseId
    * @return {?Resources.IDBDatabaseTreeElement}
    */
   _idbDatabaseTreeElement(model, databaseId) {
-    let index = -1;
-    let i;
-    for (i = 0; i < this._idbDatabaseTreeElements.length; ++i) {
-      if (this._idbDatabaseTreeElements[i]._databaseId.equals(databaseId) &&
-          this._idbDatabaseTreeElements[i]._model === model) {
-        index = i;
-        break;
-      }
-    }
-    if (index !== -1)
-      return this._idbDatabaseTreeElements[i];
-    return null;
+    return this._idbDatabaseTreeElements.find(x => x._databaseId.equals(databaseId) && x._model === model) || null;
   }
 };
 
diff --git a/third_party/blink/renderer/devtools/front_end/resources/IndexedDBModel.js b/third_party/blink/renderer/devtools/front_end/resources/IndexedDBModel.js
index 2ece1b2..5d531f3d 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/IndexedDBModel.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/IndexedDBModel.js
@@ -499,7 +499,7 @@
   }
 };
 
-SDK.SDKModel.register(Resources.IndexedDBModel, SDK.Target.Capability.None, false);
+SDK.SDKModel.register(Resources.IndexedDBModel, SDK.Target.Capability.DOM, false);
 
 Resources.IndexedDBModel.KeyTypes = {
   NumberType: 'number',
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/ResourceTreeModel.js b/third_party/blink/renderer/devtools/front_end/sdk/ResourceTreeModel.js
index e4ca040..d3c947f 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/ResourceTreeModel.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/ResourceTreeModel.js
@@ -411,6 +411,15 @@
       return {url: response.url, data: null, errors: []};
     return {url: response.url, data: response.data || null, errors: response.errors};
   }
+
+  /**
+   * @return {!Promise<!Array<string>>}
+   */
+  async getInstallabilityErrors() {
+    const response = await this._agent.invoke_getInstallabilityErrors({});
+    return response.errors || [];
+  }
+
   /**
    * @param {!SDK.ExecutionContext} a
    * @param {!SDK.ExecutionContext} b
diff --git a/third_party/blink/renderer/devtools/front_end/ui/softContextMenu.css b/third_party/blink/renderer/devtools/front_end/ui/softContextMenu.css
index ced1f40..7ee95b6 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/softContextMenu.css
+++ b/third_party/blink/renderer/devtools/front_end/ui/softContextMenu.css
@@ -62,8 +62,7 @@
     pointer-events: none;
 }
 
-.soft-context-menu-item-mouse-over,
-.-theme-selection-color {
+.soft-context-menu-item-mouse-over {
     border-top: 1px solid var(--context-menu-hover-bg);
     border-bottom: 1px solid var(--context-menu-hover-bg);
     background-color: var(--context-menu-hover-bg);
diff --git a/third_party/blink/renderer/devtools/front_end/ui/toolbar.css b/third_party/blink/renderer/devtools/front_end/ui/toolbar.css
index fb5e0db..5e0a3263 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/toolbar.css
+++ b/third_party/blink/renderer/devtools/front_end/ui/toolbar.css
@@ -147,13 +147,11 @@
 }
 
 .toolbar-button.toolbar-state-on .toolbar-glyph,
-.toolbar-blue-on-hover .toolbar-button:not(.toolbar-state-on):enabled:hover:not(:active),
-.-theme-selection-color {
+.toolbar-blue-on-hover .toolbar-button:not(.toolbar-state-on):enabled:hover:not(:active) {
     background-color: var(--accent-color);
 }
 
-.toolbar-button.toolbar-state-on .toolbar-text,
-.-theme-selection-color {
+.toolbar-button.toolbar-state-on .toolbar-text {
     color: var(--accent-color);
 }
 
@@ -166,13 +164,11 @@
 }
 
 .toolbar-button.toolbar-state-on:enabled:hover:not(:active) .toolbar-glyph,
-.toolbar-blue-on-hover .toolbar-button:not(.toolbar-state-on):enabled:active:hover,
-.-theme-selection-color {
+.toolbar-blue-on-hover .toolbar-button:not(.toolbar-state-on):enabled:active:hover {
     background-color: var(--accent-color);
 }
 
-.toolbar-button.toolbar-state-on:enabled:hover:not(:active) .toolbar-text,
-.-theme-selection-color {
+.toolbar-button.toolbar-state-on:enabled:hover:not(:active) .toolbar-text {
     color: var(--accent-color);
 }
 
diff --git a/third_party/blink/renderer/modules/exported/BUILD.gn b/third_party/blink/renderer/modules/exported/BUILD.gn
index b62c939..74f7348 100644
--- a/third_party/blink/renderer/modules/exported/BUILD.gn
+++ b/third_party/blink/renderer/modules/exported/BUILD.gn
@@ -13,7 +13,6 @@
     "web_dom_media_stream_track.cc",
     "web_embedded_worker_impl.cc",
     "web_embedded_worker_impl.h",
-    "web_manifest_fetcher.cc",
     "web_manifest_parser.cc",
     "web_storage_event_dispatcher_impl.cc",
     "web_user_media_request.cc",
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc
index 16a8acf..0d4d396 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -769,12 +769,14 @@
   }
 }
 
-void WebAXObject::Selection(WebAXObject& anchor_object,
+void WebAXObject::Selection(bool& is_selection_backward,
+                            WebAXObject& anchor_object,
                             int& anchor_offset,
                             ax::mojom::TextAffinity& anchor_affinity,
                             WebAXObject& focus_object,
                             int& focus_offset,
                             ax::mojom::TextAffinity& focus_affinity) const {
+  is_selection_backward = false;
   anchor_object = WebAXObject();
   anchor_offset = -1;
   anchor_affinity = ax::mojom::TextAffinity::kDownstream;
@@ -802,6 +804,7 @@
   const AXPosition extent = ax_selection.Extent();
   focus_object = WebAXObject(const_cast<AXObject*>(extent.ContainerObject()));
 
+  is_selection_backward = base > extent;
   if (base.IsTextPosition()) {
     anchor_offset = base.TextOffset();
     anchor_affinity = ToAXAffinity(base.Affinity());
@@ -1542,6 +1545,16 @@
   return private_->RequestScrollToGlobalPointAction(point);
 }
 
+void WebAXObject::Swap(WebAXObject& other) {
+  if (IsDetached() || other.IsDetached())
+    return;
+
+  AXObject* temp = private_.Get();
+  DCHECK(temp) << "|private_| should not be null.";
+  this->Assign(other);
+  other = temp;
+}
+
 WebString WebAXObject::ToString() const {
   if (IsDetached())
     return WebString();
@@ -1556,6 +1569,42 @@
   return *this;
 }
 
+bool WebAXObject::operator==(const WebAXObject& other) const {
+  if (IsDetached() || other.IsDetached())
+    return false;
+  return *private_ == *other.private_;
+}
+
+bool WebAXObject::operator!=(const WebAXObject& other) const {
+  if (IsDetached() || other.IsDetached())
+    return false;
+  return *private_ != *other.private_;
+}
+
+bool WebAXObject::operator<(const WebAXObject& other) const {
+  if (IsDetached() || other.IsDetached())
+    return false;
+  return *private_ < *other.private_;
+}
+
+bool WebAXObject::operator<=(const WebAXObject& other) const {
+  if (IsDetached() || other.IsDetached())
+    return false;
+  return *private_ <= *other.private_;
+}
+
+bool WebAXObject::operator>(const WebAXObject& other) const {
+  if (IsDetached() || other.IsDetached())
+    return false;
+  return *private_ > *other.private_;
+}
+
+bool WebAXObject::operator>=(const WebAXObject& other) const {
+  if (IsDetached() || other.IsDetached())
+    return false;
+  return *private_ >= *other.private_;
+}
+
 WebAXObject::operator AXObject*() const {
   return private_.Get();
 }
diff --git a/third_party/blink/renderer/modules/exported/web_manifest_fetcher.cc b/third_party/blink/renderer/modules/exported/web_manifest_fetcher.cc
deleted file mode 100644
index f2bffec69..0000000
--- a/third_party/blink/renderer/modules/exported/web_manifest_fetcher.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/public/web/web_manifest_fetcher.h"
-
-#include "third_party/blink/public/platform/web_url.h"
-#include "third_party/blink/public/web/web_document.h"
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/modules/manifest/manifest_fetcher.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-
-namespace blink {
-
-WebManifestFetcher::WebManifestFetcher(const WebURL& url)
-    : private_(MakeGarbageCollected<ManifestFetcher>(url)) {}
-
-void WebManifestFetcher::Start(WebDocument* web_document,
-                               bool use_credentials,
-                               Callback callback) {
-  DCHECK(!IsNull());
-
-  Document* document = web_document->Unwrap<Document>();
-  private_->Start(*document, use_credentials, std::move(callback));
-}
-
-void WebManifestFetcher::Cancel() {
-  DCHECK(!IsNull());
-  private_->Cancel();
-}
-
-void WebManifestFetcher::Reset() {
-  private_.Reset();
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/manifest/BUILD.gn b/third_party/blink/renderer/modules/manifest/BUILD.gn
index dc27426..31862cc 100644
--- a/third_party/blink/renderer/modules/manifest/BUILD.gn
+++ b/third_party/blink/renderer/modules/manifest/BUILD.gn
@@ -8,8 +8,6 @@
   sources = [
     "image_resource_type_converters.cc",
     "image_resource_type_converters.h",
-    "manifest_fetcher.cc",
-    "manifest_fetcher.h",
     "manifest_parser.cc",
     "manifest_parser.h",
     "manifest_uma_util.cc",
diff --git a/third_party/blink/renderer/modules/manifest/manifest_fetcher.cc b/third_party/blink/renderer/modules/manifest/manifest_fetcher.cc
deleted file mode 100644
index f6c0cf1..0000000
--- a/third_party/blink/renderer/modules/manifest/manifest_fetcher.cc
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/modules/manifest/manifest_fetcher.h"
-
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/loader/threadable_loader.h"
-#include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
-
-namespace blink {
-
-ManifestFetcher::ManifestFetcher(const KURL& url)
-    : url_(url), completed_(false) {}
-
-ManifestFetcher::~ManifestFetcher() {
-  if (!completed_)
-    Cancel();
-}
-
-void ManifestFetcher::Start(Document& document,
-                            bool use_credentials,
-                            WebManifestFetcher::Callback callback) {
-  callback_ = std::move(callback);
-
-  ResourceRequest request(url_);
-  request.SetRequestContext(mojom::RequestContextType::MANIFEST);
-  request.SetFetchRequestMode(network::mojom::FetchRequestMode::kCors);
-  // See https://w3c.github.io/manifest/. Use "include" when use_credentials is
-  // true, and "omit" otherwise.
-  request.SetFetchCredentialsMode(
-      use_credentials ? network::mojom::FetchCredentialsMode::kInclude
-                      : network::mojom::FetchCredentialsMode::kOmit);
-
-  ResourceLoaderOptions resource_loader_options;
-  resource_loader_options.data_buffering_policy = kDoNotBufferData;
-
-  loader_ = MakeGarbageCollected<ThreadableLoader>(document, this,
-                                                   resource_loader_options);
-  loader_->Start(request);
-}
-
-void ManifestFetcher::Cancel() {
-  if (!loader_)
-    return;
-
-  DCHECK(!completed_);
-
-  ThreadableLoader* loader = loader_.Release();
-  loader->Cancel();
-}
-
-void ManifestFetcher::DidReceiveResponse(uint64_t,
-                                         const ResourceResponse& response) {
-  response_ = response;
-}
-
-void ManifestFetcher::DidReceiveData(const char* data, unsigned length) {
-  if (!length)
-    return;
-
-  data_.Append(data, length);
-}
-
-void ManifestFetcher::DidFinishLoading(uint64_t) {
-  DCHECK(!completed_);
-  completed_ = true;
-
-  WrappedResourceResponse wrapped_response(response_);
-  std::move(callback_).Run(wrapped_response, data_.ToString());
-  data_.Clear();
-}
-
-void ManifestFetcher::DidFail(const ResourceError& error) {
-  if (!callback_)
-    return;
-
-  data_.Clear();
-
-  WrappedResourceResponse wrapped_response(response_);
-  std::move(callback_).Run(wrapped_response, String());
-}
-
-void ManifestFetcher::DidFailRedirectCheck() {
-  DidFail(ResourceError::Failure(NullURL()));
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/manifest/manifest_fetcher.h b/third_party/blink/renderer/modules/manifest/manifest_fetcher.h
deleted file mode 100644
index 9a73ed4..0000000
--- a/third_party/blink/renderer/modules/manifest/manifest_fetcher.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MANIFEST_MANIFEST_FETCHER_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_MANIFEST_MANIFEST_FETCHER_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "third_party/blink/public/web/web_manifest_fetcher.h"
-#include "third_party/blink/renderer/core/loader/threadable_loader.h"
-#include "third_party/blink/renderer/core/loader/threadable_loader_client.h"
-#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-#include "third_party/blink/renderer/platform/heap/member.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
-#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
-
-namespace blink {
-
-class Document;
-class KURL;
-
-// Helper class to download a Web Manifest. When an instance is created, the
-// caller need to call Start() and wait for the passed callback to be executed.
-// If the fetch fails, the callback will be called with two empty objects.
-class ManifestFetcher final : public GarbageCollectedFinalized<ManifestFetcher>,
-                              public ThreadableLoaderClient {
-  USING_GARBAGE_COLLECTED_MIXIN(ManifestFetcher);
-
- public:
-  explicit ManifestFetcher(const KURL& url);
-  ~ManifestFetcher() override;
-
-  void Start(Document& document,
-             bool use_credentials,
-             WebManifestFetcher::Callback callback);
-  void Cancel();
-
-  // ThreadableLoaderClient
-  void DidReceiveResponse(uint64_t, const ResourceResponse&) override;
-  void DidReceiveData(const char*, unsigned) override;
-  void DidFinishLoading(uint64_t) override;
-  void DidFail(const ResourceError&) override;
-  void DidFailRedirectCheck() override;
-
-  void Trace(Visitor* visitor) override { visitor->Trace(loader_); }
-
- private:
-  KURL url_;
-  bool completed_;
-  WebManifestFetcher::Callback callback_;
-  ResourceResponse response_;
-  StringBuilder data_;
-  Member<ThreadableLoader> loader_;
-
-  DISALLOW_COPY_AND_ASSIGN(ManifestFetcher);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MANIFEST_MANIFEST_FETCHER_H_
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
index 1449ea8..25d194906 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
@@ -4,16 +4,103 @@
 
 #include "third_party/blink/renderer/modules/webgpu/gpu_command_encoder.h"
 
+#include "third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h"
+#include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_buffer.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_buffer_copy_view.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_command_buffer.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_command_encoder_descriptor.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_compute_pass_encoder.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_extent_3d.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_origin_3d.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_render_pass_color_attachment_descriptor.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_render_pass_depth_stencil_attachment_descriptor.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_render_pass_descriptor.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_texture.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_texture_copy_view.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_texture_view.h"
 
 namespace blink {
 
+DawnRenderPassColorAttachmentDescriptor AsDawnType(
+    const GPURenderPassColorAttachmentDescriptor* webgpu_desc) {
+  DCHECK(webgpu_desc);
+
+  DawnRenderPassColorAttachmentDescriptor dawn_desc;
+  dawn_desc.attachment = webgpu_desc->attachment()->GetHandle();
+  dawn_desc.resolveTarget = webgpu_desc->resolveTarget()
+                                ? webgpu_desc->resolveTarget()->GetHandle()
+                                : nullptr;
+  dawn_desc.loadOp = AsDawnEnum<DawnLoadOp>(webgpu_desc->loadOp());
+  dawn_desc.storeOp = AsDawnEnum<DawnStoreOp>(webgpu_desc->storeOp());
+  dawn_desc.clearColor = AsDawnType(webgpu_desc->clearColor());
+
+  return dawn_desc;
+}
+
+namespace {
+
+DawnRenderPassDepthStencilAttachmentDescriptor AsDawnType(
+    const GPURenderPassDepthStencilAttachmentDescriptor* webgpu_desc) {
+  DCHECK(webgpu_desc);
+
+  DawnRenderPassDepthStencilAttachmentDescriptor dawn_desc;
+  dawn_desc.attachment = webgpu_desc->attachment()->GetHandle();
+  dawn_desc.depthLoadOp = AsDawnEnum<DawnLoadOp>(webgpu_desc->depthLoadOp());
+  dawn_desc.depthStoreOp = AsDawnEnum<DawnStoreOp>(webgpu_desc->depthStoreOp());
+  dawn_desc.clearDepth = webgpu_desc->clearDepth();
+  dawn_desc.stencilLoadOp =
+      AsDawnEnum<DawnLoadOp>(webgpu_desc->stencilLoadOp());
+  dawn_desc.stencilStoreOp =
+      AsDawnEnum<DawnStoreOp>(webgpu_desc->stencilStoreOp());
+  dawn_desc.clearStencil = webgpu_desc->clearStencil();
+
+  return dawn_desc;
+}
+
+DawnBufferCopyView AsDawnType(const GPUBufferCopyView* webgpu_view) {
+  DCHECK(webgpu_view);
+  DCHECK(webgpu_view->buffer());
+
+  DawnBufferCopyView dawn_view;
+  dawn_view.nextInChain = nullptr;
+  dawn_view.buffer = webgpu_view->buffer()->GetHandle();
+  dawn_view.offset = webgpu_view->offset();
+  dawn_view.rowPitch = webgpu_view->rowPitch();
+  dawn_view.imageHeight = webgpu_view->imageHeight();
+
+  return dawn_view;
+}
+
+DawnTextureCopyView AsDawnType(const GPUTextureCopyView* webgpu_view) {
+  DCHECK(webgpu_view);
+  DCHECK(webgpu_view->texture());
+
+  DawnTextureCopyView dawn_view;
+  dawn_view.nextInChain = nullptr;
+  dawn_view.texture = webgpu_view->texture()->GetHandle();
+  dawn_view.level = webgpu_view->mipLevel();
+  dawn_view.slice = webgpu_view->arrayLayer();
+  dawn_view.origin = AsDawnType(webgpu_view->origin());
+
+  return dawn_view;
+}
+
+}  // anonymous namespace
+
 // static
 GPUCommandEncoder* GPUCommandEncoder::Create(
     GPUDevice* device,
     const GPUCommandEncoderDescriptor* webgpu_desc) {
-  NOTIMPLEMENTED();
-  return nullptr;
+  DCHECK(device);
+  DCHECK(webgpu_desc);
+  ALLOW_UNUSED_LOCAL(webgpu_desc);
+
+  return MakeGarbageCollected<GPUCommandEncoder>(
+      device,
+      device->GetProcs().deviceCreateCommandEncoder(device->GetHandle()));
 }
 
 GPUCommandEncoder::GPUCommandEncoder(GPUDevice* device,
@@ -27,4 +114,102 @@
   GetProcs().commandEncoderRelease(GetHandle());
 }
 
+GPURenderPassEncoder* GPUCommandEncoder::beginRenderPass(
+    const GPURenderPassDescriptor* descriptor) {
+  DCHECK(descriptor);
+
+  uint32_t color_attachment_count =
+      static_cast<uint32_t>(descriptor->colorAttachments().size());
+
+  DawnRenderPassDescriptor dawn_desc;
+  dawn_desc.colorAttachmentCount = color_attachment_count;
+  dawn_desc.colorAttachments = nullptr;
+
+  using DescriptorPtr = DawnRenderPassColorAttachmentDescriptor*;
+  using DescriptorArray = DawnRenderPassColorAttachmentDescriptor[];
+  using DescriptorPtrArray = DescriptorPtr[];
+
+  std::unique_ptr<DescriptorArray> color_attachments;
+  std::unique_ptr<DescriptorPtrArray> color_attachment_ptrs;
+
+  if (color_attachment_count > 0) {
+    color_attachments = AsDawnType(descriptor->colorAttachments());
+
+    color_attachment_ptrs = std::unique_ptr<DescriptorPtrArray>(
+        new DescriptorPtr[color_attachment_count]);
+
+    for (uint32_t i = 0; i < color_attachment_count; ++i) {
+      color_attachment_ptrs[i] = color_attachments.get() + i;
+    }
+    dawn_desc.colorAttachments = color_attachment_ptrs.get();
+  }
+
+  DawnRenderPassDepthStencilAttachmentDescriptor depthStencilAttachment = {};
+  if (descriptor->hasDepthStencilAttachment()) {
+    depthStencilAttachment = AsDawnType(descriptor->depthStencilAttachment());
+    dawn_desc.depthStencilAttachment = &depthStencilAttachment;
+  } else {
+    dawn_desc.depthStencilAttachment = nullptr;
+  }
+
+  return GPURenderPassEncoder::Create(
+      device_,
+      GetProcs().commandEncoderBeginRenderPass(GetHandle(), &dawn_desc));
+}
+
+GPUComputePassEncoder* GPUCommandEncoder::beginComputePass() {
+  return GPUComputePassEncoder::Create(
+      device_, GetProcs().commandEncoderBeginComputePass(GetHandle()));
+}
+
+void GPUCommandEncoder::copyBufferToBuffer(GPUBuffer* src,
+                                           uint64_t src_offset,
+                                           GPUBuffer* dst,
+                                           uint64_t dst_offset,
+                                           uint64_t size) {
+  DCHECK(src);
+  DCHECK(dst);
+  GetProcs().commandEncoderCopyBufferToBuffer(GetHandle(), src->GetHandle(),
+                                              src_offset, dst->GetHandle(),
+                                              dst_offset, size);
+}
+
+void GPUCommandEncoder::copyBufferToTexture(GPUBufferCopyView* source,
+                                            GPUTextureCopyView* destination,
+                                            GPUExtent3D* copy_size) {
+  DawnBufferCopyView dawn_source = AsDawnType(source);
+  DawnTextureCopyView dawn_destination = AsDawnType(destination);
+  DawnExtent3D dawn_copy_size = AsDawnType(copy_size);
+
+  GetProcs().commandEncoderCopyBufferToTexture(
+      GetHandle(), &dawn_source, &dawn_destination, &dawn_copy_size);
+}
+
+void GPUCommandEncoder::copyTextureToBuffer(GPUTextureCopyView* source,
+                                            GPUBufferCopyView* destination,
+                                            GPUExtent3D* copy_size) {
+  DawnTextureCopyView dawn_source = AsDawnType(source);
+  DawnBufferCopyView dawn_destination = AsDawnType(destination);
+  DawnExtent3D dawn_copy_size = AsDawnType(copy_size);
+
+  GetProcs().commandEncoderCopyTextureToBuffer(
+      GetHandle(), &dawn_source, &dawn_destination, &dawn_copy_size);
+}
+
+void GPUCommandEncoder::copyTextureToTexture(GPUTextureCopyView* source,
+                                             GPUTextureCopyView* destination,
+                                             GPUExtent3D* copy_size) {
+  DawnTextureCopyView dawn_source = AsDawnType(source);
+  DawnTextureCopyView dawn_destination = AsDawnType(destination);
+  DawnExtent3D dawn_copy_size = AsDawnType(copy_size);
+
+  GetProcs().commandEncoderCopyTextureToTexture(
+      GetHandle(), &dawn_source, &dawn_destination, &dawn_copy_size);
+}
+
+GPUCommandBuffer* GPUCommandEncoder::finish() {
+  return GPUCommandBuffer::Create(device_,
+                                  GetProcs().commandEncoderFinish(GetHandle()));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.h b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.h
index 20286ca..adb50dc 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.h
@@ -9,7 +9,15 @@
 
 namespace blink {
 
+class GPUBuffer;
+class GPUBufferCopyView;
+class GPUCommandBuffer;
 class GPUCommandEncoderDescriptor;
+class GPUComputePassEncoder;
+class GPUExtent3D;
+class GPURenderPassDescriptor;
+class GPURenderPassEncoder;
+class GPUTextureCopyView;
 
 class GPUCommandEncoder : public DawnObject<DawnCommandEncoder> {
   DEFINE_WRAPPERTYPEINFO();
@@ -23,7 +31,24 @@
   ~GPUCommandEncoder() override;
 
   // gpu_command_encoder.idl
-  // TODO(crbug.com/877147): implement GPUCommandEncoder.
+  GPURenderPassEncoder* beginRenderPass(
+      const GPURenderPassDescriptor* descriptor);
+  GPUComputePassEncoder* beginComputePass();
+  void copyBufferToBuffer(GPUBuffer* src,
+                          uint64_t src_offset,
+                          GPUBuffer* dst,
+                          uint64_t dst_offset,
+                          uint64_t size);
+  void copyBufferToTexture(GPUBufferCopyView* source,
+                           GPUTextureCopyView* destination,
+                           GPUExtent3D* copy_size);
+  void copyTextureToBuffer(GPUTextureCopyView* source,
+                           GPUBufferCopyView* destination,
+                           GPUExtent3D* copy_size);
+  void copyTextureToTexture(GPUTextureCopyView* source,
+                            GPUTextureCopyView* destination,
+                            GPUExtent3D* copy_size);
+  GPUCommandBuffer* finish();
 
  private:
   DISALLOW_COPY_AND_ASSIGN(GPUCommandEncoder);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.idl b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.idl
index 4036658..83a719f 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.idl
@@ -7,4 +7,28 @@
 [
     RuntimeEnabled=WebGPU
 ] interface GPUCommandEncoder {
+    GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor);
+    GPUComputePassEncoder beginComputePass();
+
+    void copyBufferToBuffer(
+        GPUBuffer src, unsigned long long srcOffset,
+        GPUBuffer dst, unsigned long long dstOffset,
+        unsigned long long size);
+
+    void copyBufferToTexture(
+        GPUBufferCopyView source,
+        GPUTextureCopyView destination,
+        GPUExtent3D copySize);
+
+    void copyTextureToBuffer(
+        GPUTextureCopyView source,
+        GPUBufferCopyView destination,
+        GPUExtent3D copySize);
+
+    void copyTextureToTexture(
+        GPUTextureCopyView source,
+        GPUTextureCopyView destination,
+        GPUExtent3D copySize);
+
+    GPUCommandBuffer finish();
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
index 8d66a2d..8bfcba0 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
@@ -72,7 +72,7 @@
   bool want_depth_buffer = initializer->depth();
   bool want_stencil_buffer = initializer->stencil();
   bool want_alpha_channel = initializer->alpha();
-  bool ignore_depth_values = initializer->ignoreDepthValues();
+  bool want_ignore_depth_values = initializer->ignoreDepthValues();
 
   double framebuffer_scale = 1.0;
 
@@ -110,6 +110,16 @@
     return nullptr;
   }
 
+  // TODO: In the future this should be communicated by the drawing buffer and
+  // indicate whether the depth buffers are being supplied to the XR compositor.
+  bool compositor_supports_depth_values = false;
+
+  // The ignoreDepthValues attribute of XRWebGLLayer may only be set to false if
+  // the compositor is actually making use of the depth values and the user did
+  // not set ignoreDepthValues to true explicitly.
+  bool ignore_depth_values =
+      !compositor_supports_depth_values || want_ignore_depth_values;
+
   return MakeGarbageCollected<XRWebGLLayer>(
       session, webgl_context, std::move(drawing_buffer), framebuffer,
       framebuffer_scale, ignore_depth_values);
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 7a81c05..aeded0bc 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6260,3 +6260,8 @@
 
 # Sheriff 2019-04-09
 crbug.com/946335 [ Linux Mac ] fast/filesystem/file-writer-abort-depth.html [ Pass Crash ]
+
+# Sheriff 2019-04-10
+crbug.com/951638 [ Linux ] virtual/mouseevent_fractional/fast/events/mouse-right-coords-in-zoom-and-scroll-right.html [ Pass Failure ]
+crbug.com/951638 [ Mac10.13 Debug ] virtual/mouseevent_fractional/fast/events/mouse-right-coords-in-zoom-and-scroll-right.html [ Pass Failure ]
+crbug.com/951638 [ Win7 ] virtual/mouseevent_fractional/fast/events/mouse-right-coords-in-zoom-and-scroll-right.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 08af3f8..21d36d02 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -192,8 +192,7 @@
     "prefix": "android",
     "base": "fullscreen",
     "args": ["--enable-features=OverlayScrollbar", "--enable-threaded-compositing",
-             "--enable-fixed-position-compositing", "--enable-prefer-compositing-to-lcd-text",
-             "--enable-composited-scrolling-for-frames", "--enable-gesture-tap-highlight", "--enable-pinch",
+             "--enable-prefer-compositing-to-lcd-text",
              "--force-overlay-fullscreen-video", "--enable-overscroll-notifications",
              "--enable-viewport", "--disable-canvas-aa", "--disable-composited-antialiasing"]
   },
@@ -201,8 +200,7 @@
     "prefix": "android",
     "base": "rootscroller",
     "args": ["--enable-features=OverlayScrollbar", "--enable-threaded-compositing",
-             "--enable-fixed-position-compositing", "--enable-prefer-compositing-to-lcd-text",
-             "--enable-composited-scrolling-for-frames", "--enable-gesture-tap-highlight", "--enable-pinch",
+             "--enable-prefer-compositing-to-lcd-text",
              "--force-overlay-fullscreen-video", "--enable-overscroll-notifications",
              "--enable-viewport", "--disable-canvas-aa", "--disable-composited-antialiasing"]
   },
@@ -210,8 +208,7 @@
     "prefix": "android",
     "base": "url-bar",
     "args": ["--enable-features=OverlayScrollbar", "--enable-threaded-compositing",
-             "--enable-fixed-position-compositing", "--enable-prefer-compositing-to-lcd-text",
-             "--enable-composited-scrolling-for-frames", "--enable-gesture-tap-highlight", "--enable-pinch",
+             "--enable-prefer-compositing-to-lcd-text",
              "--force-overlay-fullscreen-video", "--enable-overscroll-notifications",
              "--enable-viewport", "--disable-canvas-aa", "--disable-composited-antialiasing"]
   },
@@ -219,8 +216,7 @@
     "prefix": "android",
     "base": "frame-size",
     "args": ["--enable-features=OverlayScrollbar", "--enable-threaded-compositing",
-             "--enable-fixed-position-compositing", "--enable-prefer-compositing-to-lcd-text",
-             "--enable-composited-scrolling-for-frames", "--enable-gesture-tap-highlight", "--enable-pinch",
+             "--enable-prefer-compositing-to-lcd-text",
              "--force-overlay-fullscreen-video", "--enable-overscroll-notifications",
              "--enable-viewport", "--disable-canvas-aa", "--disable-composited-antialiasing"]
   },
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index 6b5ac85d..a2b53c33 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -120403,6 +120403,11 @@
      {}
     ]
    ],
+   "content-security-policy/generic/eval-typecheck-callout-order.tentative.html.headers": [
+    [
+     {}
+    ]
+   ],
    "content-security-policy/generic/fail-0_1.js": [
     [
      {}
@@ -216060,6 +216065,12 @@
      {}
     ]
    ],
+   "content-security-policy/generic/eval-typecheck-callout-order.tentative.html": [
+    [
+     "/content-security-policy/generic/eval-typecheck-callout-order.tentative.html",
+     {}
+    ]
+   ],
    "content-security-policy/generic/filesystem-urls-do-not-match-self.sub.html": [
     [
      "/content-security-policy/generic/filesystem-urls-do-not-match-self.sub.html",
@@ -324796,6 +324807,14 @@
    "7810533e455968eea8eb0bdf4d8edf62e495f956",
    "testharness"
   ],
+  "content-security-policy/generic/eval-typecheck-callout-order.tentative.html": [
+   "7b3c12e396445ff72480a1e9c7cc77550f93f75c",
+   "testharness"
+  ],
+  "content-security-policy/generic/eval-typecheck-callout-order.tentative.html.headers": [
+   "85de8bd415def35ca45c0abf74590cdfa393d0f4",
+   "support"
+  ],
   "content-security-policy/generic/fail-0_1.js": [
    "5c580273dcfc94eff137a0ae65314bebc9b7b5c0",
    "support"
@@ -485685,7 +485704,7 @@
    "support"
   ],
   "webxr/idlharness.https.window-expected.txt": [
-   "93f6ab5a8da7b4a9740101195dae3fc8c158911a",
+   "20cbc03f1b67ec30b60be9da9729095a778956bc",
    "support"
   ],
   "webxr/idlharness.https.window.js": [
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/generic/eval-typecheck-callout-order.tentative.html b/third_party/blink/web_tests/external/wpt/content-security-policy/generic/eval-typecheck-callout-order.tentative.html
new file mode 100644
index 0000000..7b3c12e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/generic/eval-typecheck-callout-order.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+    <script src='/resources/testharness.js' nonce='abc'></script>
+    <script src='/resources/testharnessreport.js' nonce='abc'></script>
+    <title>Test for order of Type(evalInput) and host callout</title>
+</head>
+<body>
+    <div id='log'></div>
+
+    <script nonce='abc'>
+    test(function() {
+      assert_throws(new EvalError, function() {
+        eval("0");
+      }, "eval of a string should reach host callout");
+    }, "eval of a string should be checked by CSP");
+
+    test(function() {
+      let array = ["0"];
+      assert_equals(
+        eval(array),
+        array,
+        "eval is identity when applied to non-strings");
+    }, "eval of a non-string should not be checked by CSP");
+    </script>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/generic/eval-typecheck-callout-order.tentative.html.headers b/third_party/blink/web_tests/external/wpt/content-security-policy/generic/eval-typecheck-callout-order.tentative.html.headers
new file mode 100644
index 0000000..85de8bd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/generic/eval-typecheck-callout-order.tentative.html.headers
@@ -0,0 +1 @@
+Content-Security-Policy: script-src 'nonce-abc'
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-1-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-1-expected.txt
index ef62d260..f92d635 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-1-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-1-expected.txt
@@ -133,16 +133,6 @@
           "reason": "geometry"
         },
         {
-          "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
-          "rect": [14, 420, 356, 39],
-          "reason": "geometry"
-        },
-        {
-          "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
-          "rect": [14, 420, 356, 39],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'about once in a minute.'",
           "rect": [14, 460, 355, 59],
           "reason": "geometry"
@@ -158,8 +148,18 @@
           "reason": "geometry"
         },
         {
+          "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
           "object": "NGPhysicalTextFragment 'The players all played at once without waiting'",
-          "rect": [65, 400, 305, 19],
+          "rect": [65, 400, 304, 19],
           "reason": "geometry"
         },
         {
@@ -169,7 +169,7 @@
         },
         {
           "object": "NGPhysicalTextFragment 'the Queen'",
-          "rect": [302, 440, 68, 19],
+          "rect": [302, 440, 67, 19],
           "reason": "geometry"
         },
         {
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-10-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-10-expected.txt
index 8c72a3fc..68be8c9 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-10-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-10-expected.txt
@@ -103,16 +103,6 @@
           "reason": "geometry"
         },
         {
-          "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
-          "rect": [14, 420, 356, 39],
-          "reason": "geometry"
-        },
-        {
-          "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
-          "rect": [14, 420, 356, 39],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment ' was in a furious passion, and went stamping'",
           "rect": [14, 460, 355, 59],
           "reason": "appeared"
@@ -143,6 +133,16 @@
           "reason": "geometry"
         },
         {
+          "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
           "object": "NGPhysicalTextFragment 'The chief difficulty Alice found at first was in managing'",
           "rect": [14, 80, 354, 119],
           "reason": "geometry"
@@ -174,12 +174,7 @@
         },
         {
           "object": "NGPhysicalTextFragment 'The players all played at once without waiting'",
-          "rect": [65, 400, 305, 19],
-          "reason": "geometry"
-        },
-        {
-          "object": "NGPhysicalTextFragment 'the'",
-          "rect": [302, 440, 68, 19],
+          "rect": [65, 400, 304, 19],
           "reason": "geometry"
         },
         {
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-2-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-2-expected.txt
index dc60591..7b921bdf 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-2-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-2-expected.txt
@@ -193,26 +193,6 @@
           "reason": "geometry"
         },
         {
-          "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
-          "rect": [14, 421, 356, 39],
-          "reason": "geometry"
-        },
-        {
-          "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
-          "rect": [14, 421, 356, 39],
-          "reason": "geometry"
-        },
-        {
-          "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
-          "rect": [14, 420, 356, 39],
-          "reason": "geometry"
-        },
-        {
-          "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
-          "rect": [14, 420, 356, 39],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'about once in a minute.'",
           "rect": [14, 461, 355, 59],
           "reason": "geometry"
@@ -243,6 +223,26 @@
           "reason": "geometry"
         },
         {
+          "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
+          "rect": [14, 421, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
+          "rect": [14, 421, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
           "object": "NGPhysicalTextFragment 'The chief difficulty Alice found at first was in managing'",
           "rect": [14, 80, 354, 120],
           "reason": "geometry"
@@ -274,22 +274,22 @@
         },
         {
           "object": "NGPhysicalTextFragment 'The players all played at once without waiting'",
-          "rect": [65, 401, 305, 19],
+          "rect": [65, 401, 304, 19],
           "reason": "geometry"
         },
         {
           "object": "NGPhysicalTextFragment 'The players all played at once without waiting'",
-          "rect": [65, 400, 305, 19],
+          "rect": [65, 400, 304, 19],
           "reason": "geometry"
         },
         {
           "object": "NGPhysicalTextFragment 'the Queen'",
-          "rect": [302, 441, 68, 19],
+          "rect": [302, 441, 67, 19],
           "reason": "geometry"
         },
         {
           "object": "NGPhysicalTextFragment 'the Queen'",
-          "rect": [302, 440, 68, 19],
+          "rect": [302, 440, 67, 19],
           "reason": "geometry"
         },
         {
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-3-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-3-expected.txt
index 7fda67f..70aa3f33 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-3-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-3-expected.txt
@@ -164,37 +164,37 @@
         },
         {
           "object": "NGPhysicalTextFragment ' was in'",
-          "rect": [14, 440, 340, 79],
+          "rect": [14, 440, 339, 79],
           "reason": "appeared"
         },
         {
           "object": "NGPhysicalTextFragment 'a furious passion, and went stamping about, and'",
-          "rect": [14, 440, 340, 79],
+          "rect": [14, 440, 339, 79],
           "reason": "geometry"
         },
         {
           "object": "NGPhysicalTextFragment 'about once in a minute.'",
-          "rect": [14, 440, 340, 79],
+          "rect": [14, 440, 339, 79],
           "reason": "geometry"
         },
         {
           "object": "NGPhysicalTextFragment 'shouting \u2018Off with his head!\u2019 or \u2018Off with her head!\u2019'",
-          "rect": [14, 440, 340, 79],
+          "rect": [14, 440, 339, 79],
           "reason": "geometry"
         },
         {
           "object": "NGPhysicalTextFragment 'The players all played at once without waiting '",
-          "rect": [65, 400, 305, 19],
+          "rect": [65, 400, 304, 19],
           "reason": "geometry"
         },
         {
           "object": "NGPhysicalTextFragment 'the Queen'",
-          "rect": [302, 440, 68, 19],
+          "rect": [302, 440, 67, 19],
           "reason": "geometry"
         },
         {
           "object": "NGPhysicalTextFragment 'the Queen'",
-          "rect": [242, 440, 67, 19],
+          "rect": [242, 440, 66, 19],
           "reason": "geometry"
         },
         {
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-4-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-4-expected.txt
index ef82eb1..c3afda5 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-4-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-4-expected.txt
@@ -119,12 +119,12 @@
         },
         {
           "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
-          "rect": [14, 420, 356, 39],
+          "rect": [14, 420, 355, 39],
           "reason": "geometry"
         },
         {
           "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
-          "rect": [14, 420, 356, 39],
+          "rect": [14, 420, 355, 39],
           "reason": "geometry"
         },
         {
@@ -159,12 +159,12 @@
         },
         {
           "object": "NGPhysicalTextFragment 'The players all played at once without waiting'",
-          "rect": [65, 400, 305, 19],
+          "rect": [65, 400, 304, 19],
           "reason": "geometry"
         },
         {
           "object": "NGPhysicalTextFragment 'the Queen'",
-          "rect": [302, 440, 68, 19],
+          "rect": [302, 440, 67, 19],
           "reason": "geometry"
         },
         {
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-5-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-5-expected.txt
index 1d6190c6..420fd6fc 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-5-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-5-expected.txt
@@ -103,26 +103,6 @@
           "reason": "geometry"
         },
         {
-          "object": "NGPhysicalTextFragment ' was in a'",
-          "rect": [14, 440, 356, 79],
-          "reason": "appeared"
-        },
-        {
-          "object": "NGPhysicalTextFragment 'furious passion, and went stamping about, and shouting'",
-          "rect": [14, 440, 356, 79],
-          "reason": "geometry"
-        },
-        {
-          "object": "NGPhysicalTextFragment 'in a minute.'",
-          "rect": [14, 440, 356, 79],
-          "reason": "geometry"
-        },
-        {
-          "object": "NGPhysicalTextFragment '\u2018Off with his head!\u2019 or \u2018Off with her head!\u2019 about once'",
-          "rect": [14, 440, 356, 79],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'for'",
           "rect": [14, 400, 356, 59],
           "reason": "appeared"
@@ -138,6 +118,26 @@
           "reason": "geometry"
         },
         {
+          "object": "NGPhysicalTextFragment ' was in a'",
+          "rect": [14, 440, 355, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'furious passion, and went stamping about, and shouting'",
+          "rect": [14, 440, 355, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'in a minute.'",
+          "rect": [14, 440, 355, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment '\u2018Off with his head!\u2019 or \u2018Off with her head!\u2019 about once'",
+          "rect": [14, 440, 355, 79],
+          "reason": "geometry"
+        },
+        {
           "object": "NGPhysicalTextFragment 'The chief difficulty Alice found at first was in managing'",
           "rect": [14, 80, 354, 119],
           "reason": "geometry"
@@ -169,7 +169,7 @@
         },
         {
           "object": "NGPhysicalTextFragment 'The players all played at once without waiting '",
-          "rect": [65, 400, 305, 19],
+          "rect": [65, 400, 304, 19],
           "reason": "geometry"
         },
         {
@@ -179,7 +179,7 @@
         },
         {
           "object": "NGPhysicalTextFragment 'the Queen'",
-          "rect": [302, 440, 68, 19],
+          "rect": [302, 440, 67, 19],
           "reason": "geometry"
         },
         {
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-6-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-6-expected.txt
index 0b777ca4..bddd578 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-6-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-6-expected.txt
@@ -103,16 +103,6 @@
           "reason": "geometry"
         },
         {
-          "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
-          "rect": [14, 420, 356, 39],
-          "reason": "geometry"
-        },
-        {
-          "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
-          "rect": [14, 420, 356, 39],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'about once in a minute.'",
           "rect": [14, 460, 355, 59],
           "reason": "geometry"
@@ -128,6 +118,16 @@
           "reason": "geometry"
         },
         {
+          "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
           "object": "NGPhysicalTextFragment 'The chief difficulty Alice found at first was in managing'",
           "rect": [14, 80, 354, 119],
           "reason": "geometry"
@@ -159,12 +159,12 @@
         },
         {
           "object": "NGPhysicalTextFragment 'The players all played at once without waiting'",
-          "rect": [65, 400, 305, 19],
+          "rect": [65, 400, 304, 19],
           "reason": "geometry"
         },
         {
           "object": "NGPhysicalTextFragment 'the Queen'",
-          "rect": [302, 440, 68, 19],
+          "rect": [302, 440, 67, 19],
           "reason": "geometry"
         },
         {
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-7-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-7-expected.txt
index 7af269a280..82d45e15 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-7-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-7-expected.txt
@@ -103,16 +103,6 @@
           "reason": "geometry"
         },
         {
-          "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
-          "rect": [14, 420, 356, 39],
-          "reason": "geometry"
-        },
-        {
-          "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
-          "rect": [14, 420, 356, 39],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'about once in a minute.'",
           "rect": [14, 460, 355, 59],
           "reason": "geometry"
@@ -128,6 +118,16 @@
           "reason": "geometry"
         },
         {
+          "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
           "object": "NGPhysicalTextFragment 'The chief difficulty Alice found at first was in managing'",
           "rect": [14, 80, 354, 119],
           "reason": "geometry"
@@ -159,12 +159,12 @@
         },
         {
           "object": "NGPhysicalTextFragment 'The players all played at once without waiting'",
-          "rect": [65, 400, 305, 19],
+          "rect": [65, 400, 304, 19],
           "reason": "geometry"
         },
         {
           "object": "NGPhysicalTextFragment 'the Queen'",
-          "rect": [298, 440, 72, 19],
+          "rect": [298, 440, 71, 19],
           "reason": "style change"
         },
         {
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-8-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-8-expected.txt
index c77a74b..6a915e0 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-8-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-8-expected.txt
@@ -129,16 +129,6 @@
         },
         {
           "object": "NGPhysicalTextFragment 'and in a very short time '",
-          "rect": [14, 420, 356, 39],
-          "reason": "geometry"
-        },
-        {
-          "object": "NGPhysicalTextFragment 'quarrelling all the while, and fighting for the hedgehogs;'",
-          "rect": [14, 420, 356, 39],
-          "reason": "geometry"
-        },
-        {
-          "object": "NGPhysicalTextFragment 'and in a very short time '",
           "rect": [14, 400, 355, 59],
           "reason": "geometry"
         },
@@ -184,7 +174,7 @@
         },
         {
           "object": "NGPhysicalTextFragment 'The players all played at once without waiting '",
-          "rect": [65, 400, 305, 19],
+          "rect": [65, 400, 304, 19],
           "reason": "geometry"
         },
         {
@@ -194,12 +184,12 @@
         },
         {
           "object": "NGPhysicalTextFragment 'the Queen'",
-          "rect": [184, 440, 70, 19],
+          "rect": [184, 440, 69, 19],
           "reason": "geometry"
         },
         {
           "object": "NGPhysicalTextFragment 'the Queen'",
-          "rect": [302, 440, 68, 19],
+          "rect": [302, 440, 67, 19],
           "reason": "geometry"
         },
         {
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-9-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-9-expected.txt
index 55f60db..4baf7cc6 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-9-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-9-expected.txt
@@ -108,16 +108,6 @@
           "reason": "disappeared"
         },
         {
-          "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
-          "rect": [14, 420, 356, 39],
-          "reason": "geometry"
-        },
-        {
-          "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
-          "rect": [14, 420, 356, 39],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment ' was in a furious passion, and went stamping'",
           "rect": [14, 460, 355, 59],
           "reason": "appeared"
@@ -148,6 +138,16 @@
           "reason": "geometry"
         },
         {
+          "object": "NGPhysicalTextFragment 'for the hedgehogs; and in a very short time '",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'for turns, quarrelling all the while, and fighting'",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
           "object": "NGPhysicalTextFragment 'The chief difficulty Alice found at first was in managing'",
           "rect": [14, 80, 354, 119],
           "reason": "geometry"
@@ -179,12 +179,7 @@
         },
         {
           "object": "NGPhysicalTextFragment 'The players all played at once without waiting'",
-          "rect": [65, 400, 305, 19],
-          "reason": "geometry"
-        },
-        {
-          "object": "NGPhysicalTextFragment 'the'",
-          "rect": [302, 440, 68, 19],
+          "rect": [65, 400, 304, 19],
           "reason": "geometry"
         },
         {
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-iframe-idb-expected.txt b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-iframe-idb-expected.txt
new file mode 100644
index 0000000..8cefe027
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-iframe-idb-expected.txt
@@ -0,0 +1,79 @@
+Tests Application Panel's handling of storages in iframes.
+
+Initial tree...
+
+Application
+ Manifest
+ Service Workers
+ Clear storage
+Storage
+ Local Storage
+  http://127.0.0.1:8000
+  http://localhost:8000
+ Session Storage
+  http://127.0.0.1:8000
+  http://localhost:8000
+ IndexedDB
+  Database-iframe - http://localhost:8000
+   Database-iframe
+  Database-main-frame - http://127.0.0.1:8000
+ Web SQL
+ Cookies
+  http://127.0.0.1:8000
+  http://localhost:8000
+Cache
+ Cache Storage
+ Application Cache
+Frames
+ top
+
+Remove iframe from page...
+
+Application
+ Manifest
+ Service Workers
+ Clear storage
+Storage
+ Local Storage
+  http://127.0.0.1:8000
+ Session Storage
+  http://127.0.0.1:8000
+ IndexedDB
+  Database-main-frame - http://127.0.0.1:8000
+ Web SQL
+ Cookies
+  http://127.0.0.1:8000
+  http://localhost:8000
+Cache
+ Cache Storage
+ Application Cache
+Frames
+ top
+
+Add iframe to page again...
+
+Application
+ Manifest
+ Service Workers
+ Clear storage
+Storage
+ Local Storage
+  http://127.0.0.1:8000
+  http://localhost:8000
+ Session Storage
+  http://127.0.0.1:8000
+  http://localhost:8000
+ IndexedDB
+  Database-main-frame - http://127.0.0.1:8000
+  Database-iframe - http://localhost:8000
+   Database-iframe
+ Web SQL
+ Cookies
+  http://127.0.0.1:8000
+  http://localhost:8000
+Cache
+ Cache Storage
+ Application Cache
+Frames
+ top
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-iframe-idb.js b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-iframe-idb.js
new file mode 100644
index 0000000..d03bcaf
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-iframe-idb.js
@@ -0,0 +1,64 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  TestRunner.addResult(`Tests Application Panel's handling of storages in iframes.\n`);
+  await TestRunner.loadModule('application_test_runner');
+  // Note: every test that uses a storage API must manually clean-up state from previous tests.
+  await ApplicationTestRunner.resetState();
+
+  await TestRunner.loadModule('console_test_runner');
+  await TestRunner.showPanel('resources');
+
+  function createIndexedDBInMainFrame(callback) {
+    var mainFrameId = TestRunner.resourceTreeModel.mainFrame.id;
+    var model = TestRunner.mainTarget.model(Resources.IndexedDBModel);
+    ApplicationTestRunner.createDatabase(mainFrameId, 'Database-main-frame', () => {
+      var event = model.addEventListener(Resources.IndexedDBModel.Events.DatabaseAdded, () => {
+        Common.EventTarget.removeEventListeners([event]);
+        callback();
+      });
+      model.refreshDatabaseNames();
+    });
+  }
+
+  function dumpTree(node, level) {
+    for (var child of node.children()) {
+      TestRunner.addResult(' '.repeat(level) + child.listItemElement.textContent);
+      dumpTree(child, level + 1);
+    }
+  }
+
+  // create IndexedDB in iframe
+  await TestRunner.addIframe('http://localhost:8000/devtools/application-panel/resources/indexeddb-in-iframe.html', {'id': 'indexeddb_page'});
+  await TestRunner.addSnifferPromise(UI.panels.resources._sidebar.indexedDBListTreeElement, '_indexedDBLoadedForTest');
+
+  // create IndexedDB in main frame
+  await new Promise(createIndexedDBInMainFrame);
+  await TestRunner.addSnifferPromise(UI.panels.resources._sidebar.indexedDBListTreeElement, '_indexedDBLoadedForTest');
+
+  const view = UI.panels.resources;
+
+  TestRunner.addResult('Initial tree...\n');
+
+  dumpTree(view._sidebar._sidebarTree.rootElement(), 0);
+
+  TestRunner.addResult('\nRemove iframe from page...\n');
+  await TestRunner.evaluateInPageAsync(`
+    (function(){
+      let iframe = document.getElementById('indexeddb_page');
+      iframe.parentNode.removeChild(iframe);
+    })();
+  `);
+
+  dumpTree(view._sidebar._sidebarTree.rootElement(), 0);
+
+  TestRunner.addResult('\nAdd iframe to page again...\n');
+  await TestRunner.addIframe('http://localhost:8000/devtools/application-panel/resources/indexeddb-in-iframe.html', {'id': 'indexeddb_page'});
+  await TestRunner.addSnifferPromise(UI.panels.resources._sidebar.indexedDBListTreeElement, '_indexedDBLoadedForTest');
+
+  dumpTree(view._sidebar._sidebarTree.rootElement(), 0);
+
+  TestRunner.completeTest();
+})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-selection-on-reload.js b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-selection-on-reload.js
index 2ccb2c8..c120296a 100644
--- a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-selection-on-reload.js
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-selection-on-reload.js
@@ -43,11 +43,6 @@
     TestRunner.addResult('Visible view is a cookie view: ' + (view.visibleView instanceof Resources.CookieItemsView));
   }
 
-  function fireFrameNavigated() {
-    var rtm = TestRunner.resourceTreeModel;
-    rtm.dispatchEventToListeners(SDK.ResourceTreeModel.Events.FrameNavigated, rtm.mainFrame);
-  }
-
   await new Promise(createIndexedDB);
   await ApplicationTestRunner.createWebSQLDatabase('database-for-test');
   UI.viewManager.showView('resources');
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources/indexeddb-in-iframe.html b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources/indexeddb-in-iframe.html
new file mode 100644
index 0000000..5d94f53
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources/indexeddb-in-iframe.html
@@ -0,0 +1,22 @@
+<script>
+  function makeDB() {
+    return new Promise((resolve) => {
+      indexedDB.open('Database-iframe').onupgradeneeded = event => {
+          const db = event.target.result;
+          const objectStore = db.createObjectStore('Database-iframe');
+          objectStore.transaction.oncomplete = _ => {
+            indexedDB.open('Database-iframe').onsuccess = event => event.target.result
+              .transaction('Database-iframe', 'readwrite')
+              .objectStore('Database-iframe')
+              .add(940123, 'key');
+          }
+          resolve();
+      }
+    });
+  }
+
+  window.addEventListener("load", async function(e) {
+    await makeDB();
+  }, false);
+
+</script>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-expose-devtools-protocol-errors-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-expose-devtools-protocol-errors-expected.txt
new file mode 100644
index 0000000..641557cb
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-expose-devtools-protocol-errors-expected.txt
@@ -0,0 +1,5 @@
+Verify that errors in the protocol handlers are dispatched in the page.
+ReferenceError: c is not defined
+    at Object.window.cdp.onmessage (<anonymous>:5:37)
+    at <anonymous>:1:18
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-expose-devtools-protocol-errors.js b/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-expose-devtools-protocol-errors.js
new file mode 100644
index 0000000..912b127
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-expose-devtools-protocol-errors.js
@@ -0,0 +1,30 @@
+(async function(testRunner) {
+  // 1. Create a page, connect to it and use browser connection to grant it a remote debugging capability.
+  const {page, session, dp} = await testRunner.startBlank(
+      'Verify that errors in the protocol handlers are dispatched in the page.');
+  await testRunner.browserP().Target.exposeDevToolsProtocol({targetId: page._targetId, bindingName: 'cdp'});
+
+  // 2. To avoid implementing a protocol client in test, use target domain to validate protocol binding.
+  await dp.Target.setDiscoverTargets({discover: true});
+
+  dp.Runtime.enable();
+  dp.Runtime.onConsoleAPICalled(result => {
+    testRunner.log(result.params.args[0].description);
+    testRunner.completeTest();
+  });
+
+  session.evaluate(() => {
+    // Redirect unhandled errors into console.
+    window.onerror = msg => console.log('Unhandled error: ' + msg);
+    // Inject unhandled error.
+    window.cdp.onmessage = msg => a = c;
+    window.cdp.send(JSON.stringify({
+      id: 0,
+      method: 'Target.setDiscoverTargets',
+      params: {
+        discover: true
+      }
+    }));
+  });
+})
+
diff --git a/third_party/closure_compiler/externs/automation.js b/third_party/closure_compiler/externs/automation.js
index d334e07db..1716b22 100644
--- a/third_party/closure_compiler/externs/automation.js
+++ b/third_party/closure_compiler/externs/automation.js
@@ -942,6 +942,48 @@
 chrome.automation.AutomationNode.prototype.focusAffinity;
 
 /**
+ * The node at the start of the selection, if any.
+ * @type {(!chrome.automation.AutomationNode|undefined)}
+ * @see https://developer.chrome.com/extensions/automation#type-selectionStartObject
+ */
+chrome.automation.AutomationNode.prototype.selectionStartObject;
+
+/**
+ * The offset at the start of the selection, if any.
+ * @type {(number|undefined)}
+ * @see https://developer.chrome.com/extensions/automation#type-selectionStartOffset
+ */
+chrome.automation.AutomationNode.prototype.selectionStartOffset;
+
+/**
+ * The affinity at the start of the selection, if any.
+ * @type {(string|undefined)}
+ * @see https://developer.chrome.com/extensions/automation#type-selectionStartAffinity
+ */
+chrome.automation.AutomationNode.prototype.selectionStartAffinity;
+
+/**
+ * The node at the end of the selection, if any.
+ * @type {(!chrome.automation.AutomationNode|undefined)}
+ * @see https://developer.chrome.com/extensions/automation#type-selectionEndObject
+ */
+chrome.automation.AutomationNode.prototype.selectionEndObject;
+
+/**
+ * The offset at the end of the selection, if any.
+ * @type {(number|undefined)}
+ * @see https://developer.chrome.com/extensions/automation#type-selectionEndOffset
+ */
+chrome.automation.AutomationNode.prototype.selectionEndOffset;
+
+/**
+ * The affinity at the end of the selection, if any.
+ * @type {(string|undefined)}
+ * @see https://developer.chrome.com/extensions/automation#type-selectionEndAffinity
+ */
+chrome.automation.AutomationNode.prototype.selectionEndAffinity;
+
+/**
  * The current value for this range.
  * @type {(number|undefined)}
  * @see https://developer.chrome.com/extensions/automation#type-valueForRange
diff --git a/third_party/fuchsia-sdk/gen_build_defs.py b/third_party/fuchsia-sdk/gen_build_defs.py
index ec501c7..eeb3a4c9 100755
--- a/third_party/fuchsia-sdk/gen_build_defs.py
+++ b/third_party/fuchsia-sdk/gen_build_defs.py
@@ -187,12 +187,10 @@
   with open(build_output_path, 'w') as buildfile:
     buildfile.write(_GENERATED_PREAMBLE)
 
-    for next_part in toplevel_meta['parts']:
-      parsed = json.load(open(os.path.join(sdk_base_dir, next_part)))
-      if 'type' not in parsed:
-        raise Exception("Couldn't find 'type' node in %s." % next_part)
+    for part in toplevel_meta['new_parts']:
+      parsed = json.load(open(os.path.join(sdk_base_dir, part['meta'])))
 
-      convert_function = _CONVERSION_FUNCTION_MAP.get(parsed['type'])
+      convert_function = _CONVERSION_FUNCTION_MAP.get(part['type'])
       if convert_function is None:
         raise Exception('Unexpected SDK artifact type %s in %s.' %
                         (parsed['type'], next_part))
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium
index 3496570..5ce3406 100644
--- a/third_party/metrics_proto/README.chromium
+++ b/third_party/metrics_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Metrics Protos
 Short Name: metrics_proto
 URL: This is the canonical public repository
-Version: 240367580
-Date: 2019/03/26 UTC
+Version: 241818494
+Date: 2019/04/03 UTC
 License: BSD
 Security Critical: Yes
 
diff --git a/third_party/metrics_proto/system_profile.proto b/third_party/metrics_proto/system_profile.proto
index c876225..43f9ce58 100644
--- a/third_party/metrics_proto/system_profile.proto
+++ b/third_party/metrics_proto/system_profile.proto
@@ -1019,7 +1019,8 @@
   // Next Tag: 5
   message LinkedAndroidPhoneData {
     // The pii-free model name of the phone used for Better Together with this
-    // device. Will not be set if Better Together is not set up.
+    // device. Will not be set if Better Together is not set up. Hashed using
+    // variations::HashName() to produce a 32-bit SHA1 hash.
     optional fixed32 phone_model_name_hash = 1;
 
     // True if SmartLock is enabled on this Chromebook.
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 10aa162..87cbc3e8 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -160,7 +160,8 @@
       'ToTWinCFI64': 'clang_tot_win_cfi_full_cfi_diag_thin_lto_release_static_dcheck_always_on',
       'ToTWinLibcxx64': 'clang_tot_official_optimize_minimal_symbols_static_release_libcxx',
       'ToTWinThinLTO64': 'clang_tot_win_official_full_symbols_thin_lto_static',
-      'ToTiOS': 'ios_error',
+      'ToTiOS': 'clang_tot_ios_simulator',
+      'ToTiOSDevice': 'clang_tot_ios_device',
       'UBSanVptr Linux': 'ubsan_vptr_release_bot',
     },
 
@@ -235,6 +236,15 @@
       'mac-hermetic-upgrade-rel': 'release_bot',
       'Chromium Mac 10.13': 'release_bot',
 
+      'ios-device-goma-canary-clobber': 'ios_device',
+      'ios-device-goma-latest-clobber': 'ios_device',
+      'ios-simulator': 'ios_simulator',
+      'ios-simulator-cronet': 'ios_simulator_cronet_no_tss_preload',
+      'ios12-beta-simulator': 'ios_simulator',
+      'ios12-sdk-device': 'ios_device',
+      'ios12-sdk-simulator': 'ios_simulator',
+      'ios13-beta-simulator': 'ios_simulator',
+
       'Mac Builder (dbg) Goma Canary (clobber)': 'debug_bot',
       'Mac Builder (dbg) Goma Canary': 'debug_bot',
       'Mac Builder (dbg) Goma Latest Client (clobber)': 'debug_bot',
@@ -265,11 +275,7 @@
       'fuchsia-fyi-x64-dbg': 'debug_bot_fuchsia',
       'fuchsia-fyi-x64-rel': 'release_bot_fuchsia',
 
-      'ios-device-goma-canary-clobber': 'ios_error',
-      'ios-device-goma-latest-clobber': 'ios_error',
-
       'ios-simulator-code-coverage': 'clang_code_coverage_ios',
-      'ios-simulator': 'ios_error',
       'Jumbo Linux x64': 'jumbo_large_chunks_release_bot_minimal_symbols',
       'Jumbo Mac': 'jumbo_release_bot_minimal_symbols',
       'Jumbo Win x64': 'jumbo_release_bot_minimal_symbols',
@@ -462,12 +468,15 @@
       'Mac Builder': 'gpu_tests_release_bot_minimal_symbols',
       'Mac Builder (dbg)': 'gpu_tests_debug_bot',
       'mac-jumbo-rel': 'jumbo_large_chunks_release_bot_minimal_symbols',
-      'ios-device': 'ios_error',
-      'ios-device-xcode-clang': 'ios_error',
-      'ios-simulator': 'ios_error',
-      'ios-simulator-cronet': 'ios_error',
-      'ios-simulator-full-configs': 'ios_error',
-      'ios-simulator-xcode-clang': 'ios_error',
+      'ios-device': 'ios_device_no_symbols',
+      'ios-device-xcode-clang': 'ios_device_no_symbols_xcode_clang',
+      'ios-simulator': 'ios_simulator',
+      'ios-simulator-cronet': 'ios_simulator_cronet',
+      'ios-simulator-full-configs': 'ios_simulator',
+      'ios-simulator-xcode-clang': 'ios_simulator_no_symbols_xcode_clang',
+      'ios-slimnav': 'ios_simulator',
+      'ios-uirefresh-simulator': 'ios_simulator',
+      'ios12-sdk-simulator': 'ios_simulator',
       'WebKit Mac10.13 (retina)': 'release_bot',
     },
 
@@ -569,6 +578,8 @@
       'WebRTC Chromium FYI Android Builder': 'android_release_bot_minimal_symbols',
       'WebRTC Chromium FYI Android Builder (dbg)': 'android_debug_static_bot',
       'WebRTC Chromium FYI Android Builder ARM64 (dbg)': 'android_debug_static_bot_arm64',
+      'WebRTC Chromium FYI ios-device': 'ios_device',
+      'WebRTC Chromium FYI ios-simulator': 'ios_simulator',
       'WebRTC Chromium FYI Linux Builder': 'gpu_tests_release_bot',
       'WebRTC Chromium FYI Linux Builder (RBE)': 'gpu_tests_release_bot',
       'WebRTC Chromium FYI Linux Builder (dbg)': 'debug_bot',
@@ -707,6 +718,7 @@
 
     'tryserver.chromium.dawn': {
       'dawn-linux-x64-deps-rel': 'dawn_tests_release_trybot',
+      'dawn-mac-x64-deps-rel': 'dawn_tests_release_trybot',
       'dawn-win10-x86-deps-rel': 'dawn_tests_release_trybot_x86',
       'dawn-win10-x64-deps-rel': 'dawn_tests_release_trybot',
       'linux-dawn-rel': 'gpu_fyi_tests_release_trybot',
@@ -785,12 +797,15 @@
     },
 
     'tryserver.chromium.mac': {
-      'ios-device': 'ios_error',
-      'ios-device-xcode-clang': 'ios_error',
-      'ios-simulator': 'ios_error',
-      'ios-simulator-full-configs': 'ios_error',
-      'ios-simulator-cronet': 'ios_error',
-      'ios-simulator-xcode-clang': 'ios_error',
+      'ios-device': 'ios_device_no_symbols',
+      'ios-device-xcode-clang': 'ios_device_no_symbols_xcode_clang',
+      'ios-simulator': 'ios_simulator',
+      'ios-simulator-cronet': 'ios_simulator_cronet',
+      'ios-simulator-full-configs': 'ios_simulator',
+      'ios-simulator-xcode-clang': 'ios_simulator_no_symbols_xcode_clang',
+      'ios-slimnav': 'ios_simulator',
+      'ios-uirefresh-simulator': 'ios_simulator',
+      'ios12-sdk-simulator': 'ios_simulator',
       'mac-jumbo-rel': 'jumbo_large_chunks_release_bot_minimal_symbols',
       'mac_chromium_10.10': 'gpu_tests_release_trybot',
       'mac_chromium_10.12_rel_ng': 'gpu_tests_release_trybot',
@@ -1257,6 +1272,11 @@
       'dcheck_always_on', 'x86', 'win_linker_timing',
     ],
 
+    # TODO(thakis): Can we use clang_tot here instead of llvm_force_head?
+    # https://crbug.com/951182.
+    'clang_tot_ios_device': ['ios_device', 'ios_disable_code_signing', 'static', 'llvm_force_head'],
+    'clang_tot_ios_simulator': ['ios_simulator', 'ios_disable_code_signing', 'static', 'release', 'llvm_force_head'],
+
     'clang_tot_ubsan_no_recover_hack_static_release': [
       'clang_tot', 'ubsan_no_recover_hack', 'static', 'release',
     ],
@@ -1596,12 +1616,15 @@
       'gn_linux_upload', 'official', 'goma',
     ],
 
-    # The 'ios_error' config is just used for auditing. iOS bots
-    # actually use the ios recipes, not the chromium recipe, and look
-    # up their GN arguments via files checked in under //ios/build/bots.
-    # It is an error to actually use one of these configs to generate the
-    # build files.
-    'ios_error': [ 'error'],
+    'ios_device': ['ios_device', 'release_bot', 'ios_disable_code_signing'],
+    # TODO(justincohen): Why do we build with no_symbols on main waterfall?
+    # Shouldn't we just build with symbols everywhere? https://crbug.com/951182.
+    'ios_device_no_symbols': ['ios_device', 'release_bot', 'ios_disable_code_signing', 'no_symbols'],
+    'ios_device_no_symbols_xcode_clang': ['ios_device', 'release_bot', 'ios_disable_code_signing', 'no_symbols', 'xcode_clang'],
+    'ios_simulator': ['ios_simulator', 'debug', 'minimal_symbols', 'goma'],
+    'ios_simulator_cronet': ['ios_simulator', 'debug', 'minimal_symbols', 'goma', 'ios_cronet_mixins'],
+    'ios_simulator_cronet_no_tss_preload': ['ios_simulator', 'debug', 'minimal_symbols', 'goma', 'ios_cronet_mixins_no_tss_preload'],
+    'ios_simulator_no_symbols_xcode_clang': ['ios_simulator', 'debug', 'ios_disable_code_signing', 'goma', 'no_symbols', 'xcode_clang'],
 
     'jumbo_release_bot_minimal_symbols': [
       'jumbo', 'release_bot', 'compile_only',
@@ -2176,6 +2199,29 @@
       'gn_args': 'target_os="ios"',
     },
 
+    'ios_disable_code_signing': {
+      'gn_args': 'ios_enable_code_signing=false',
+    },
+
+    'ios_device': {
+      'mixins': ['ios'],
+      'gn_args': 'target_cpu="arm64"',
+    },
+
+    'ios_simulator': {
+      'mixins': ['ios'],
+      'gn_args': 'target_cpu="x64"',
+    },
+
+    'ios_cronet_mixins': {
+      'gn_args': 'additional_target_cpus=["x86"] disable_brotli_filter=false disable_file_support=true disable_ftp_support=true enable_websockets=false ios_deployment_target="9.0" use_crash_key_stubs=true is_cronet_build=true use_platform_icu_alternatives=true',
+    },
+
+    'ios_cronet_mixins_no_tss_preload': {
+      'mixins': ['ios_cronet_mixins'],
+      'gn_args': 'include_transport_security_state_preload_list=false',
+    },
+
     'java_coverage': {
       'gn_args': 'emma_coverage=true emma_filter="org.chromium.*"',
     },
@@ -2194,6 +2240,10 @@
 
     'libfuzzer': { 'gn_args': 'use_libfuzzer=true' },
 
+    'llvm_force_head': {
+      'gn_args': 'llvm_force_head_revision=true',
+    },
+
     'lsan': {
       'gn_args': 'is_lsan=true',
     },
@@ -2410,6 +2460,10 @@
       'gn_args': 'target_os="win"',
     },
 
+    'xcode_clang': {
+      'gn_args': 'use_xcode_clang=true',
+    },
+
     'x64': {
       'gn_args': 'target_cpu="x64"',
     },
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a20d7d3..3bffbe7 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -28833,6 +28833,7 @@
   <int value="89" label="INLINE_UPDATE_READY_INFOBAR_ANDROID"/>
   <int value="90" label="INLINE_UPDATE_FAILED_INFOBAR_ANDROID"/>
   <int value="91" label="FLASH_DEPRECATION_INFOBAR_DELEGATE"/>
+  <int value="92" label="SEND_TAB_TO_SELF_INFOBAR_DELEGATE"/>
 </enum>
 
 <enum name="InfoBarResponse">
@@ -32034,6 +32035,7 @@
       label="AutofillCreditCardLastUsedDateDisplay:enabled"/>
   <int value="-2080504230" label="TabHoverCards:disabled"/>
   <int value="-2077268643" label="disable-device-enumeration"/>
+  <int value="-2076250656" label="MojoIMF:enabled"/>
   <int value="-2075870708" label="MediaRemotingEncrypted:disabled"/>
   <int value="-2075807193" label="enable-webusb-on-any-origin"/>
   <int value="-2075725205" label="disable-new-zip-unpacker"/>
@@ -32175,6 +32177,7 @@
   <int value="-1892555086" label="disable-compositor-animation-timelines"/>
   <int value="-1892000374" label="SeccompSandboxAndroid:enabled"/>
   <int value="-1890374564" label="OobeRecommendAppsScreen:disabled"/>
+  <int value="-1890060129" label="MojoIMF:disabled"/>
   <int value="-1888273969" label="tab-capture-upscale-quality"/>
   <int value="-1887862464" label="SpannableInlineAutocomplete:disabled"/>
   <int value="-1887053262"
@@ -38032,7 +38035,12 @@
   <int value="3" label="A navigation suggestion is found using top sites list"/>
   <int value="4"
       label="A navigation suggestion is found using site engagement"/>
-  <int value="5" label="A navigation suggestion is found using edit distance"/>
+  <int value="5"
+      label="A navigation suggestion is found using edit distance against a
+             top domain"/>
+  <int value="6"
+      label="A navigation suggestion is found using edit distance against an
+             engaged site"/>
 </enum>
 
 <enum name="NavigationWasServedFromCache">
diff --git a/ui/accessibility/ax_event_generator.cc b/ui/accessibility/ax_event_generator.cc
index b5e60f700..f7d3f50f 100644
--- a/ui/accessibility/ax_event_generator.cc
+++ b/ui/accessibility/ax_event_generator.cc
@@ -404,7 +404,8 @@
     AddEvent(tree->root(), Event::LOAD_COMPLETE);
   }
 
-  if (new_tree_data.sel_anchor_object_id !=
+  if (new_tree_data.sel_is_backward != old_tree_data.sel_is_backward ||
+      new_tree_data.sel_anchor_object_id !=
           old_tree_data.sel_anchor_object_id ||
       new_tree_data.sel_anchor_offset != old_tree_data.sel_anchor_offset ||
       new_tree_data.sel_anchor_affinity != old_tree_data.sel_anchor_affinity ||
diff --git a/ui/accessibility/ax_tree_combiner.cc b/ui/accessibility/ax_tree_combiner.cc
index 207a2aa3..3d57d47 100644
--- a/ui/accessibility/ax_tree_combiner.cc
+++ b/ui/accessibility/ax_tree_combiner.cc
@@ -52,6 +52,8 @@
     focused_tree = tree_id_map_[focused_tree_id];
   combined_.tree_data.focus_id =
       MapId(focused_tree_id, focused_tree->tree_data.focus_id);
+  combined_.tree_data.sel_is_backward =
+      MapId(focused_tree_id, focused_tree->tree_data.sel_is_backward);
   combined_.tree_data.sel_anchor_object_id =
       MapId(focused_tree_id, focused_tree->tree_data.sel_anchor_object_id);
   combined_.tree_data.sel_focus_object_id =
diff --git a/ui/accessibility/ax_tree_data.cc b/ui/accessibility/ax_tree_data.cc
index ee2a8e13..7380fe3 100644
--- a/ui/accessibility/ax_tree_data.cc
+++ b/ui/accessibility/ax_tree_data.cc
@@ -48,6 +48,8 @@
 
   if (sel_anchor_object_id != -1) {
     result +=
+        (sel_is_backward ? " sel_is_backward=true" : " sel_is_backward=false");
+    result +=
         " sel_anchor_object_id=" + base::NumberToString(sel_anchor_object_id);
     result += " sel_anchor_offset=" + base::NumberToString(sel_anchor_offset);
     result += " sel_anchor_affinity=";
diff --git a/ui/accessibility/ax_tree_data.h b/ui/accessibility/ax_tree_data.h
index 2470f4e..39e88c6 100644
--- a/ui/accessibility/ax_tree_data.h
+++ b/ui/accessibility/ax_tree_data.h
@@ -60,6 +60,7 @@
   // (selection end). If the offset could correspond to a position on two
   // different lines, sel_upstream_affinity means the cursor is on the first
   // line, otherwise it's on the second line.
+  bool sel_is_backward = false;
   int32_t sel_anchor_object_id = -1;
   int32_t sel_anchor_offset = -1;
   ax::mojom::TextAffinity sel_anchor_affinity =
diff --git a/ui/accessibility/mojom/ax_tree_data.mojom b/ui/accessibility/mojom/ax_tree_data.mojom
index c2fabbf2..626691f 100644
--- a/ui/accessibility/mojom/ax_tree_data.mojom
+++ b/ui/accessibility/mojom/ax_tree_data.mojom
@@ -19,6 +19,7 @@
   string title;
   string url;
   int32 focus_id;
+  bool sel_is_backward;
   int32 sel_anchor_object_id;
   int32 sel_anchor_offset;
   ax.mojom.TextAffinity sel_anchor_affinity;
diff --git a/ui/accessibility/mojom/ax_tree_data_mojom_traits.cc b/ui/accessibility/mojom/ax_tree_data_mojom_traits.cc
index 50ad6115..9450001 100644
--- a/ui/accessibility/mojom/ax_tree_data_mojom_traits.cc
+++ b/ui/accessibility/mojom/ax_tree_data_mojom_traits.cc
@@ -27,6 +27,7 @@
   if (!data.ReadUrl(&out->url))
     return false;
   out->focus_id = data.focus_id();
+  out->sel_is_backward = data.sel_is_backward();
   out->sel_anchor_object_id = data.sel_anchor_object_id();
   out->sel_anchor_offset = data.sel_anchor_offset();
   out->sel_anchor_affinity = data.sel_anchor_affinity();
diff --git a/ui/accessibility/mojom/ax_tree_data_mojom_traits.h b/ui/accessibility/mojom/ax_tree_data_mojom_traits.h
index 6beea97..6be48769 100644
--- a/ui/accessibility/mojom/ax_tree_data_mojom_traits.h
+++ b/ui/accessibility/mojom/ax_tree_data_mojom_traits.h
@@ -35,6 +35,9 @@
   static const std::string& title(const ui::AXTreeData& p) { return p.title; }
   static const std::string& url(const ui::AXTreeData& p) { return p.url; }
   static int32_t focus_id(const ui::AXTreeData& p) { return p.focus_id; }
+  static bool sel_is_backward(const ui::AXTreeData& p) {
+    return p.sel_is_backward;
+  }
   static int32_t sel_anchor_object_id(const ui::AXTreeData& p) {
     return p.sel_anchor_object_id;
   }
diff --git a/ui/accessibility/mojom/ax_tree_data_mojom_traits_unittest.cc b/ui/accessibility/mojom/ax_tree_data_mojom_traits_unittest.cc
index fa3cb0e..2508d22 100644
--- a/ui/accessibility/mojom/ax_tree_data_mojom_traits_unittest.cc
+++ b/ui/accessibility/mojom/ax_tree_data_mojom_traits_unittest.cc
@@ -26,6 +26,7 @@
   input.title = "7";
   input.url = "8";
   input.focus_id = 9;
+  input.sel_is_backward = true;  // Set to true only for testing purposes.
   input.sel_anchor_object_id = 10;
   input.sel_anchor_offset = 11;
   input.sel_anchor_affinity = ax::mojom::TextAffinity::kUpstream;
@@ -45,6 +46,7 @@
   EXPECT_EQ("7", output.title);
   EXPECT_EQ("8", output.url);
   EXPECT_EQ(9, output.focus_id);
+  EXPECT_TRUE(output.sel_is_backward);
   EXPECT_EQ(10, output.sel_anchor_object_id);
   EXPECT_EQ(11, output.sel_anchor_offset);
   EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, output.sel_anchor_affinity);
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index 91c6b26..5e93250 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -21,6 +21,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/threading/thread.h"
+#include "base/trace_event/trace_event.h"
 #include "cc/base/switches.h"
 #include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
 #include "mojo/public/cpp/bindings/map.h"
@@ -1224,6 +1225,14 @@
   if (!window)
     return;
 
+  if (IsRoot(window)) {
+    TRACE_EVENT_WITH_FLOW0(
+        "ui", "ClientRoot::NotifyClientOfNewBounds",
+        local_surface_id_allocation->local_surface_id().hash(),
+        TRACE_EVENT_FLAG_FLOW_IN);
+  }
+  TRACE_EVENT0("ui", "WindowTreeClient::OnWindowBoundsChanged");
+
   InFlightBoundsChange new_change(this, window, new_bounds,
                                   /* from_server */ true,
                                   local_surface_id_allocation);
diff --git a/ui/base/accelerators/accelerator.cc b/ui/base/accelerators/accelerator.cc
index a59d08c..dadc140 100644
--- a/ui/base/accelerators/accelerator.cc
+++ b/ui/base/accelerators/accelerator.cc
@@ -65,7 +65,8 @@
       // |modifiers_| may include the repeat flag.
       modifiers_(key_event.flags() & kInterestingFlagsMask),
       time_stamp_(key_event.time_stamp()),
-      interrupted_by_mouse_event_(false) {}
+      interrupted_by_mouse_event_(false),
+      source_device_id_(key_event.source_device_id()) {}
 
 Accelerator::Accelerator(const Accelerator& accelerator) {
   key_code_ = accelerator.key_code_;
@@ -73,6 +74,7 @@
   modifiers_ = accelerator.modifiers_;
   time_stamp_ = accelerator.time_stamp_;
   interrupted_by_mouse_event_ = accelerator.interrupted_by_mouse_event_;
+  source_device_id_ = accelerator.source_device_id_;
 }
 
 Accelerator::~Accelerator() {
diff --git a/ui/base/accelerators/accelerator.h b/ui/base/accelerators/accelerator.h
index 643d508f..5ece4bf 100644
--- a/ui/base/accelerators/accelerator.h
+++ b/ui/base/accelerators/accelerator.h
@@ -78,6 +78,8 @@
 
   base::TimeTicks time_stamp() const { return time_stamp_; }
 
+  int source_device_id() const { return source_device_id_; }
+
   bool IsShiftDown() const;
   bool IsCtrlDown() const;
   bool IsAltDown() const;
@@ -121,6 +123,9 @@
   // TOGGLE_APP_LIST and TOGGLE_APP_LIST_FULLSCREEN are disabled when mouse
   // press/release occurs between search key down and up. See crbug.com/665897)
   bool interrupted_by_mouse_event_;
+
+  // The |source_device_id_| of the KeyEvent.
+  int source_device_id_ = -1;
 };
 
 // An interface that classes that want to register for keyboard accelerators
diff --git a/ui/events/devices/device_data_manager.cc b/ui/events/devices/device_data_manager.cc
index f94d420..71389f36 100644
--- a/ui/events/devices/device_data_manager.cc
+++ b/ui/events/devices/device_data_manager.cc
@@ -149,6 +149,11 @@
   return touchpad_devices_;
 }
 
+const std::vector<InputDevice>& DeviceDataManager::GetUncategorizedDevices()
+    const {
+  return uncategorized_devices_;
+}
+
 bool DeviceDataManager::AreDeviceListsComplete() const {
   return device_lists_complete_;
 }
@@ -217,6 +222,17 @@
   NotifyObserversTouchpadDeviceConfigurationChanged();
 }
 
+void DeviceDataManager::OnUncategorizedDevicesUpdated(
+    const std::vector<InputDevice>& devices) {
+  if (devices.size() == uncategorized_devices_.size() &&
+      std::equal(devices.begin(), devices.end(), uncategorized_devices_.begin(),
+                 InputDeviceEquals)) {
+    return;
+  }
+  uncategorized_devices_ = devices;
+  NotifyObserversUncategorizedDeviceConfigurationChanged();
+}
+
 void DeviceDataManager::OnDeviceListsComplete() {
   if (!device_lists_complete_) {
     device_lists_complete_ = true;
@@ -241,6 +257,10 @@
     OnInputDeviceConfigurationChanged(InputDeviceEventObserver::kTouchpad))
 
 NOTIFY_OBSERVERS(
+    NotifyObserversUncategorizedDeviceConfigurationChanged(),
+    OnInputDeviceConfigurationChanged(InputDeviceEventObserver::kUncategorized))
+
+NOTIFY_OBSERVERS(
     NotifyObserversTouchscreenDeviceConfigurationChanged(),
     OnInputDeviceConfigurationChanged(InputDeviceEventObserver::kTouchscreen))
 
diff --git a/ui/events/devices/device_data_manager.h b/ui/events/devices/device_data_manager.h
index 22062276..d5be2ea 100644
--- a/ui/events/devices/device_data_manager.h
+++ b/ui/events/devices/device_data_manager.h
@@ -60,6 +60,7 @@
   const std::vector<InputDevice>& GetKeyboardDevices() const override;
   const std::vector<InputDevice>& GetMouseDevices() const override;
   const std::vector<InputDevice>& GetTouchpadDevices() const override;
+  const std::vector<InputDevice>& GetUncategorizedDevices() const override;
   bool AreDeviceListsComplete() const override;
   bool AreTouchscreensEnabled() const override;
   bool AreTouchscreenTargetDisplaysValid() const override;
@@ -80,6 +81,8 @@
       const std::vector<InputDevice>& devices) override;
   void OnTouchpadDevicesUpdated(
       const std::vector<InputDevice>& devices) override;
+  void OnUncategorizedDevicesUpdated(
+      const std::vector<InputDevice>& devices) override;
   void OnDeviceListsComplete() override;
   void OnStylusStateChanged(StylusState state) override;
 
@@ -96,6 +99,7 @@
   void NotifyObserversKeyboardDeviceConfigurationChanged();
   void NotifyObserversMouseDeviceConfigurationChanged();
   void NotifyObserversTouchpadDeviceConfigurationChanged();
+  void NotifyObserversUncategorizedDeviceConfigurationChanged();
   void NotifyObserversDeviceListsComplete();
   void NotifyObserversStylusStateChanged(StylusState stylus_state);
 
@@ -105,6 +109,7 @@
   std::vector<InputDevice> keyboard_devices_;
   std::vector<InputDevice> mouse_devices_;
   std::vector<InputDevice> touchpad_devices_;
+  std::vector<InputDevice> uncategorized_devices_;
   bool device_lists_complete_ = false;
 
   base::ObserverList<InputDeviceEventObserver>::Unchecked observers_;
diff --git a/ui/events/devices/device_hotplug_event_observer.h b/ui/events/devices/device_hotplug_event_observer.h
index 7b846bc..ec7f678 100644
--- a/ui/events/devices/device_hotplug_event_observer.h
+++ b/ui/events/devices/device_hotplug_event_observer.h
@@ -40,6 +40,12 @@
   virtual void OnTouchpadDevicesUpdated(
       const std::vector<InputDevice>& devices) = 0;
 
+  // On a hotplug event this is called with the list of the available
+  // uncategorized input devices, which means not touchscreens, keyboards, mice
+  // and touchpads.
+  virtual void OnUncategorizedDevicesUpdated(
+      const std::vector<InputDevice>& devices) = 0;
+
   // On completion of the initial startup scan. This means all of the above
   // OnDevicesUpdated() methods have been called with a complete list.
   virtual void OnDeviceListsComplete() = 0;
diff --git a/ui/events/devices/input_device_event_observer.h b/ui/events/devices/input_device_event_observer.h
index 6310592..ca4c974 100644
--- a/ui/events/devices/input_device_event_observer.h
+++ b/ui/events/devices/input_device_event_observer.h
@@ -22,6 +22,7 @@
   static constexpr uint8_t kMouse = 1 << 1;
   static constexpr uint8_t kTouchpad = 1 << 2;
   static constexpr uint8_t kTouchscreen = 1 << 3;
+  static constexpr uint8_t kUncategorized = 1 << 4;
 
   virtual ~InputDeviceEventObserver() {}
 
diff --git a/ui/events/devices/input_device_manager.h b/ui/events/devices/input_device_manager.h
index 4f72ea6..0a4317e9 100644
--- a/ui/events/devices/input_device_manager.h
+++ b/ui/events/devices/input_device_manager.h
@@ -32,6 +32,10 @@
   virtual const std::vector<InputDevice>& GetMouseDevices() const = 0;
   virtual const std::vector<InputDevice>& GetTouchpadDevices() const = 0;
 
+  // Returns all the uncategorized input devices, which means input devices
+  // besides keyboards, touchscreens, mice and touchpads.
+  virtual const std::vector<InputDevice>& GetUncategorizedDevices() const = 0;
+
   virtual bool AreDeviceListsComplete() const = 0;
   virtual bool AreTouchscreensEnabled() const = 0;
 
diff --git a/ui/events/keycodes/dom/keycode_converter_data.inc b/ui/events/keycodes/dom/keycode_converter_data.inc
index d4174fc5..53cc8a3 100644
--- a/ui/events/keycodes/dom/keycode_converter_data.inc
+++ b/ui/events/keycodes/dom/keycode_converter_data.inc
@@ -103,6 +103,9 @@
   USB_KEYMAP(0x000014, 0x0000, 0x0000, 0x0000, 0xffff, "Suspend", SUSPEND),
   USB_KEYMAP(0x000015, 0x0000, 0x0000, 0x0000, 0xffff, "Resume", RESUME),
   USB_KEYMAP(0x000016, 0x0000, 0x0000, 0x0000, 0xffff, "Turbo", TURBO),
+  // AL Context-aware desktop assistant, not in HID specification (yet?)
+  USB_KEYMAP(0x000017, 0x0247, 0x024f, 0x0000, 0xffff, "LaunchAssistant",
+             LAUNCH_ASSISTANT),
 
   // =========================================
   // USB Usage Page 0x01: Generic Desktop Page
@@ -113,9 +116,6 @@
   //            USB     evdev    XKB     Win     Mac
   USB_KEYMAP(0x010082, 0x008e, 0x0096, 0xe05f, 0xffff, "Sleep", SLEEP), // SystemSleep
   USB_KEYMAP(0x010083, 0x008f, 0x0097, 0xe063, 0xffff, "WakeUp", WAKE_UP),
-  USB_KEYMAP(0x0100b5, 0x00e3, 0x00eb, 0x0000, 0xffff, NULL,
-             DISPLAY_TOGGLE_INT_EXT),  // System Display Toggle Int/Ext
-
 
   // =========================================
   // USB Usage Page 0x07: Keyboard/Keypad Page
@@ -539,9 +539,7 @@
   USB_KEYMAP(0x0c01ae, 0x0176, 0x017e, 0x0000, 0xffff, NULL, LAUNCH_KEYBOARD_LAYOUT),
   USB_KEYMAP(0x0c01b1, 0x0245, 0x024d, 0x0000, 0xffff, "LaunchScreenSaver",
              LAUNCH_SCREEN_SAVER),  // AL Screen Saver
-  USB_KEYMAP(0x0c01cb, 0x0247, 0x024f, 0x0000, 0xffff, NULL,
-             LAUNCH_ASSISTANT),  // AL Context-aware desktop assistant
-   // USB#0c01b4: Home Directory (AL_FileBrowser) (Explorer)
+  // USB#0c01b4: Home Directory (AL_FileBrowser) (Explorer)
   //USB_KEYMAP(0x0c01b4, 0x0000, 0x0000, 0x0000, 0xffff, NULL, LAUNCH_FILE_BROWSER),
   // USB#0x0c01b7: AL Audio Browser
   USB_KEYMAP(0x0c01b7, 0x0188, 0x0190, 0x0000, 0xffff, NULL, LAUNCH_AUDIO_BROWSER),
@@ -576,8 +574,8 @@
   //USB_KEYMAP(0x0c0230, 0x0000, 0x0000, 0x0000, 0xffff, NULL, ZOOM_FULL),
   // USB#0x0c0231:  AC Normal View
   //USB_KEYMAP(0x0c0231, 0x0000, 0x0000, 0x0000, 0xffff, NULL, ZOOM_NORMAL),
-  USB_KEYMAP(0x0c0232, 0x0174, 0x017c, 0x0000, 0xffff, NULL,
-             ZOOM_TOGGLE),  // AC View Toggle
+  // USB#0x0c0232:  AC View Toggle
+  USB_KEYMAP(0x0c0232, 0x0000, 0x0000, 0x0000, 0xffff, "ZoomToggle", ZOOM_TOGGLE),
   // USB#0x0c0279:  AC Redo/Repeat
   USB_KEYMAP(0x0c0279, 0x00b6, 0x00be, 0x0000, 0xffff, NULL, REDO),
   // USB#0x0c0289:  AC_Reply
@@ -586,6 +584,4 @@
   USB_KEYMAP(0x0c028b, 0x00e9, 0x00f1, 0x0000, 0xffff, "MailForward", MAIL_FORWARD),
   // USB#0x0c028c:  AC_Send
   USB_KEYMAP(0x0c028c, 0x00e7, 0x00ef, 0x0000, 0xffff, "MailSend", MAIL_SEND),
-  USB_KEYMAP(0x0c029f, 0x0078, 0x0080, 0x0000, 0xffff, NULL,
-             SHOW_ALL_WINDOWS),  // AC Desktop Show All Windows
 };
diff --git a/ui/events/keycodes/dom/keycode_converter_unittest.cc b/ui/events/keycodes/dom/keycode_converter_unittest.cc
index b82d525..d3e51aa3 100644
--- a/ui/events/keycodes/dom/keycode_converter_unittest.cc
+++ b/ui/events/keycodes/dom/keycode_converter_unittest.cc
@@ -24,10 +24,10 @@
 // These are in the same order as the columns in keycode_converter_data.inc
 // as reflected in the USB_KEYMAP() macro below.
 const size_t expected_mapped_key_count[] = {
-  211,  // evdev
-  211,  // xkb
-  157,  // windows
-  118,  // mac
+  208, // evdev
+  208, // xkb
+  157, // windows
+  118, // mac
 };
 
 const size_t kNativeColumns = base::size(expected_mapped_key_count);
diff --git a/ui/events/keycodes/dom_us_layout_data.h b/ui/events/keycodes/dom_us_layout_data.h
index b61b001..3bfcadcb 100644
--- a/ui/events/keycodes/dom_us_layout_data.h
+++ b/ui/events/keycodes/dom_us_layout_data.h
@@ -416,6 +416,10 @@
     // DomCode::SUSPEND                            0x000014 Suspend
     // DomCode::RESUME                             0x000015 Resume
     // DomCode::TURBO                              0x000016 Turbo
+#if defined(OS_POSIX)
+    {DomCode::LAUNCH_ASSISTANT,
+     VKEY_ASSISTANT},                          // 0x000017 Launch Assistant
+#endif
     {DomCode::SLEEP, VKEY_SLEEP},               // 0x010082 Sleep
     // DomCode::WAKE_UP                            0x010083 WakeUp
     {DomCode::US_A, VKEY_A},                   // 0x070004 KeyA
@@ -600,8 +604,6 @@
 #if defined(OS_POSIX)
     {DomCode::LAUNCH_CONTROL_PANEL,
      VKEY_SETTINGS},                            // 0x0C019F Launch Assistant
-    {DomCode::LAUNCH_ASSISTANT,
-     VKEY_ASSISTANT},                           // 0x0C01CB Launch Assistant
 #endif
     {DomCode::BROWSER_SEARCH,
      VKEY_BROWSER_SEARCH},                      // 0x0C0221 BrowserSearch
diff --git a/ui/events/ozone/evdev/device_event_dispatcher_evdev.h b/ui/events/ozone/evdev/device_event_dispatcher_evdev.h
index f37d66b7..197be1d3 100644
--- a/ui/events/ozone/evdev/device_event_dispatcher_evdev.h
+++ b/ui/events/ozone/evdev/device_event_dispatcher_evdev.h
@@ -183,6 +183,8 @@
   virtual void DispatchStylusStateChanged(StylusState stylus_state) = 0;
   virtual void DispatchGamepadDevicesUpdated(
       const std::vector<InputDevice>& devices) = 0;
+  virtual void DispatchUncategorizedDevicesUpdated(
+      const std::vector<InputDevice>& devices) = 0;
 };
 
 }  // namespace ui
diff --git a/ui/events/ozone/evdev/event_converter_test_util.cc b/ui/events/ozone/evdev/event_converter_test_util.cc
index 52d339c..57b14c7 100644
--- a/ui/events/ozone/evdev/event_converter_test_util.cc
+++ b/ui/events/ozone/evdev/event_converter_test_util.cc
@@ -78,6 +78,10 @@
       const std::vector<InputDevice>& devices) override {
     event_factory_evdev_->DispatchTouchpadDevicesUpdated(devices);
   }
+  void DispatchUncategorizedDevicesUpdated(
+      const std::vector<InputDevice>& devices) override {
+    event_factory_evdev_->DispatchUncategorizedDevicesUpdated(devices);
+  }
   void DispatchDeviceListsComplete() override {
     event_factory_evdev_->DispatchDeviceListsComplete();
   }
diff --git a/ui/events/ozone/evdev/event_device_info_unittest.cc b/ui/events/ozone/evdev/event_device_info_unittest.cc
index 3e7c081..f09d730 100644
--- a/ui/events/ozone/evdev/event_device_info_unittest.cc
+++ b/ui/events/ozone/evdev/event_device_info_unittest.cc
@@ -43,6 +43,18 @@
   EXPECT_EQ(ui::InputDeviceType::INPUT_DEVICE_INTERNAL, devinfo.device_type());
 }
 
+TEST(EventDeviceInfoTest, SideVolumeButton) {
+  EventDeviceInfo devinfo;
+  EXPECT_TRUE(CapabilitiesToDeviceInfo(kSideVolumeButton, &devinfo));
+
+  EXPECT_FALSE(devinfo.HasKeyboard());
+  EXPECT_FALSE(devinfo.HasMouse());
+  EXPECT_FALSE(devinfo.HasTouchpad());
+  EXPECT_FALSE(devinfo.HasTouchscreen());
+  EXPECT_FALSE(devinfo.HasTablet());
+  EXPECT_FALSE(devinfo.HasGamepad());
+}
+
 TEST(EventDeviceInfoTest, BasicCrosTouchscreen) {
   EventDeviceInfo devinfo;
   EXPECT_TRUE(CapabilitiesToDeviceInfo(kLinkTouchscreen, &devinfo));
diff --git a/ui/events/ozone/evdev/event_device_test_util.cc b/ui/events/ozone/evdev/event_device_test_util.cc
index e652ea7..0cdb1f6a 100644
--- a/ui/events/ozone/evdev/event_device_test_util.cc
+++ b/ui/events/ozone/evdev/event_device_test_util.cc
@@ -760,6 +760,28 @@
     base::size(kIlitekTPAbsAxes),
 };
 
+const DeviceCapabilities kSideVolumeButton = {
+    /* path */
+    "/sys/devices/pci0000:00/0000:00:1f.0/PNP0C09:00/GOOG0004:00/GOOG0007:00/"
+    "input/input5/event4",
+    /* name */ "cros_ec_buttons",
+    /* phys */ "GOOG0004:00/input1",
+    /* uniq */ "",
+    /* bustype */ "0006",
+    /* vendor */ "0000",
+    /* product */ "0000",
+    /* version */ "0001",
+    /* prop */ "0",
+    /* ev */ "100023",
+    /* key */ "1c000000000000 0",
+    /* rel */ "0",
+    /* abs */ "0",
+    /* msc */ "0",
+    /* sw */ "1",
+    /* led */ "0",
+    /* ff */ "0",
+};
+
 // NB: Please use the capture_device_capabilities.py script to add more
 // test data here. This will help ensure the data matches what the kernel
 // reports for a real device and is entered correctly.
diff --git a/ui/events/ozone/evdev/event_device_test_util.h b/ui/events/ozone/evdev/event_device_test_util.h
index 533690f..203b8aa 100644
--- a/ui/events/ozone/evdev/event_device_test_util.h
+++ b/ui/events/ozone/evdev/event_device_test_util.h
@@ -81,6 +81,7 @@
 extern const DeviceCapabilities kHammerTouchpad;
 extern const DeviceCapabilities kIlitekTP_Mouse;
 extern const DeviceCapabilities kIlitekTP;
+extern const DeviceCapabilities kSideVolumeButton;
 
 }  // namspace ui
 
diff --git a/ui/events/ozone/evdev/event_factory_evdev.cc b/ui/events/ozone/evdev/event_factory_evdev.cc
index cdc68677..067f5662 100644
--- a/ui/events/ozone/evdev/event_factory_evdev.cc
+++ b/ui/events/ozone/evdev/event_factory_evdev.cc
@@ -140,6 +140,14 @@
                        event_factory_evdev_, devices));
   }
 
+  void DispatchUncategorizedDevicesUpdated(
+      const std::vector<InputDevice>& devices) override {
+    ui_thread_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&EventFactoryEvdev::DispatchUncategorizedDevicesUpdated,
+                       event_factory_evdev_, devices));
+  }
+
  private:
   scoped_refptr<base::SingleThreadTaskRunner> ui_thread_runner_;
   base::WeakPtr<EventFactoryEvdev> event_factory_evdev_;
@@ -409,6 +417,14 @@
   observer->OnStylusStateChanged(stylus_state);
 }
 
+void EventFactoryEvdev::DispatchUncategorizedDevicesUpdated(
+    const std::vector<InputDevice>& devices) {
+  TRACE_EVENT0("evdev",
+               "EventFactoryEvdev::DispatchUncategorizedDevicesUpdated");
+  DeviceHotplugEventObserver* observer = DeviceDataManager::GetInstance();
+  observer->OnUncategorizedDevicesUpdated(devices);
+}
+
 void EventFactoryEvdev::DispatchGamepadDevicesUpdated(
     const std::vector<InputDevice>& devices) {
   TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchGamepadDevicesUpdated");
diff --git a/ui/events/ozone/evdev/event_factory_evdev.h b/ui/events/ozone/evdev/event_factory_evdev.h
index 1c6a1cc..ce77fb1 100644
--- a/ui/events/ozone/evdev/event_factory_evdev.h
+++ b/ui/events/ozone/evdev/event_factory_evdev.h
@@ -81,6 +81,8 @@
       const std::vector<TouchscreenDevice>& devices);
   void DispatchMouseDevicesUpdated(const std::vector<InputDevice>& devices);
   void DispatchTouchpadDevicesUpdated(const std::vector<InputDevice>& devices);
+  void DispatchUncategorizedDevicesUpdated(
+      const std::vector<InputDevice>& devices);
   void DispatchDeviceListsComplete();
   void DispatchStylusStateChanged(StylusState stylus_state);
 
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev.cc b/ui/events/ozone/evdev/input_device_factory_evdev.cc
index 0871b55..b621db9 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev.cc
+++ b/ui/events/ozone/evdev/input_device_factory_evdev.cc
@@ -156,6 +156,12 @@
   return CreateConverter(params, std::move(fd), devinfo);
 }
 
+bool IsUncategorizedDevice(const EventConverterEvdev& converter) {
+  return !converter.HasTouchscreen() && !converter.HasKeyboard() &&
+         !converter.HasMouse() && !converter.HasTouchpad() &&
+         !converter.HasGamepad();
+}
+
 }  // namespace
 
 InputDeviceFactoryEvdev::InputDeviceFactoryEvdev(
@@ -391,6 +397,9 @@
 
   if (converter->HasGamepad())
     gamepad_list_dirty_ = true;
+
+  if (IsUncategorizedDevice(*converter))
+    uncategorized_list_dirty_ = true;
 }
 
 void InputDeviceFactoryEvdev::NotifyDevicesUpdated() {
@@ -406,6 +415,8 @@
     NotifyTouchpadDevicesUpdated();
   if (gamepad_list_dirty_)
     NotifyGamepadDevicesUpdated();
+  if (uncategorized_list_dirty_)
+    NotifyUncategorizedDevicesUpdated();
   if (!startup_devices_opened_) {
     dispatcher_->DispatchDeviceListsComplete();
     startup_devices_opened_ = true;
@@ -415,6 +426,7 @@
   mouse_list_dirty_ = false;
   touchpad_list_dirty_ = false;
   gamepad_list_dirty_ = false;
+  uncategorized_list_dirty_ = false;
 }
 
 void InputDeviceFactoryEvdev::NotifyTouchscreensUpdated() {
@@ -474,6 +486,16 @@
   dispatcher_->DispatchGamepadDevicesUpdated(gamepads);
 }
 
+void InputDeviceFactoryEvdev::NotifyUncategorizedDevicesUpdated() {
+  std::vector<InputDevice> uncategorized_devices;
+  for (auto it = converters_.begin(); it != converters_.end(); ++it) {
+    if (IsUncategorizedDevice(*(it->second)))
+      uncategorized_devices.push_back(it->second->input_device());
+  }
+
+  dispatcher_->DispatchUncategorizedDevicesUpdated(uncategorized_devices);
+}
+
 void InputDeviceFactoryEvdev::SetIntPropertyForOneType(
     const EventDeviceType type,
     const std::string& name,
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev.h b/ui/events/ozone/evdev/input_device_factory_evdev.h
index a47c9b4..085908f3 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev.h
+++ b/ui/events/ozone/evdev/input_device_factory_evdev.h
@@ -86,6 +86,7 @@
   void NotifyMouseDevicesUpdated();
   void NotifyTouchpadDevicesUpdated();
   void NotifyGamepadDevicesUpdated();
+  void NotifyUncategorizedDevicesUpdated();
 
   void SetIntPropertyForOneType(const EventDeviceType type,
                                 const std::string& name,
@@ -117,6 +118,7 @@
   bool mouse_list_dirty_ = true;
   bool touchpad_list_dirty_ = true;
   bool gamepad_list_dirty_ = true;
+  bool uncategorized_list_dirty_ = true;
 
   // Whether we have a list of devices that were present at startup.
   bool startup_devices_enumerated_ = false;
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc b/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
index 4b04146..0104171 100644
--- a/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
+++ b/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
@@ -159,6 +159,8 @@
       const std::vector<InputDevice>& devices) override {}
   void DispatchTouchpadDevicesUpdated(
       const std::vector<InputDevice>& devices) override {}
+  void DispatchUncategorizedDevicesUpdated(
+      const std::vector<InputDevice>& devices) override {}
   void DispatchDeviceListsComplete() override {}
   void DispatchStylusStateChanged(StylusState stylus_state) override {}
 
diff --git a/ui/file_manager/image_loader/piex/tests.js b/ui/file_manager/image_loader/piex/tests.js
index 0763897f..2b15b1f 100644
--- a/ui/file_manager/image_loader/piex/tests.js
+++ b/ui/file_manager/image_loader/piex/tests.js
@@ -95,11 +95,11 @@
     return window.createFileSystem(length);
   }, images.length);
 
-  for (let i = 0; i < images.length; ++i) {
-    await page.evaluate((image) => {
+  await Promise.all(images.map((image) => {
+    return page.evaluate((image) => {
       return window.writeToFileSystem(image);
-    }, images[i]);
-  }
+    }, image);
+  }));
 
   await page.evaluate(() => {
     window.testTime = 0;
diff --git a/ui/keyboard/keyboard_controller.cc b/ui/keyboard/keyboard_controller.cc
index 9692d59a..6bcb61d 100644
--- a/ui/keyboard/keyboard_controller.cc
+++ b/ui/keyboard/keyboard_controller.cc
@@ -571,8 +571,7 @@
            mojom::KeyboardOverscrollBehavior::kEnabled;
   }
 
-  return !base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kDisableVirtualKeyboardOverscroll);
+  return true;
 }
 
 // private
diff --git a/ui/keyboard/public/keyboard_switches.cc b/ui/keyboard/public/keyboard_switches.cc
index 14eb206..247b92a 100644
--- a/ui/keyboard/public/keyboard_switches.cc
+++ b/ui/keyboard/public/keyboard_switches.cc
@@ -9,8 +9,6 @@
 
 const char kDisableGestureTyping[] = "disable-gesture-typing";
 const char kEnableVirtualKeyboard[] = "enable-virtual-keyboard";
-const char kDisableVirtualKeyboardOverscroll[] =
-    "disable-virtual-keyboard-overscroll";
 
 }  // namespace switches
 }  // namespace keyboard
diff --git a/ui/keyboard/public/keyboard_switches.h b/ui/keyboard/public/keyboard_switches.h
index d4e7469..6931e7c 100644
--- a/ui/keyboard/public/keyboard_switches.h
+++ b/ui/keyboard/public/keyboard_switches.h
@@ -16,11 +16,6 @@
 // Enables the virtual keyboard.
 KEYBOARD_EXPORT extern const char kEnableVirtualKeyboard[];
 
-// Disabled overscrolling of web content when the virtual keyboard is displayed.
-// If disabled, the work area is resized to restrict windows from overlapping
-// with the keybaord area.
-KEYBOARD_EXPORT extern const char kDisableVirtualKeyboardOverscroll[];
-
 }  // namespace switches
 }  // namespace keyboard
 
diff --git a/ui/message_center/views/notification_header_view.cc b/ui/message_center/views/notification_header_view.cc
index 4e6d92c..8ef9bbf 100644
--- a/ui/message_center/views/notification_header_view.cc
+++ b/ui/message_center/views/notification_header_view.cc
@@ -202,6 +202,7 @@
   summary_text_divider_->SetLineHeight(font_list_height);
   summary_text_divider_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   summary_text_divider_->SetBorder(views::CreateEmptyBorder(text_view_padding));
+  summary_text_divider_->SetEnabledColor(accent_color_);
   summary_text_divider_->SetVisible(false);
   DCHECK_EQ(kInnerHeaderHeight,
             summary_text_divider_->GetPreferredSize().height());
@@ -213,6 +214,7 @@
   summary_text_view_->SetLineHeight(font_list_height);
   summary_text_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   summary_text_view_->SetBorder(views::CreateEmptyBorder(text_view_padding));
+  summary_text_view_->SetEnabledColor(accent_color_);
   summary_text_view_->SetVisible(false);
   DCHECK_EQ(kInnerHeaderHeight,
             summary_text_view_->GetPreferredSize().height());
@@ -287,7 +289,14 @@
   UpdateSummaryTextVisibility();
 }
 
+void NotificationHeaderView::SetSummaryText(const base::string16& text) {
+  DCHECK(!has_progress_);
+  summary_text_view_->SetText(text);
+  UpdateSummaryTextVisibility();
+}
+
 void NotificationHeaderView::ClearProgress() {
+  summary_text_view_->SetText(base::string16());
   has_progress_ = false;
   UpdateSummaryTextVisibility();
 }
@@ -296,15 +305,10 @@
   if (count > 0) {
     summary_text_view_->SetText(l10n_util::GetStringFUTF16Int(
         IDS_MESSAGE_CENTER_LIST_NOTIFICATION_HEADER_OVERFLOW_INDICATOR, count));
-    has_overflow_indicator_ = true;
   } else {
-    has_overflow_indicator_ = false;
+    summary_text_view_->SetText(base::string16());
   }
-  UpdateSummaryTextVisibility();
-}
 
-void NotificationHeaderView::ClearOverflowIndicator() {
-  has_overflow_indicator_ = false;
   UpdateSummaryTextVisibility();
 }
 
@@ -353,6 +357,8 @@
 void NotificationHeaderView::SetAccentColor(SkColor color) {
   accent_color_ = color;
   app_name_view_->SetEnabledColor(accent_color_);
+  summary_text_view_->SetEnabledColor(accent_color_);
+  summary_text_divider_->SetEnabledColor(accent_color_);
   SetExpanded(is_expanded_);
 }
 
@@ -381,7 +387,7 @@
 }
 
 void NotificationHeaderView::UpdateSummaryTextVisibility() {
-  const bool visible = has_progress_ || has_overflow_indicator_;
+  const bool visible = !summary_text_view_->text().empty();
   summary_text_divider_->SetVisible(visible);
   summary_text_view_->SetVisible(visible);
   timestamp_divider_->SetVisible(!has_progress_ && has_timestamp_);
diff --git a/ui/message_center/views/notification_header_view.h b/ui/message_center/views/notification_header_view.h
index 7b607b6..adc254a 100644
--- a/ui/message_center/views/notification_header_view.h
+++ b/ui/message_center/views/notification_header_view.h
@@ -28,8 +28,13 @@
   void SetAppIcon(const gfx::ImageSkia& img);
   void SetAppName(const base::string16& name);
   void SetAppNameElideBehavior(gfx::ElideBehavior elide_behavior);
+
+  // Progress, summary and overflow indicator are all the same UI element so are
+  // mutually exclusive.
   void SetProgress(int progress);
+  void SetSummaryText(const base::string16& text);
   void SetOverflowIndicator(int count);
+
   void SetTimestamp(base::Time timestamp);
   void SetExpandButtonEnabled(bool enabled);
   void SetExpanded(bool expanded);
@@ -41,7 +46,6 @@
   void SetAccentColor(SkColor color);
   void ClearAppIcon();
   void ClearProgress();
-  void ClearOverflowIndicator();
   void ClearTimestamp();
   bool IsExpandButtonEnabled();
   void SetSubpixelRenderingEnabled(bool enabled);
@@ -56,6 +60,10 @@
 
   SkColor accent_color_for_testing() { return accent_color_; }
 
+  const views::Label* summary_text_for_testing() const {
+    return summary_text_view_;
+  }
+
   const base::string16& app_name_for_testing() const;
 
   const gfx::ImageSkia& app_icon_for_testing() const;
@@ -76,7 +84,6 @@
 
   bool settings_button_enabled_ = false;
   bool has_progress_ = false;
-  bool has_overflow_indicator_ = false;
   bool has_timestamp_ = false;
   bool is_expanded_ = false;
 
diff --git a/ui/views/controls/slider.cc b/ui/views/controls/slider.cc
index ebb9cb4..9ddc227 100644
--- a/ui/views/controls/slider.cc
+++ b/ui/views/controls/slider.cc
@@ -51,9 +51,9 @@
 
 // The radius of the thumb and the highlighted thumb of the slider,
 // respectively.
-constexpr float kThumbRadius = 6.f;
+constexpr float kThumbRadius = 4.f;
 constexpr float kThumbWidth = 2 * kThumbRadius;
-constexpr float kThumbHighlightRadius = 10.f;
+constexpr float kThumbHighlightRadius = 12.f;
 
 // Duration of the thumb highlight growing effect animation.
 constexpr int kSlideHighlightChangeDurationMs = 150;
@@ -270,9 +270,10 @@
       is_active_ ? kEmptySliderColor
                  : SkColorSetA(kEmptySliderColor, kHighlightColorAlpha);
 
-  // Extra space used to hide slider ends behind the thumb when slider is
-  // disabled.
-  const int extra_padding = is_active_ ? 0 : kSliderPadding;
+  // Padding used to adjust space between slider ends and slider thumb.
+  // Value is negative when slider is active so that there is no separation
+  // between slider and thumb.
+  const int extra_padding = is_active_ ? -kSliderPadding : kSliderPadding;
 
   cc::PaintFlags slider_flags;
   slider_flags.setAntiAlias(true);